github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/vars.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  	"bytes"
    15  	"context"
    16  	"fmt"
    17  	"sort"
    18  	"strconv"
    19  	"strings"
    20  	"time"
    21  
    22  	"github.com/cockroachdb/cockroach/pkg/build"
    23  	"github.com/cockroachdb/cockroach/pkg/server/telemetry"
    24  	"github.com/cockroachdb/cockroach/pkg/settings"
    25  	"github.com/cockroachdb/cockroach/pkg/sql/delegate"
    26  	"github.com/cockroachdb/cockroach/pkg/sql/lex"
    27  	"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode"
    28  	"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror"
    29  	"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgnotice"
    30  	"github.com/cockroachdb/cockroach/pkg/sql/sem/builtins"
    31  	"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
    32  	"github.com/cockroachdb/cockroach/pkg/sql/sessiondata"
    33  	"github.com/cockroachdb/cockroach/pkg/sql/sqlbase"
    34  	"github.com/cockroachdb/cockroach/pkg/sql/sqltelemetry"
    35  	"github.com/cockroachdb/cockroach/pkg/util/errorutil/unimplemented"
    36  	"github.com/cockroachdb/cockroach/pkg/util/timeutil"
    37  	"github.com/cockroachdb/cockroach/pkg/util/tracing"
    38  	"github.com/cockroachdb/errors"
    39  )
    40  
    41  const (
    42  	// PgServerVersion is the latest version of postgres that we claim to support.
    43  	PgServerVersion = "9.5.0"
    44  	// PgServerVersionNum is the latest version of postgres that we claim to support in the numeric format of "server_version_num".
    45  	PgServerVersionNum = "90500"
    46  )
    47  
    48  type getStringValFn = func(
    49  	ctx context.Context, evalCtx *extendedEvalContext, values []tree.TypedExpr,
    50  ) (string, error)
    51  
    52  // sessionVar provides a unified interface for performing operations on
    53  // variables such as the selected database, or desired syntax.
    54  type sessionVar struct {
    55  	// Hidden indicates that the variable should not show up in the output of SHOW ALL.
    56  	Hidden bool
    57  
    58  	// Get returns a string representation of a given variable to be used
    59  	// either by SHOW or in the pg_catalog table.
    60  	Get func(evalCtx *extendedEvalContext) string
    61  
    62  	// GetStringVal converts the provided Expr to a string suitable
    63  	// for Set() or RuntimeSet().
    64  	// If this method is not provided,
    65  	//   `getStringVal(evalCtx, varName, values)`
    66  	// will be used instead.
    67  	//
    68  	// The reason why variable sets work in two phases like this is that
    69  	// the Set() method has to operate on strings, because it can be
    70  	// invoked at a point where there is no evalContext yet (e.g.
    71  	// upon session initialization in pgwire).
    72  	GetStringVal getStringValFn
    73  
    74  	// Set performs mutations to effect the change desired by SET commands.
    75  	// This method should be provided for variables that can be overridden
    76  	// in pgwire.
    77  	Set func(ctx context.Context, m *sessionDataMutator, val string) error
    78  
    79  	// RuntimeSet is like Set except it can only be used in sessions
    80  	// that are already running (i.e. not during session
    81  	// initialization).  Currently only used for transaction_isolation.
    82  	RuntimeSet func(_ context.Context, evalCtx *extendedEvalContext, s string) error
    83  
    84  	// GlobalDefault is the string value to use as default for RESET or
    85  	// during session initialization when no default value was provided
    86  	// by the client.
    87  	GlobalDefault func(sv *settings.Values) string
    88  }
    89  
    90  func formatBoolAsPostgresSetting(b bool) string {
    91  	if b {
    92  		return "on"
    93  	}
    94  	return "off"
    95  }
    96  
    97  func parseBoolVar(varName, val string) (bool, error) {
    98  	val = strings.ToLower(val)
    99  	switch val {
   100  	case "on":
   101  		return true, nil
   102  	case "off":
   103  		return false, nil
   104  	case "yes":
   105  		return true, nil
   106  	case "no":
   107  		return false, nil
   108  	}
   109  	b, err := strconv.ParseBool(val)
   110  	if err != nil {
   111  		return false, pgerror.Newf(pgcode.InvalidParameterValue,
   112  			"parameter \"%s\" requires a Boolean value", varName)
   113  	}
   114  	return b, nil
   115  }
   116  
   117  // varGen is the main definition array for all session variables.
   118  // Note to maintainers: try to keep this sorted in the source code.
   119  var varGen = map[string]sessionVar{
   120  	// Set by clients to improve query logging.
   121  	// See https://www.postgresql.org/docs/10/static/runtime-config-logging.html#GUC-APPLICATION-NAME
   122  	`application_name`: {
   123  		Set: func(
   124  			_ context.Context, m *sessionDataMutator, s string,
   125  		) error {
   126  			m.SetApplicationName(s)
   127  			return nil
   128  		},
   129  		Get: func(evalCtx *extendedEvalContext) string {
   130  			return evalCtx.SessionData.ApplicationName
   131  		},
   132  		GlobalDefault: func(_ *settings.Values) string { return "" },
   133  	},
   134  
   135  	// See https://www.postgresql.org/docs/10/static/runtime-config-client.html
   136  	// and https://www.postgresql.org/docs/10/static/datatype-binary.html
   137  	`bytea_output`: {
   138  		Set: func(
   139  			_ context.Context, m *sessionDataMutator, s string,
   140  		) error {
   141  			mode, ok := lex.BytesEncodeFormatFromString(s)
   142  			if !ok {
   143  				return newVarValueError(`bytea_output`, s, "hex", "escape", "base64")
   144  			}
   145  			m.SetBytesEncodeFormat(mode)
   146  			return nil
   147  		},
   148  		Get: func(evalCtx *extendedEvalContext) string {
   149  			return evalCtx.SessionData.DataConversion.BytesEncodeFormat.String()
   150  		},
   151  		GlobalDefault: func(sv *settings.Values) string { return lex.BytesEncodeHex.String() },
   152  	},
   153  
   154  	`client_min_messages`: {
   155  		Set: func(
   156  			_ context.Context, m *sessionDataMutator, s string,
   157  		) error {
   158  			severity, ok := pgnotice.ParseDisplaySeverity(s)
   159  			if !ok {
   160  				return errors.WithHintf(
   161  					pgerror.Newf(
   162  						pgcode.InvalidParameterValue,
   163  						"%s is not supported",
   164  						severity,
   165  					),
   166  					"Valid severities are: %s.",
   167  					strings.Join(pgnotice.ValidDisplaySeverities(), ", "),
   168  				)
   169  			}
   170  			m.SetNoticeDisplaySeverity(severity)
   171  			return nil
   172  		},
   173  		Get: func(evalCtx *extendedEvalContext) string {
   174  			return evalCtx.SessionData.NoticeDisplaySeverity.String()
   175  		},
   176  		GlobalDefault: func(_ *settings.Values) string { return "notice" },
   177  	},
   178  
   179  	// See https://www.postgresql.org/docs/9.6/static/multibyte.html
   180  	// Also aliased to SET NAMES.
   181  	`client_encoding`: {
   182  		Set: func(
   183  			_ context.Context, m *sessionDataMutator, s string,
   184  		) error {
   185  			encoding := builtins.CleanEncodingName(s)
   186  			switch encoding {
   187  			case "utf8", "unicode", "cp65001":
   188  				return nil
   189  			default:
   190  				return unimplemented.NewWithIssueDetailf(35882,
   191  					"client_encoding "+encoding,
   192  					"unimplemented client encoding: %q", encoding)
   193  			}
   194  		},
   195  		Get:           func(evalCtx *extendedEvalContext) string { return "UTF8" },
   196  		GlobalDefault: func(_ *settings.Values) string { return "UTF8" },
   197  	},
   198  
   199  	// Supported for PG compatibility only.
   200  	// See https://www.postgresql.org/docs/9.6/static/multibyte.html
   201  	`server_encoding`: makeReadOnlyVar("UTF8"),
   202  
   203  	// CockroachDB extension.
   204  	`database`: {
   205  		GetStringVal: func(
   206  			ctx context.Context, evalCtx *extendedEvalContext, values []tree.TypedExpr,
   207  		) (string, error) {
   208  			dbName, err := getStringVal(&evalCtx.EvalContext, `database`, values)
   209  			if err != nil {
   210  				return "", err
   211  			}
   212  
   213  			if len(dbName) == 0 && evalCtx.SessionData.SafeUpdates {
   214  				return "", pgerror.DangerousStatementf("SET database to empty string")
   215  			}
   216  
   217  			if len(dbName) != 0 {
   218  				// Verify database descriptor exists.
   219  				if _, err := evalCtx.schemaAccessors.logical.GetDatabaseDesc(
   220  					ctx, evalCtx.Txn, evalCtx.Codec, dbName, tree.DatabaseLookupFlags{Required: true},
   221  				); err != nil {
   222  					return "", err
   223  				}
   224  			}
   225  			return dbName, nil
   226  		},
   227  		Set: func(
   228  			ctx context.Context, m *sessionDataMutator, dbName string,
   229  		) error {
   230  			m.SetDatabase(dbName)
   231  			return nil
   232  		},
   233  		Get: func(evalCtx *extendedEvalContext) string { return evalCtx.SessionData.Database },
   234  		GlobalDefault: func(_ *settings.Values) string {
   235  			// The "defaultdb" value is set as session default in the pgwire
   236  			// connection code. The global default is the empty string,
   237  			// which is what internal connections should pick up.
   238  			return ""
   239  		},
   240  	},
   241  
   242  	// Supported for PG compatibility only.
   243  	// See https://www.postgresql.org/docs/10/static/runtime-config-client.html#GUC-DATESTYLE
   244  	`datestyle`: {
   245  		Set: func(_ context.Context, m *sessionDataMutator, s string) error {
   246  			s = strings.ToLower(s)
   247  			parts := strings.Split(s, ",")
   248  			if strings.TrimSpace(parts[0]) != "iso" ||
   249  				(len(parts) == 2 && strings.TrimSpace(parts[1]) != "mdy") ||
   250  				len(parts) > 2 {
   251  				err := newVarValueError("DateStyle", s, "ISO", "ISO, MDY")
   252  				err = errors.WithDetail(err, compatErrMsg)
   253  				return err
   254  			}
   255  			return nil
   256  		},
   257  		Get:           func(evalCtx *extendedEvalContext) string { return "ISO, MDY" },
   258  		GlobalDefault: func(_ *settings.Values) string { return "ISO, MDY" },
   259  	},
   260  
   261  	// Controls the subsequent parsing of a "naked" INT type.
   262  	// TODO(bob): Remove or no-op this in v2.4: https://github.com/cockroachdb/cockroach/issues/32844
   263  	`default_int_size`: {
   264  		Get: func(evalCtx *extendedEvalContext) string {
   265  			return strconv.FormatInt(int64(evalCtx.SessionData.DefaultIntSize), 10)
   266  		},
   267  		GetStringVal: makeIntGetStringValFn("default_int_size"),
   268  		Set: func(ctx context.Context, m *sessionDataMutator, val string) error {
   269  			i, err := strconv.ParseInt(val, 10, 64)
   270  			if err != nil {
   271  				return wrapSetVarError("default_int_size", val, "%v", err)
   272  			}
   273  			if i != 4 && i != 8 {
   274  				return pgerror.New(pgcode.InvalidParameterValue,
   275  					`only 4 or 8 are supported by default_int_size`)
   276  			}
   277  			// Only record when the value has been changed to a non-default
   278  			// value, since we really just want to know how useful int4-mode
   279  			// is. If we were to record counts for size.4 and size.8
   280  			// variables, we'd have to distinguish cases in which a session
   281  			// was opened in int8 mode and switched to int4 mode, versus ones
   282  			// set to int4 by a connection string.
   283  			// TODO(bob): Change to 8 in v2.3: https://github.com/cockroachdb/cockroach/issues/32534
   284  			if i == 4 {
   285  				telemetry.Inc(sqltelemetry.DefaultIntSize4Counter)
   286  			}
   287  			m.SetDefaultIntSize(int(i))
   288  			return nil
   289  		},
   290  		GlobalDefault: func(sv *settings.Values) string {
   291  			return strconv.FormatInt(defaultIntSize.Get(sv), 10)
   292  		},
   293  	},
   294  
   295  	// See https://www.postgresql.org/docs/10/runtime-config-client.html.
   296  	// Supported only for pg compatibility - CockroachDB has no notion of
   297  	// tablespaces.
   298  	`default_tablespace`: {
   299  		Set: func(_ context.Context, m *sessionDataMutator, s string) error {
   300  			if s != "" {
   301  				return newVarValueError(`default_tablespace`, s, "")
   302  			}
   303  			return nil
   304  		},
   305  		Get: func(evalCtx *extendedEvalContext) string {
   306  			return ""
   307  		},
   308  		GlobalDefault: func(sv *settings.Values) string { return "" },
   309  	},
   310  
   311  	// See https://www.postgresql.org/docs/10/static/runtime-config-client.html#GUC-DEFAULT-TRANSACTION-ISOLATION
   312  	`default_transaction_isolation`: {
   313  		Set: func(_ context.Context, m *sessionDataMutator, s string) error {
   314  			switch strings.ToUpper(s) {
   315  			case `READ UNCOMMITTED`, `READ COMMITTED`, `SNAPSHOT`, `REPEATABLE READ`, `SERIALIZABLE`, `DEFAULT`:
   316  				// Do nothing. All transactions execute with serializable isolation.
   317  			default:
   318  				return newVarValueError(`default_transaction_isolation`, s, "serializable")
   319  			}
   320  
   321  			return nil
   322  		},
   323  		Get: func(evalCtx *extendedEvalContext) string {
   324  			return "serializable"
   325  		},
   326  		GlobalDefault: func(sv *settings.Values) string { return "default" },
   327  	},
   328  
   329  	// CockroachDB extension.
   330  	`default_transaction_priority`: {
   331  		Set: func(_ context.Context, m *sessionDataMutator, s string) error {
   332  			pri, ok := tree.UserPriorityFromString(s)
   333  			if !ok {
   334  				return newVarValueError(`default_transaction_isolation`, s, "low", "normal", "high")
   335  			}
   336  			m.SetDefaultTransactionPriority(pri)
   337  			return nil
   338  		},
   339  		Get: func(evalCtx *extendedEvalContext) string {
   340  			pri := tree.UserPriority(evalCtx.SessionData.DefaultTxnPriority)
   341  			if pri == tree.UnspecifiedUserPriority {
   342  				pri = tree.Normal
   343  			}
   344  			return strings.ToLower(pri.String())
   345  		},
   346  		GlobalDefault: func(sv *settings.Values) string {
   347  			return strings.ToLower(tree.Normal.String())
   348  		},
   349  	},
   350  
   351  	// See https://www.postgresql.org/docs/9.3/static/runtime-config-client.html#GUC-DEFAULT-TRANSACTION-READ-ONLY
   352  	`default_transaction_read_only`: {
   353  		GetStringVal: makePostgresBoolGetStringValFn("default_transaction_read_only"),
   354  		Set: func(_ context.Context, m *sessionDataMutator, s string) error {
   355  			b, err := parseBoolVar("default_transaction_read_only", s)
   356  			if err != nil {
   357  				return err
   358  			}
   359  			m.SetDefaultReadOnly(b)
   360  			return nil
   361  		},
   362  		Get: func(evalCtx *extendedEvalContext) string {
   363  			return formatBoolAsPostgresSetting(evalCtx.SessionData.DefaultReadOnly)
   364  		},
   365  		GlobalDefault: globalFalse,
   366  	},
   367  
   368  	// CockroachDB extension.
   369  	`distsql`: {
   370  		Set: func(_ context.Context, m *sessionDataMutator, s string) error {
   371  			mode, ok := sessiondata.DistSQLExecModeFromString(s)
   372  			if !ok {
   373  				return newVarValueError(`distsql`, s, "on", "off", "auto", "always", "2.0-auto", "2.0-off")
   374  			}
   375  			m.SetDistSQLMode(mode)
   376  			return nil
   377  		},
   378  		Get: func(evalCtx *extendedEvalContext) string {
   379  			return evalCtx.SessionData.DistSQLMode.String()
   380  		},
   381  		GlobalDefault: func(sv *settings.Values) string {
   382  			return sessiondata.DistSQLExecMode(DistSQLClusterExecMode.Get(sv)).String()
   383  		},
   384  	},
   385  
   386  	// CockroachDB extension.
   387  	`experimental_distsql_planning`: {
   388  		GetStringVal: makePostgresBoolGetStringValFn(`experimental_distsql_planning`),
   389  		Set: func(_ context.Context, m *sessionDataMutator, s string) error {
   390  			mode, ok := sessiondata.ExperimentalDistSQLPlanningModeFromString(s)
   391  			if !ok {
   392  				return newVarValueError(`experimental_distsql_planning`, s,
   393  					"off", "on", "always")
   394  			}
   395  			m.SetExperimentalDistSQLPlanning(mode)
   396  			return nil
   397  		},
   398  		Get: func(evalCtx *extendedEvalContext) string {
   399  			return evalCtx.SessionData.ExperimentalDistSQLPlanningMode.String()
   400  		},
   401  		GlobalDefault: func(sv *settings.Values) string {
   402  			return sessiondata.ExperimentalDistSQLPlanningMode(experimentalDistSQLPlanningClusterMode.Get(sv)).String()
   403  		},
   404  	},
   405  
   406  	// CockroachDB extension.
   407  	`experimental_enable_enums`: {
   408  		GetStringVal: makePostgresBoolGetStringValFn(`experimental_enable_enums`),
   409  		Set: func(_ context.Context, m *sessionDataMutator, s string) error {
   410  			b, err := parseBoolVar(`experimental_enable_enums`, s)
   411  			if err != nil {
   412  				return err
   413  			}
   414  			m.SetEnumsEnabled(b)
   415  			return nil
   416  		},
   417  		Get: func(evalCtx *extendedEvalContext) string {
   418  			return formatBoolAsPostgresSetting(evalCtx.SessionData.EnumsEnabled)
   419  		},
   420  		GlobalDefault: func(sv *settings.Values) string {
   421  			return formatBoolAsPostgresSetting(enumsEnabledClusterMode.Get(sv))
   422  		},
   423  	},
   424  
   425  	// CockroachDB extension.
   426  	`enable_zigzag_join`: {
   427  		GetStringVal: makePostgresBoolGetStringValFn(`enable_zigzag_join`),
   428  		Set: func(_ context.Context, m *sessionDataMutator, s string) error {
   429  			b, err := parseBoolVar("enable_zigzag_join", s)
   430  			if err != nil {
   431  				return err
   432  			}
   433  			m.SetZigzagJoinEnabled(b)
   434  			return nil
   435  		},
   436  		Get: func(evalCtx *extendedEvalContext) string {
   437  			return formatBoolAsPostgresSetting(evalCtx.SessionData.ZigzagJoinEnabled)
   438  		},
   439  		GlobalDefault: func(sv *settings.Values) string {
   440  			return formatBoolAsPostgresSetting(zigzagJoinClusterMode.Get(sv))
   441  		},
   442  	},
   443  
   444  	// CockroachDB extension.
   445  	`reorder_joins_limit`: {
   446  		GetStringVal: makeIntGetStringValFn(`reorder_joins_limit`),
   447  		Set: func(_ context.Context, m *sessionDataMutator, s string) error {
   448  			b, err := strconv.ParseInt(s, 10, 64)
   449  			if err != nil {
   450  				return err
   451  			}
   452  			if b < 0 {
   453  				return pgerror.Newf(pgcode.InvalidParameterValue,
   454  					"cannot set reorder_joins_limit to a negative value: %d", b)
   455  			}
   456  			m.SetReorderJoinsLimit(int(b))
   457  			return nil
   458  		},
   459  		Get: func(evalCtx *extendedEvalContext) string {
   460  			return strconv.FormatInt(int64(evalCtx.SessionData.ReorderJoinsLimit), 10)
   461  		},
   462  		GlobalDefault: func(sv *settings.Values) string {
   463  			return strconv.FormatInt(ReorderJoinsLimitClusterValue.Get(sv), 10)
   464  		},
   465  	},
   466  
   467  	// CockroachDB extension.
   468  	`require_explicit_primary_keys`: {
   469  		GetStringVal: makePostgresBoolGetStringValFn(`require_explicit_primary_keys`),
   470  		Set: func(_ context.Context, m *sessionDataMutator, s string) error {
   471  			b, err := parseBoolVar("require_explicit_primary_key", s)
   472  			if err != nil {
   473  				return err
   474  			}
   475  			m.SetRequireExplicitPrimaryKeys(b)
   476  			return nil
   477  		},
   478  		Get: func(evalCtx *extendedEvalContext) string {
   479  			return formatBoolAsPostgresSetting(evalCtx.SessionData.RequireExplicitPrimaryKeys)
   480  		},
   481  		GlobalDefault: func(sv *settings.Values) string {
   482  			return formatBoolAsPostgresSetting(requireExplicitPrimaryKeysClusterMode.Get(sv))
   483  		},
   484  	},
   485  
   486  	// CockroachDB extension.
   487  	`vectorize`: {
   488  		Set: func(_ context.Context, m *sessionDataMutator, s string) error {
   489  			mode, ok := sessiondata.VectorizeExecModeFromString(s)
   490  			if !ok {
   491  				return newVarValueError(`vectorize`, s,
   492  					"off", "201auto", "on", "experimental_always")
   493  			}
   494  			m.SetVectorize(mode)
   495  			return nil
   496  		},
   497  		Get: func(evalCtx *extendedEvalContext) string {
   498  			return evalCtx.SessionData.VectorizeMode.String()
   499  		},
   500  		GlobalDefault: func(sv *settings.Values) string {
   501  			return sessiondata.VectorizeExecMode(
   502  				VectorizeClusterMode.Get(sv)).String()
   503  		},
   504  	},
   505  
   506  	// CockroachDB extension.
   507  	`vectorize_row_count_threshold`: {
   508  		GetStringVal: makeIntGetStringValFn(`vectorize_row_count_threshold`),
   509  		Set: func(_ context.Context, m *sessionDataMutator, s string) error {
   510  			b, err := strconv.ParseInt(s, 10, 64)
   511  			if err != nil {
   512  				return err
   513  			}
   514  			if b < 0 {
   515  				return pgerror.Newf(pgcode.InvalidParameterValue,
   516  					"cannot set vectorize_row_count_threshold to a negative value: %d", b)
   517  			}
   518  			m.SetVectorizeRowCountThreshold(uint64(b))
   519  			return nil
   520  		},
   521  		Get: func(evalCtx *extendedEvalContext) string {
   522  			return strconv.FormatInt(int64(evalCtx.SessionData.VectorizeRowCountThreshold), 10)
   523  		},
   524  		GlobalDefault: func(sv *settings.Values) string {
   525  			return strconv.FormatInt(VectorizeRowCountThresholdClusterValue.Get(sv), 10)
   526  		},
   527  	},
   528  
   529  	// CockroachDB extension.
   530  	// This is deprecated; the only allowable setting is "on".
   531  	`optimizer`: {
   532  		Set: func(_ context.Context, m *sessionDataMutator, s string) error {
   533  			if strings.ToUpper(s) != "ON" {
   534  				return newVarValueError(`optimizer`, s, "on")
   535  			}
   536  			return nil
   537  		},
   538  		Get: func(evalCtx *extendedEvalContext) string {
   539  			return "on"
   540  		},
   541  		GlobalDefault: func(sv *settings.Values) string {
   542  			return "on"
   543  		},
   544  	},
   545  
   546  	// CockroachDB extension.
   547  	`optimizer_foreign_keys`: {
   548  		GetStringVal: makePostgresBoolGetStringValFn(`optimizer_foreign_keys`),
   549  		Set: func(_ context.Context, m *sessionDataMutator, s string) error {
   550  			b, err := parseBoolVar("optimizer_foreign_keys", s)
   551  			if err != nil {
   552  				return err
   553  			}
   554  			m.SetOptimizerFKChecks(b)
   555  			return nil
   556  		},
   557  		Get: func(evalCtx *extendedEvalContext) string {
   558  			return formatBoolAsPostgresSetting(evalCtx.SessionData.OptimizerFKChecks)
   559  		},
   560  		GlobalDefault: func(sv *settings.Values) string {
   561  			return formatBoolAsPostgresSetting(optDrivenFKChecksClusterMode.Get(sv))
   562  		},
   563  	},
   564  
   565  	// CockroachDB extension.
   566  	`experimental_optimizer_foreign_key_cascades`: {
   567  		GetStringVal: makePostgresBoolGetStringValFn(`experimental_optimizer_foreign_key_cascades`),
   568  		Set: func(_ context.Context, m *sessionDataMutator, s string) error {
   569  			b, err := parseBoolVar("experimental_optimizer_foreign_key_cascades", s)
   570  			if err != nil {
   571  				return err
   572  			}
   573  			m.SetOptimizerFKCascades(b)
   574  			return nil
   575  		},
   576  		Get: func(evalCtx *extendedEvalContext) string {
   577  			return formatBoolAsPostgresSetting(evalCtx.SessionData.OptimizerFKCascades)
   578  		},
   579  		GlobalDefault: func(sv *settings.Values) string {
   580  			return formatBoolAsPostgresSetting(optDrivenFKCascadesClusterMode.Get(sv))
   581  		},
   582  	},
   583  
   584  	// CockroachDB extension.
   585  	`foreign_key_cascades_limit`: {
   586  		GetStringVal: makeIntGetStringValFn(`foreign_key_cascades_limit`),
   587  		Set: func(_ context.Context, m *sessionDataMutator, s string) error {
   588  			b, err := strconv.ParseInt(s, 10, 64)
   589  			if err != nil {
   590  				return err
   591  			}
   592  			if b < 0 {
   593  				return pgerror.Newf(pgcode.InvalidParameterValue,
   594  					"cannot set foreign_key_cascades_limit to a negative value: %d", b)
   595  			}
   596  			m.SetOptimizerFKCascadesLimit(int(b))
   597  			return nil
   598  		},
   599  		Get: func(evalCtx *extendedEvalContext) string {
   600  			return strconv.FormatInt(int64(evalCtx.SessionData.OptimizerFKCascadesLimit), 10)
   601  		},
   602  		GlobalDefault: func(sv *settings.Values) string {
   603  			return strconv.FormatInt(optDrivenFKCascadesClusterLimit.Get(sv), 10)
   604  		},
   605  	},
   606  
   607  	// CockroachDB extension.
   608  	`optimizer_use_histograms`: {
   609  		GetStringVal: makePostgresBoolGetStringValFn(`optimizer_use_histograms`),
   610  		Set: func(_ context.Context, m *sessionDataMutator, s string) error {
   611  			b, err := parseBoolVar("optimizer_use_histograms", s)
   612  			if err != nil {
   613  				return err
   614  			}
   615  			m.SetOptimizerUseHistograms(b)
   616  			return nil
   617  		},
   618  		Get: func(evalCtx *extendedEvalContext) string {
   619  			return formatBoolAsPostgresSetting(evalCtx.SessionData.OptimizerUseHistograms)
   620  		},
   621  		GlobalDefault: func(sv *settings.Values) string {
   622  			return formatBoolAsPostgresSetting(optUseHistogramsClusterMode.Get(sv))
   623  		},
   624  	},
   625  
   626  	// CockroachDB extension.
   627  	`optimizer_use_multicol_stats`: {
   628  		GetStringVal: makePostgresBoolGetStringValFn(`optimizer_use_multicol_stats`),
   629  		Set: func(_ context.Context, m *sessionDataMutator, s string) error {
   630  			b, err := parseBoolVar("optimizer_use_multicol_stats", s)
   631  			if err != nil {
   632  				return err
   633  			}
   634  			m.SetOptimizerUseMultiColStats(b)
   635  			return nil
   636  		},
   637  		Get: func(evalCtx *extendedEvalContext) string {
   638  			return formatBoolAsPostgresSetting(evalCtx.SessionData.OptimizerUseMultiColStats)
   639  		},
   640  		GlobalDefault: func(sv *settings.Values) string {
   641  			return formatBoolAsPostgresSetting(optUseMultiColStatsClusterMode.Get(sv))
   642  		},
   643  	},
   644  
   645  	// CockroachDB extension.
   646  	// TODO(mgartner): remove this once partial indexes are fully supported.
   647  	`experimental_partial_indexes`: {
   648  		GetStringVal: makePostgresBoolGetStringValFn(`experimental_partial_indexes`),
   649  		Set: func(_ context.Context, m *sessionDataMutator, s string) error {
   650  			b, err := parseBoolVar("experimental_partial_indexes", s)
   651  			if err != nil {
   652  				return err
   653  			}
   654  			m.SetPartialIndexes(b)
   655  			return nil
   656  		},
   657  		Get: func(evalCtx *extendedEvalContext) string {
   658  			return formatBoolAsPostgresSetting(evalCtx.SessionData.PartialIndexes)
   659  		},
   660  		GlobalDefault: func(sv *settings.Values) string {
   661  			return formatBoolAsPostgresSetting(partialIndexClusterMode.Get(sv))
   662  		},
   663  	},
   664  
   665  	// CockroachDB extension.
   666  	`enable_implicit_select_for_update`: {
   667  		GetStringVal: makePostgresBoolGetStringValFn(`enable_implicit_select_for_update`),
   668  		Set: func(_ context.Context, m *sessionDataMutator, s string) error {
   669  			b, err := parseBoolVar("enabled_implicit_select_for_update", s)
   670  			if err != nil {
   671  				return err
   672  			}
   673  			m.SetImplicitSelectForUpdate(b)
   674  			return nil
   675  		},
   676  		Get: func(evalCtx *extendedEvalContext) string {
   677  			return formatBoolAsPostgresSetting(evalCtx.SessionData.ImplicitSelectForUpdate)
   678  		},
   679  		GlobalDefault: func(sv *settings.Values) string {
   680  			return formatBoolAsPostgresSetting(implicitSelectForUpdateClusterMode.Get(sv))
   681  		},
   682  	},
   683  
   684  	// CockroachDB extension.
   685  	`enable_insert_fast_path`: {
   686  		GetStringVal: makePostgresBoolGetStringValFn(`enable_insert_fast_path`),
   687  		Set: func(_ context.Context, m *sessionDataMutator, s string) error {
   688  			b, err := parseBoolVar("enable_insert_fast_path", s)
   689  			if err != nil {
   690  				return err
   691  			}
   692  			m.SetInsertFastPath(b)
   693  			return nil
   694  		},
   695  		Get: func(evalCtx *extendedEvalContext) string {
   696  			return formatBoolAsPostgresSetting(evalCtx.SessionData.InsertFastPath)
   697  		},
   698  		GlobalDefault: func(sv *settings.Values) string {
   699  			return formatBoolAsPostgresSetting(insertFastPathClusterMode.Get(sv))
   700  		},
   701  	},
   702  
   703  	// CockroachDB extension.
   704  	`serial_normalization`: {
   705  		Set: func(_ context.Context, m *sessionDataMutator, s string) error {
   706  			mode, ok := sessiondata.SerialNormalizationModeFromString(s)
   707  			if !ok {
   708  				return newVarValueError(`serial_normalization`, s,
   709  					"rowid", "virtual_sequence", "sql_sequence")
   710  			}
   711  			m.SetSerialNormalizationMode(mode)
   712  			return nil
   713  		},
   714  		Get: func(evalCtx *extendedEvalContext) string {
   715  			return evalCtx.SessionData.SerialNormalizationMode.String()
   716  		},
   717  		GlobalDefault: func(sv *settings.Values) string {
   718  			return sessiondata.SerialNormalizationMode(
   719  				SerialNormalizationMode.Get(sv)).String()
   720  		},
   721  	},
   722  
   723  	// See https://www.postgresql.org/docs/10/static/runtime-config-client.html
   724  	`extra_float_digits`: {
   725  		GetStringVal: makeIntGetStringValFn(`extra_float_digits`),
   726  		Set: func(
   727  			_ context.Context, m *sessionDataMutator, s string,
   728  		) error {
   729  			i, err := strconv.ParseInt(s, 10, 64)
   730  			if err != nil {
   731  				return wrapSetVarError("extra_float_digits", s, "%v", err)
   732  			}
   733  			// Note: this is the range allowed by PostgreSQL.
   734  			// See also the documentation around (DataConversionConfig).GetFloatPrec()
   735  			// in session_data.go.
   736  			if i < -15 || i > 3 {
   737  				return pgerror.Newf(pgcode.InvalidParameterValue,
   738  					`%d is outside the valid range for parameter "extra_float_digits" (-15 .. 3)`, i)
   739  			}
   740  			m.SetExtraFloatDigits(int(i))
   741  			return nil
   742  		},
   743  		Get: func(evalCtx *extendedEvalContext) string {
   744  			return fmt.Sprintf("%d", evalCtx.SessionData.DataConversion.ExtraFloatDigits)
   745  		},
   746  		GlobalDefault: func(sv *settings.Values) string { return "0" },
   747  	},
   748  
   749  	// CockroachDB extension. See docs on SessionData.ForceSavepointRestart.
   750  	// https://github.com/cockroachdb/cockroach/issues/30588
   751  	`force_savepoint_restart`: {
   752  		Get: func(evalCtx *extendedEvalContext) string {
   753  			return formatBoolAsPostgresSetting(evalCtx.SessionData.ForceSavepointRestart)
   754  		},
   755  		GetStringVal: makePostgresBoolGetStringValFn("force_savepoint_restart"),
   756  		Set: func(_ context.Context, m *sessionDataMutator, val string) error {
   757  			b, err := parseBoolVar("force_savepoint_restart", val)
   758  			if err != nil {
   759  				return err
   760  			}
   761  			if b {
   762  				telemetry.Inc(sqltelemetry.ForceSavepointRestartCounter)
   763  			}
   764  			m.SetForceSavepointRestart(b)
   765  			return nil
   766  		},
   767  		GlobalDefault: globalFalse,
   768  	},
   769  
   770  	// See https://www.postgresql.org/docs/10/static/runtime-config-preset.html
   771  	`integer_datetimes`: makeReadOnlyVar("on"),
   772  
   773  	// See https://www.postgresql.org/docs/10/static/runtime-config-client.html#GUC-INTERVALSTYLE
   774  	`intervalstyle`: makeCompatStringVar(`IntervalStyle`, "postgres"),
   775  
   776  	// CockroachDB extension.
   777  	`locality`: {
   778  		Get: func(evalCtx *extendedEvalContext) string {
   779  			return evalCtx.Locality.String()
   780  		},
   781  	},
   782  
   783  	// See https://www.postgresql.org/docs/10/static/runtime-config-client.html#GUC-LOC-TIMEOUT
   784  	`lock_timeout`: makeCompatIntVar(`lock_timeout`, 0),
   785  
   786  	// See https://www.postgresql.org/docs/10/static/runtime-config-client.html#GUC-IDLE-IN-TRANSACTION-SESSION-TIMEOUT
   787  	// See also issue #5924.
   788  	`idle_in_transaction_session_timeout`: makeCompatIntVar(`idle_in_transaction_session_timeout`, 0),
   789  
   790  	// Supported for PG compatibility only.
   791  	// See https://www.postgresql.org/docs/10/static/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS
   792  	`max_identifier_length`: {
   793  		Get: func(evalCtx *extendedEvalContext) string { return "128" },
   794  	},
   795  
   796  	// See https://www.postgresql.org/docs/10/static/runtime-config-preset.html#GUC-MAX-INDEX-KEYS
   797  	`max_index_keys`: makeReadOnlyVar("32"),
   798  
   799  	// CockroachDB extension.
   800  	`node_id`: {
   801  		Get: func(evalCtx *extendedEvalContext) string {
   802  			nodeID, _ := evalCtx.NodeID.OptionalNodeID() // zero if unavailable
   803  			return fmt.Sprintf("%d", nodeID)
   804  		},
   805  	},
   806  
   807  	// CockroachDB extension.
   808  	// TODO(dan): This should also work with SET.
   809  	`results_buffer_size`: {
   810  		Get: func(evalCtx *extendedEvalContext) string {
   811  			return strconv.FormatInt(evalCtx.SessionData.ResultsBufferSize, 10)
   812  		},
   813  	},
   814  
   815  	// CockroachDB extension (inspired by MySQL).
   816  	// See https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_sql_safe_updates
   817  	`sql_safe_updates`: {
   818  		Get: func(evalCtx *extendedEvalContext) string {
   819  			return formatBoolAsPostgresSetting(evalCtx.SessionData.SafeUpdates)
   820  		},
   821  		GetStringVal: makePostgresBoolGetStringValFn("sql_safe_updates"),
   822  		Set: func(_ context.Context, m *sessionDataMutator, s string) error {
   823  			b, err := parseBoolVar("sql_safe_updates", s)
   824  			if err != nil {
   825  				return err
   826  			}
   827  			m.SetSafeUpdates(b)
   828  			return nil
   829  		},
   830  		GlobalDefault: globalFalse,
   831  	},
   832  
   833  	// See https://www.postgresql.org/docs/10/static/ddl-schemas.html#DDL-SCHEMAS-PATH
   834  	// https://www.postgresql.org/docs/9.6/static/runtime-config-client.html
   835  	`search_path`: {
   836  		GetStringVal: func(
   837  			_ context.Context, evalCtx *extendedEvalContext, values []tree.TypedExpr,
   838  		) (string, error) {
   839  			comma := ""
   840  			var buf bytes.Buffer
   841  			for _, v := range values {
   842  				s, err := datumAsString(&evalCtx.EvalContext, "search_path", v)
   843  				if err != nil {
   844  					return "", err
   845  				}
   846  				if strings.Contains(s, ",") {
   847  					// TODO(knz): if/when we want to support this, we'll need to change
   848  					// the interface between GetStringVal() and Set() to take string
   849  					// arrays instead of a single string.
   850  					return "", unimplemented.Newf("schema names containing commas in search_path",
   851  						"schema name %q not supported in search_path", s)
   852  				}
   853  				buf.WriteString(comma)
   854  				buf.WriteString(s)
   855  				comma = ","
   856  			}
   857  			return buf.String(), nil
   858  		},
   859  		Set: func(_ context.Context, m *sessionDataMutator, s string) error {
   860  			paths := strings.Split(s, ",")
   861  			m.UpdateSearchPath(paths)
   862  			return nil
   863  		},
   864  		Get: func(evalCtx *extendedEvalContext) string {
   865  			return evalCtx.SessionData.SearchPath.String()
   866  		},
   867  		GlobalDefault: func(sv *settings.Values) string {
   868  			return sqlbase.DefaultSearchPath.String()
   869  		},
   870  	},
   871  
   872  	// See https://www.postgresql.org/docs/10/static/runtime-config-preset.html#GUC-SERVER-VERSION
   873  	`server_version`: makeReadOnlyVar(PgServerVersion),
   874  
   875  	// See https://www.postgresql.org/docs/10/static/runtime-config-preset.html#GUC-SERVER-VERSION-NUM
   876  	`server_version_num`: makeReadOnlyVar(PgServerVersionNum),
   877  
   878  	// See https://www.postgresql.org/docs/9.4/runtime-config-connection.html
   879  	`ssl_renegotiation_limit`: {
   880  		Hidden:        true,
   881  		GetStringVal:  makeIntGetStringValFn(`ssl_renegotiation_limit`),
   882  		Get:           func(_ *extendedEvalContext) string { return "0" },
   883  		GlobalDefault: func(_ *settings.Values) string { return "0" },
   884  		Set: func(_ context.Context, _ *sessionDataMutator, s string) error {
   885  			i, err := strconv.ParseInt(s, 10, 64)
   886  			if err != nil {
   887  				return wrapSetVarError("ssl_renegotiation_limit", s, "%v", err)
   888  			}
   889  			if i != 0 {
   890  				// See pg src/backend/utils/misc/guc.c: non-zero values are not to be supported.
   891  				return newVarValueError("ssl_renegotiation_limit", s, "0")
   892  			}
   893  			return nil
   894  		},
   895  	},
   896  
   897  	// CockroachDB extension.
   898  	`crdb_version`: makeReadOnlyVar(build.GetInfo().Short()),
   899  
   900  	// CockroachDB extension
   901  	`session_id`: {
   902  		Get: func(evalCtx *extendedEvalContext) string { return evalCtx.SessionID.String() },
   903  	},
   904  
   905  	// CockroachDB extension.
   906  	// In PG this is a pseudo-function used with SELECT, not SHOW.
   907  	// See https://www.postgresql.org/docs/10/static/functions-info.html
   908  	`session_user`: {
   909  		Get: func(evalCtx *extendedEvalContext) string { return evalCtx.SessionData.User },
   910  	},
   911  
   912  	// See pg sources src/backend/utils/misc/guc.c. The variable is defined
   913  	// but is hidden from SHOW ALL.
   914  	`session_authorization`: {
   915  		Hidden: true,
   916  		Get:    func(evalCtx *extendedEvalContext) string { return evalCtx.SessionData.User },
   917  	},
   918  
   919  	// Supported for PG compatibility only.
   920  	// See https://www.postgresql.org/docs/10/static/runtime-config-compatible.html#GUC-STANDARD-CONFORMING-STRINGS
   921  	`standard_conforming_strings`: makeCompatBoolVar(`standard_conforming_strings`, true, false /* anyAllowed */),
   922  
   923  	// See https://www.postgresql.org/docs/10/static/runtime-config-compatible.html#GUC-SYNCHRONIZE-SEQSCANS
   924  	// The default in pg is "on" but the behavior in CockroachDB is "off". As this does not affect
   925  	// results received by clients, we accept both values.
   926  	`synchronize_seqscans`: makeCompatBoolVar(`synchronize_seqscans`, true, true /* anyAllowed */),
   927  
   928  	// See https://www.postgresql.org/docs/10/static/runtime-config-client.html#GUC-ROW-SECURITY
   929  	// The default in pg is "on" but row security is not supported in CockroachDB.
   930  	// We blindly accept both values because as long as there are now row security policies defined,
   931  	// either value produces the same query results in PostgreSQL. That is, as long as CockroachDB
   932  	// does not support row security, accepting either "on" and "off" but ignoring the result
   933  	// is postgres-compatible.
   934  	// If/when CockroachDB is extended to support row security, the default and allowed values
   935  	// should be modified accordingly.
   936  	`row_security`: makeCompatBoolVar(`row_security`, false, true /* anyAllowed */),
   937  
   938  	`statement_timeout`: {
   939  		GetStringVal: stmtTimeoutVarGetStringVal,
   940  		Set:          stmtTimeoutVarSet,
   941  		Get: func(evalCtx *extendedEvalContext) string {
   942  			ms := evalCtx.SessionData.StmtTimeout.Nanoseconds() / int64(time.Millisecond)
   943  			return strconv.FormatInt(ms, 10)
   944  		},
   945  		GlobalDefault: func(sv *settings.Values) string { return "0" },
   946  	},
   947  
   948  	// See https://www.postgresql.org/docs/10/static/runtime-config-client.html#GUC-TIMEZONE
   949  	`timezone`: {
   950  		Get: func(evalCtx *extendedEvalContext) string {
   951  			return sessionDataTimeZoneFormat(evalCtx.SessionData.DataConversion.Location)
   952  		},
   953  		GetStringVal:  timeZoneVarGetStringVal,
   954  		Set:           timeZoneVarSet,
   955  		GlobalDefault: func(_ *settings.Values) string { return "UTC" },
   956  	},
   957  
   958  	// This is not directly documented in PG's docs but does indeed behave this way.
   959  	// See https://github.com/postgres/postgres/blob/REL_10_STABLE/src/backend/utils/misc/guc.c#L3401-L3409
   960  	`transaction_isolation`: {
   961  		Get: func(evalCtx *extendedEvalContext) string {
   962  			return "serializable"
   963  		},
   964  		RuntimeSet: func(_ context.Context, evalCtx *extendedEvalContext, s string) error {
   965  			_, ok := tree.IsolationLevelMap[s]
   966  			if !ok {
   967  				return newVarValueError(`transaction_isolation`, s, "serializable")
   968  			}
   969  			return nil
   970  		},
   971  		GlobalDefault: func(_ *settings.Values) string { return "serializable" },
   972  	},
   973  
   974  	// CockroachDB extension.
   975  	`transaction_priority`: {
   976  		Get: func(evalCtx *extendedEvalContext) string {
   977  			return evalCtx.Txn.UserPriority().String()
   978  		},
   979  	},
   980  
   981  	// CockroachDB extension.
   982  	`transaction_status`: {
   983  		Get: func(evalCtx *extendedEvalContext) string {
   984  			return evalCtx.TxnState
   985  		},
   986  	},
   987  
   988  	// See https://www.postgresql.org/docs/10/static/hot-standby.html#HOT-STANDBY-USERS
   989  	`transaction_read_only`: {
   990  		GetStringVal: makePostgresBoolGetStringValFn("transaction_read_only"),
   991  		Set: func(_ context.Context, m *sessionDataMutator, s string) error {
   992  			b, err := parseBoolVar("transaction_read_only", s)
   993  			if err != nil {
   994  				return err
   995  			}
   996  			m.SetReadOnly(b)
   997  			return nil
   998  		},
   999  		Get: func(evalCtx *extendedEvalContext) string {
  1000  			return formatBoolAsPostgresSetting(evalCtx.TxnReadOnly)
  1001  		},
  1002  		GlobalDefault: globalFalse,
  1003  	},
  1004  
  1005  	// CockroachDB extension.
  1006  	`tracing`: {
  1007  		Get: func(evalCtx *extendedEvalContext) string {
  1008  			sessTracing := evalCtx.Tracing
  1009  			if sessTracing.Enabled() {
  1010  				val := "on"
  1011  				if sessTracing.RecordingType() == tracing.SingleNodeRecording {
  1012  					val += ", local"
  1013  				}
  1014  				if sessTracing.KVTracingEnabled() {
  1015  					val += ", kv"
  1016  				}
  1017  				return val
  1018  			}
  1019  			return "off"
  1020  		},
  1021  		// Setting is done by the SetTracing statement.
  1022  	},
  1023  
  1024  	// CockroachDB extension.
  1025  	`allow_prepare_as_opt_plan`: {
  1026  		Hidden: true,
  1027  		Get: func(evalCtx *extendedEvalContext) string {
  1028  			return formatBoolAsPostgresSetting(evalCtx.SessionData.AllowPrepareAsOptPlan)
  1029  		},
  1030  		Set: func(_ context.Context, m *sessionDataMutator, s string) error {
  1031  			b, err := parseBoolVar("allow_prepare_as_opt_plan", s)
  1032  			if err != nil {
  1033  				return err
  1034  			}
  1035  			m.SetAllowPrepareAsOptPlan(b)
  1036  			return nil
  1037  		},
  1038  		GlobalDefault: globalFalse,
  1039  	},
  1040  
  1041  	// CockroachDB extension.
  1042  	`save_tables_prefix`: {
  1043  		Hidden: true,
  1044  		Get: func(evalCtx *extendedEvalContext) string {
  1045  			return evalCtx.SessionData.SaveTablesPrefix
  1046  		},
  1047  		Set: func(_ context.Context, m *sessionDataMutator, s string) error {
  1048  			m.SetSaveTablesPrefix(s)
  1049  			return nil
  1050  		},
  1051  		GlobalDefault: func(_ *settings.Values) string { return "" },
  1052  	},
  1053  
  1054  	// CockroachDB extension.
  1055  	`experimental_enable_temp_tables`: {
  1056  		GetStringVal: makePostgresBoolGetStringValFn(`experimental_enable_temp_tables`),
  1057  		Set: func(_ context.Context, m *sessionDataMutator, s string) error {
  1058  			b, err := parseBoolVar("experimental_enable_temp_tables", s)
  1059  			if err != nil {
  1060  				return err
  1061  			}
  1062  			m.SetTempTablesEnabled(b)
  1063  			return nil
  1064  		},
  1065  		Get: func(evalCtx *extendedEvalContext) string {
  1066  			return formatBoolAsPostgresSetting(evalCtx.SessionData.TempTablesEnabled)
  1067  		},
  1068  		GlobalDefault: func(sv *settings.Values) string {
  1069  			return formatBoolAsPostgresSetting(temporaryTablesEnabledClusterMode.Get(sv))
  1070  		},
  1071  	},
  1072  
  1073  	// CockroachDB extension.
  1074  	`experimental_enable_hash_sharded_indexes`: {
  1075  		GetStringVal: makePostgresBoolGetStringValFn(`experimental_enable_hash_sharded_indexes`),
  1076  		Set: func(_ context.Context, m *sessionDataMutator, s string) error {
  1077  			b, err := parseBoolVar("experimental_enable_hash_sharded_indexes", s)
  1078  			if err != nil {
  1079  				return err
  1080  			}
  1081  			m.SetHashShardedIndexesEnabled(b)
  1082  			return nil
  1083  		},
  1084  		Get: func(evalCtx *extendedEvalContext) string {
  1085  			return formatBoolAsPostgresSetting(evalCtx.SessionData.HashShardedIndexesEnabled)
  1086  		},
  1087  		GlobalDefault: func(sv *settings.Values) string {
  1088  			return formatBoolAsPostgresSetting(hashShardedIndexesEnabledClusterMode.Get(sv))
  1089  		},
  1090  	},
  1091  
  1092  	// CockroachDB extension.
  1093  	`enable_experimental_alter_column_type_general`: {
  1094  		GetStringVal: makePostgresBoolGetStringValFn(`enable_experimental_alter_column_type_general`),
  1095  		Set: func(_ context.Context, m *sessionDataMutator, s string) error {
  1096  			b, err := parseBoolVar("enable_experimental_alter_column_type_general", s)
  1097  			if err != nil {
  1098  				return err
  1099  			}
  1100  			m.SetAlterColumnTypeGeneral(b)
  1101  			return nil
  1102  		},
  1103  		Get: func(evalCtx *extendedEvalContext) string {
  1104  			return formatBoolAsPostgresSetting(evalCtx.SessionData.AlterColumnTypeGeneralEnabled)
  1105  		},
  1106  		GlobalDefault: func(sv *settings.Values) string {
  1107  			return formatBoolAsPostgresSetting(experimentalAlterColumnTypeGeneralMode.Get(sv))
  1108  		},
  1109  	},
  1110  }
  1111  
  1112  const compatErrMsg = "this parameter is currently recognized only for compatibility and has no effect in CockroachDB."
  1113  
  1114  func init() {
  1115  	// Initialize delegate.ValidVars.
  1116  	for v := range varGen {
  1117  		delegate.ValidVars[v] = struct{}{}
  1118  	}
  1119  }
  1120  
  1121  // makePostgresBoolGetStringValFn returns a function that evaluates and returns
  1122  // a string representation of the first argument value.
  1123  func makePostgresBoolGetStringValFn(varName string) getStringValFn {
  1124  	return func(
  1125  		ctx context.Context, evalCtx *extendedEvalContext, values []tree.TypedExpr,
  1126  	) (string, error) {
  1127  		if len(values) != 1 {
  1128  			return "", newSingleArgVarError(varName)
  1129  		}
  1130  		val, err := values[0].Eval(&evalCtx.EvalContext)
  1131  		if err != nil {
  1132  			return "", err
  1133  		}
  1134  		if s, ok := val.(*tree.DString); ok {
  1135  			return string(*s), nil
  1136  		}
  1137  		s, err := getSingleBool(varName, val)
  1138  		if err != nil {
  1139  			return "", err
  1140  		}
  1141  		return strconv.FormatBool(bool(*s)), nil
  1142  	}
  1143  }
  1144  
  1145  func makeReadOnlyVar(value string) sessionVar {
  1146  	return sessionVar{
  1147  		Get:           func(_ *extendedEvalContext) string { return value },
  1148  		GlobalDefault: func(_ *settings.Values) string { return value },
  1149  	}
  1150  }
  1151  
  1152  func displayPgBool(val bool) func(_ *settings.Values) string {
  1153  	strVal := formatBoolAsPostgresSetting(val)
  1154  	return func(_ *settings.Values) string { return strVal }
  1155  }
  1156  
  1157  var globalFalse = displayPgBool(false)
  1158  
  1159  // sessionDataTimeZoneFormat returns the appropriate timezone format
  1160  // to output when the `timezone` is required output.
  1161  // If the time zone is a "fixed offset" one, initialized from an offset
  1162  // and not a standard name, then we use a magic format in the Location's
  1163  // name. We attempt to parse that here and retrieve the original offset
  1164  // specified by the user.
  1165  func sessionDataTimeZoneFormat(loc *time.Location) string {
  1166  	locStr := loc.String()
  1167  	_, origRepr, parsed := timeutil.ParseFixedOffsetTimeZone(locStr)
  1168  	if parsed {
  1169  		return origRepr
  1170  	}
  1171  	return locStr
  1172  }
  1173  
  1174  func makeCompatBoolVar(varName string, displayValue, anyValAllowed bool) sessionVar {
  1175  	displayValStr := formatBoolAsPostgresSetting(displayValue)
  1176  	return sessionVar{
  1177  		Get: func(_ *extendedEvalContext) string { return displayValStr },
  1178  		Set: func(_ context.Context, m *sessionDataMutator, s string) error {
  1179  			b, err := parseBoolVar(varName, s)
  1180  			if err != nil {
  1181  				return err
  1182  			}
  1183  			if anyValAllowed || b == displayValue {
  1184  				return nil
  1185  			}
  1186  			telemetry.Inc(sqltelemetry.UnimplementedSessionVarValueCounter(varName, s))
  1187  			allowedVals := []string{displayValStr}
  1188  			if anyValAllowed {
  1189  				allowedVals = append(allowedVals, formatBoolAsPostgresSetting(!displayValue))
  1190  			}
  1191  			err = newVarValueError(varName, s, allowedVals...)
  1192  			err = errors.WithDetail(err, compatErrMsg)
  1193  			return err
  1194  		},
  1195  		GlobalDefault: func(sv *settings.Values) string { return displayValStr },
  1196  	}
  1197  }
  1198  
  1199  func makeCompatIntVar(varName string, displayValue int, extraAllowed ...int) sessionVar {
  1200  	displayValueStr := strconv.Itoa(displayValue)
  1201  	extraAllowedStr := make([]string, len(extraAllowed))
  1202  	for i, v := range extraAllowed {
  1203  		extraAllowedStr[i] = strconv.Itoa(v)
  1204  	}
  1205  	varObj := makeCompatStringVar(varName, displayValueStr, extraAllowedStr...)
  1206  	varObj.GetStringVal = makeIntGetStringValFn(varName)
  1207  	return varObj
  1208  }
  1209  
  1210  func makeCompatStringVar(varName, displayValue string, extraAllowed ...string) sessionVar {
  1211  	allowedVals := append(extraAllowed, strings.ToLower(displayValue))
  1212  	return sessionVar{
  1213  		Get: func(_ *extendedEvalContext) string {
  1214  			return displayValue
  1215  		},
  1216  		Set: func(_ context.Context, m *sessionDataMutator, s string) error {
  1217  			enc := strings.ToLower(s)
  1218  			for _, a := range allowedVals {
  1219  				if enc == a {
  1220  					return nil
  1221  				}
  1222  			}
  1223  			telemetry.Inc(sqltelemetry.UnimplementedSessionVarValueCounter(varName, s))
  1224  			err := newVarValueError(varName, s, allowedVals...)
  1225  			err = errors.WithDetail(err, compatErrMsg)
  1226  			return err
  1227  		},
  1228  		GlobalDefault: func(sv *settings.Values) string { return displayValue },
  1229  	}
  1230  }
  1231  
  1232  // makeIntGetStringValFn returns a getStringValFn which allows
  1233  // the user to provide plain integer values to a SET variable.
  1234  func makeIntGetStringValFn(name string) getStringValFn {
  1235  	return func(ctx context.Context, evalCtx *extendedEvalContext, values []tree.TypedExpr) (string, error) {
  1236  		s, err := getIntVal(&evalCtx.EvalContext, name, values)
  1237  		if err != nil {
  1238  			return "", err
  1239  		}
  1240  		return strconv.FormatInt(s, 10), nil
  1241  	}
  1242  }
  1243  
  1244  // IsSessionVariableConfigurable returns true iff there is a session
  1245  // variable with the given name and it is settable by a client
  1246  // (e.g. in pgwire).
  1247  func IsSessionVariableConfigurable(varName string) (exists, configurable bool) {
  1248  	v, exists := varGen[varName]
  1249  	return exists, v.Set != nil
  1250  }
  1251  
  1252  var varNames = func() []string {
  1253  	res := make([]string, 0, len(varGen))
  1254  	for vName := range varGen {
  1255  		res = append(res, vName)
  1256  	}
  1257  	sort.Strings(res)
  1258  	return res
  1259  }()
  1260  
  1261  // getSingleBool returns the boolean if the input Datum is a DBool,
  1262  // and returns a detailed error message if not.
  1263  func getSingleBool(name string, val tree.Datum) (*tree.DBool, error) {
  1264  	b, ok := val.(*tree.DBool)
  1265  	if !ok {
  1266  		err := pgerror.Newf(pgcode.InvalidParameterValue,
  1267  			"parameter %q requires a Boolean value", name)
  1268  		err = errors.WithDetailf(err,
  1269  			"%s is a %s", val, errors.Safe(val.ResolvedType()))
  1270  		return nil, err
  1271  	}
  1272  	return b, nil
  1273  }
  1274  
  1275  func getSessionVar(name string, missingOk bool) (bool, sessionVar, error) {
  1276  	if _, ok := UnsupportedVars[name]; ok {
  1277  		return false, sessionVar{}, unimplemented.Newf("set."+name,
  1278  			"the configuration setting %q is not supported", name)
  1279  	}
  1280  
  1281  	v, ok := varGen[name]
  1282  	if !ok {
  1283  		if missingOk {
  1284  			return false, sessionVar{}, nil
  1285  		}
  1286  		return false, sessionVar{}, pgerror.Newf(pgcode.UndefinedObject,
  1287  			"unrecognized configuration parameter %q", name)
  1288  	}
  1289  
  1290  	return true, v, nil
  1291  }
  1292  
  1293  // GetSessionVar implements the EvalSessionAccessor interface.
  1294  func (p *planner) GetSessionVar(
  1295  	_ context.Context, varName string, missingOk bool,
  1296  ) (bool, string, error) {
  1297  	name := strings.ToLower(varName)
  1298  	ok, v, err := getSessionVar(name, missingOk)
  1299  	if err != nil || !ok {
  1300  		return ok, "", err
  1301  	}
  1302  
  1303  	return true, v.Get(&p.extendedEvalCtx), nil
  1304  }
  1305  
  1306  // SetSessionVar implements the EvalSessionAccessor interface.
  1307  func (p *planner) SetSessionVar(ctx context.Context, varName, newVal string) error {
  1308  	name := strings.ToLower(varName)
  1309  	_, v, err := getSessionVar(name, false /* missingOk */)
  1310  	if err != nil {
  1311  		return err
  1312  	}
  1313  
  1314  	if v.Set == nil && v.RuntimeSet == nil {
  1315  		return newCannotChangeParameterError(name)
  1316  	}
  1317  	if v.RuntimeSet != nil {
  1318  		return v.RuntimeSet(ctx, &p.extendedEvalCtx, newVal)
  1319  	}
  1320  	return v.Set(ctx, p.sessionDataMutator, newVal)
  1321  }