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 }