1 /*------------------------------------------------------------------------- 2 * 3 * variable.c 4 * Routines for handling specialized SET variables. 5 * 6 * 7 * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group 8 * Portions Copyright (c) 1994, Regents of the University of California 9 * 10 * 11 * IDENTIFICATION 12 * src/backend/commands/variable.c 13 * 14 *------------------------------------------------------------------------- 15 */ 16 17 #include "postgres.h" 18 19 #include <ctype.h> 20 21 #include "access/htup_details.h" 22 #include "access/parallel.h" 23 #include "access/xact.h" 24 #include "access/xlog.h" 25 #include "catalog/pg_authid.h" 26 #include "commands/variable.h" 27 #include "mb/pg_wchar.h" 28 #include "miscadmin.h" 29 #include "utils/acl.h" 30 #include "utils/builtins.h" 31 #include "utils/snapmgr.h" 32 #include "utils/syscache.h" 33 #include "utils/timestamp.h" 34 #include "utils/varlena.h" 35 36 /* 37 * DATESTYLE 38 */ 39 40 /* 41 * check_datestyle: GUC check_hook for datestyle 42 */ 43 bool 44 check_datestyle(char **newval, void **extra, GucSource source) 45 { 46 int newDateStyle = DateStyle; 47 int newDateOrder = DateOrder; 48 bool have_style = false; 49 bool have_order = false; 50 bool ok = true; 51 char *rawstring; 52 int *myextra; 53 char *result; 54 List *elemlist; 55 ListCell *l; 56 57 /* Need a modifiable copy of string */ 58 rawstring = pstrdup(*newval); 59 60 /* Parse string into list of identifiers */ have_createrole_privilege(void)61 if (!SplitIdentifierString(rawstring, ',', &elemlist)) 62 { 63 /* syntax error in list */ 64 GUC_check_errdetail("List syntax is invalid."); 65 pfree(rawstring); 66 list_free(elemlist); 67 return false; 68 } 69 70 foreach(l, elemlist) CreateRole(ParseState * pstate,CreateRoleStmt * stmt)71 { 72 char *tok = (char *) lfirst(l); 73 74 /* Ugh. Somebody ought to write a table driven version -- mjl */ 75 76 if (pg_strcasecmp(tok, "ISO") == 0) 77 { 78 if (have_style && newDateStyle != USE_ISO_DATES) 79 ok = false; /* conflicting styles */ 80 newDateStyle = USE_ISO_DATES; 81 have_style = true; 82 } 83 else if (pg_strcasecmp(tok, "SQL") == 0) 84 { 85 if (have_style && newDateStyle != USE_SQL_DATES) 86 ok = false; /* conflicting styles */ 87 newDateStyle = USE_SQL_DATES; 88 have_style = true; 89 } 90 else if (pg_strncasecmp(tok, "POSTGRES", 8) == 0) 91 { 92 if (have_style && newDateStyle != USE_POSTGRES_DATES) 93 ok = false; /* conflicting styles */ 94 newDateStyle = USE_POSTGRES_DATES; 95 have_style = true; 96 } 97 else if (pg_strcasecmp(tok, "GERMAN") == 0) 98 { 99 if (have_style && newDateStyle != USE_GERMAN_DATES) 100 ok = false; /* conflicting styles */ 101 newDateStyle = USE_GERMAN_DATES; 102 have_style = true; 103 /* GERMAN also sets DMY, unless explicitly overridden */ 104 if (!have_order) 105 newDateOrder = DATEORDER_DMY; 106 } 107 else if (pg_strcasecmp(tok, "YMD") == 0) 108 { 109 if (have_order && newDateOrder != DATEORDER_YMD) 110 ok = false; /* conflicting orders */ 111 newDateOrder = DATEORDER_YMD; 112 have_order = true; 113 } 114 else if (pg_strcasecmp(tok, "DMY") == 0 || 115 pg_strncasecmp(tok, "EURO", 4) == 0) 116 { 117 if (have_order && newDateOrder != DATEORDER_DMY) 118 ok = false; /* conflicting orders */ 119 newDateOrder = DATEORDER_DMY; 120 have_order = true; 121 } 122 else if (pg_strcasecmp(tok, "MDY") == 0 || 123 pg_strcasecmp(tok, "US") == 0 || 124 pg_strncasecmp(tok, "NONEURO", 7) == 0) 125 { 126 if (have_order && newDateOrder != DATEORDER_MDY) 127 ok = false; /* conflicting orders */ 128 newDateOrder = DATEORDER_MDY; 129 have_order = true; 130 } 131 else if (pg_strcasecmp(tok, "DEFAULT") == 0) 132 { 133 /* 134 * Easiest way to get the current DEFAULT state is to fetch the 135 * DEFAULT string from guc.c and recursively parse it. 136 * 137 * We can't simply "return check_datestyle(...)" because we need 138 * to handle constructs like "DEFAULT, ISO". 139 */ 140 char *subval; 141 void *subextra = NULL; 142 143 subval = strdup(GetConfigOptionResetString("datestyle")); 144 if (!subval) 145 { 146 ok = false; 147 break; 148 } 149 if (!check_datestyle(&subval, &subextra, source)) 150 { 151 free(subval); 152 ok = false; 153 break; 154 } 155 myextra = (int *) subextra; 156 if (!have_style) 157 newDateStyle = myextra[0]; 158 if (!have_order) 159 newDateOrder = myextra[1]; 160 free(subval); 161 free(subextra); 162 } 163 else 164 { 165 GUC_check_errdetail("Unrecognized key word: \"%s\".", tok); 166 pfree(rawstring); 167 list_free(elemlist); 168 return false; 169 } 170 } 171 172 pfree(rawstring); 173 list_free(elemlist); 174 175 if (!ok) 176 { 177 GUC_check_errdetail("Conflicting \"datestyle\" specifications."); 178 return false; 179 } 180 181 /* 182 * Prepare the canonical string to return. GUC wants it malloc'd. 183 */ 184 result = (char *) malloc(32); 185 if (!result) 186 return false; 187 188 switch (newDateStyle) 189 { 190 case USE_ISO_DATES: 191 strcpy(result, "ISO"); 192 break; 193 case USE_SQL_DATES: 194 strcpy(result, "SQL"); 195 break; 196 case USE_GERMAN_DATES: 197 strcpy(result, "German"); 198 break; 199 default: 200 strcpy(result, "Postgres"); 201 break; 202 } 203 switch (newDateOrder) 204 { 205 case DATEORDER_YMD: 206 strcat(result, ", YMD"); 207 break; 208 case DATEORDER_DMY: 209 strcat(result, ", DMY"); 210 break; 211 default: 212 strcat(result, ", MDY"); 213 break; 214 } 215 216 free(*newval); 217 *newval = result; 218 219 /* 220 * Set up the "extra" struct actually used by assign_datestyle. 221 */ 222 myextra = (int *) malloc(2 * sizeof(int)); 223 if (!myextra) 224 return false; 225 myextra[0] = newDateStyle; 226 myextra[1] = newDateOrder; 227 *extra = (void *) myextra; 228 229 return true; 230 } 231 232 /* 233 * assign_datestyle: GUC assign_hook for datestyle 234 */ 235 void 236 assign_datestyle(const char *newval, void *extra) 237 { 238 int *myextra = (int *) extra; 239 240 DateStyle = myextra[0]; 241 DateOrder = myextra[1]; 242 } 243 244 245 /* 246 * TIMEZONE 247 */ 248 249 /* 250 * check_timezone: GUC check_hook for timezone 251 */ 252 bool 253 check_timezone(char **newval, void **extra, GucSource source) 254 { 255 pg_tz *new_tz; 256 long gmtoffset; 257 char *endptr; 258 double hours; 259 260 if (pg_strncasecmp(*newval, "interval", 8) == 0) 261 { 262 /* 263 * Support INTERVAL 'foo'. This is for SQL spec compliance, not 264 * because it has any actual real-world usefulness. 265 */ 266 const char *valueptr = *newval; 267 char *val; 268 Interval *interval; 269 270 valueptr += 8; 271 while (isspace((unsigned char) *valueptr)) 272 valueptr++; 273 if (*valueptr++ != '\'') 274 return false; 275 val = pstrdup(valueptr); 276 /* Check and remove trailing quote */ 277 endptr = strchr(val, '\''); 278 if (!endptr || endptr[1] != '\0') 279 { 280 pfree(val); 281 return false; 282 } 283 *endptr = '\0'; 284 285 /* 286 * Try to parse it. XXX an invalid interval format will result in 287 * ereport(ERROR), which is not desirable for GUC. We did what we 288 * could to guard against this in flatten_set_variable_args, but a 289 * string coming in from postgresql.conf might contain anything. 290 */ 291 interval = DatumGetIntervalP(DirectFunctionCall3(interval_in, 292 CStringGetDatum(val), 293 ObjectIdGetDatum(InvalidOid), 294 Int32GetDatum(-1))); 295 296 pfree(val); 297 if (interval->month != 0) 298 { 299 GUC_check_errdetail("Cannot specify months in time zone interval."); 300 pfree(interval); 301 return false; 302 } 303 if (interval->day != 0) 304 { 305 GUC_check_errdetail("Cannot specify days in time zone interval."); 306 pfree(interval); 307 return false; 308 } 309 310 /* Here we change from SQL to Unix sign convention */ 311 gmtoffset = -(interval->time / USECS_PER_SEC); 312 new_tz = pg_tzset_offset(gmtoffset); 313 314 pfree(interval); 315 } 316 else 317 { 318 /* 319 * Try it as a numeric number of hours (possibly fractional). 320 */ 321 hours = strtod(*newval, &endptr); 322 if (endptr != *newval && *endptr == '\0') 323 { 324 /* Here we change from SQL to Unix sign convention */ 325 gmtoffset = -hours * SECS_PER_HOUR; 326 new_tz = pg_tzset_offset(gmtoffset); 327 } 328 else 329 { 330 /* 331 * Otherwise assume it is a timezone name, and try to load it. 332 */ 333 new_tz = pg_tzset(*newval); 334 335 if (!new_tz) 336 { 337 /* Doesn't seem to be any great value in errdetail here */ 338 return false; 339 } 340 341 if (!pg_tz_acceptable(new_tz)) 342 { 343 GUC_check_errmsg("time zone \"%s\" appears to use leap seconds", 344 *newval); 345 GUC_check_errdetail("PostgreSQL does not support leap seconds."); 346 return false; 347 } 348 } 349 } 350 351 /* Test for failure in pg_tzset_offset, which we assume is out-of-range */ 352 if (!new_tz) 353 { 354 GUC_check_errdetail("UTC timezone offset is out of range."); 355 return false; 356 } 357 358 /* 359 * Pass back data for assign_timezone to use 360 */ 361 *extra = malloc(sizeof(pg_tz *)); 362 if (!*extra) 363 return false; 364 *((pg_tz **) *extra) = new_tz; 365 366 return true; 367 } 368 369 /* 370 * assign_timezone: GUC assign_hook for timezone 371 */ 372 void 373 assign_timezone(const char *newval, void *extra) 374 { 375 session_timezone = *((pg_tz **) extra); 376 } 377 378 /* 379 * show_timezone: GUC show_hook for timezone 380 */ 381 const char * 382 show_timezone(void) 383 { 384 const char *tzn; 385 386 /* Always show the zone's canonical name */ 387 tzn = pg_get_timezone_name(session_timezone); 388 389 if (tzn != NULL) 390 return tzn; 391 392 return "unknown"; 393 } 394 395 396 /* 397 * LOG_TIMEZONE 398 * 399 * For log_timezone, we don't support the interval-based methods of setting a 400 * zone, which are only there for SQL spec compliance not because they're 401 * actually useful. 402 */ 403 404 /* 405 * check_log_timezone: GUC check_hook for log_timezone 406 */ 407 bool 408 check_log_timezone(char **newval, void **extra, GucSource source) 409 { 410 pg_tz *new_tz; 411 412 /* 413 * Assume it is a timezone name, and try to load it. 414 */ 415 new_tz = pg_tzset(*newval); 416 417 if (!new_tz) 418 { 419 /* Doesn't seem to be any great value in errdetail here */ 420 return false; 421 } 422 423 if (!pg_tz_acceptable(new_tz)) 424 { 425 GUC_check_errmsg("time zone \"%s\" appears to use leap seconds", 426 *newval); 427 GUC_check_errdetail("PostgreSQL does not support leap seconds."); 428 return false; 429 } 430 431 /* 432 * Pass back data for assign_log_timezone to use 433 */ 434 *extra = malloc(sizeof(pg_tz *)); 435 if (!*extra) 436 return false; 437 *((pg_tz **) *extra) = new_tz; 438 439 return true; 440 } 441 442 /* 443 * assign_log_timezone: GUC assign_hook for log_timezone 444 */ 445 void 446 assign_log_timezone(const char *newval, void *extra) 447 { 448 log_timezone = *((pg_tz **) extra); 449 } 450 451 /* 452 * show_log_timezone: GUC show_hook for log_timezone 453 */ 454 const char * 455 show_log_timezone(void) 456 { 457 const char *tzn; 458 459 /* Always show the zone's canonical name */ 460 tzn = pg_get_timezone_name(log_timezone); 461 462 if (tzn != NULL) 463 return tzn; 464 465 return "unknown"; 466 } 467 468 469 /* 470 * SET TRANSACTION READ ONLY and SET TRANSACTION READ WRITE 471 * 472 * We allow idempotent changes (r/w -> r/w and r/o -> r/o) at any time, and 473 * we also always allow changes from read-write to read-only. However, 474 * read-only may be changed to read-write only when in a top-level transaction 475 * that has not yet taken an initial snapshot. Can't do it in a hot standby, 476 * either. 477 * 478 * If we are not in a transaction at all, just allow the change; it means 479 * nothing since XactReadOnly will be reset by the next StartTransaction(). 480 * The IsTransactionState() test protects us against trying to check 481 * RecoveryInProgress() in contexts where shared memory is not accessible. 482 * (Similarly, if we're restoring state in a parallel worker, just allow 483 * the change.) 484 */ 485 bool 486 check_transaction_read_only(bool *newval, void **extra, GucSource source) 487 { 488 if (*newval == false && XactReadOnly && IsTransactionState() && !InitializingParallelWorker) 489 { 490 /* Can't go to r/w mode inside a r/o transaction */ 491 if (IsSubTransaction()) 492 { 493 GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION); 494 GUC_check_errmsg("cannot set transaction read-write mode inside a read-only transaction"); 495 return false; 496 } 497 /* Top level transaction can't change to r/w after first snapshot. */ 498 if (FirstSnapshotSet) 499 { 500 GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION); 501 GUC_check_errmsg("transaction read-write mode must be set before any query"); 502 return false; 503 } 504 /* Can't go to r/w mode while recovery is still active */ 505 if (RecoveryInProgress()) 506 { 507 GUC_check_errcode(ERRCODE_FEATURE_NOT_SUPPORTED); 508 GUC_check_errmsg("cannot set transaction read-write mode during recovery"); 509 return false; 510 } 511 } 512 513 return true; 514 } 515 516 /* 517 * SET TRANSACTION ISOLATION LEVEL 518 * 519 * We allow idempotent changes at any time, but otherwise this can only be 520 * changed in a toplevel transaction that has not yet taken a snapshot. 521 * 522 * As in check_transaction_read_only, allow it if not inside a transaction. 523 */ 524 bool 525 check_XactIsoLevel(int *newval, void **extra, GucSource source) 526 { 527 int newXactIsoLevel = *newval; 528 529 if (newXactIsoLevel != XactIsoLevel && IsTransactionState()) 530 { AlterRole(AlterRoleStmt * stmt)531 if (FirstSnapshotSet) 532 { 533 GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION); 534 GUC_check_errmsg("SET TRANSACTION ISOLATION LEVEL must be called before any query"); 535 return false; 536 } 537 /* We ignore a subtransaction setting it to the existing value. */ 538 if (IsSubTransaction()) 539 { 540 GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION); 541 GUC_check_errmsg("SET TRANSACTION ISOLATION LEVEL must not be called in a subtransaction"); 542 return false; 543 } 544 /* Can't go to serializable mode while recovery is still active */ 545 if (newXactIsoLevel == XACT_SERIALIZABLE && RecoveryInProgress()) 546 { 547 GUC_check_errcode(ERRCODE_FEATURE_NOT_SUPPORTED); 548 GUC_check_errmsg("cannot use serializable mode in a hot standby"); 549 GUC_check_errhint("You can use REPEATABLE READ instead."); 550 return false; 551 } 552 } 553 554 return true; 555 } 556 557 /* 558 * SET TRANSACTION [NOT] DEFERRABLE 559 */ 560 561 bool 562 check_transaction_deferrable(bool *newval, void **extra, GucSource source) 563 { 564 if (IsSubTransaction()) 565 { 566 GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION); 567 GUC_check_errmsg("SET TRANSACTION [NOT] DEFERRABLE cannot be called within a subtransaction"); 568 return false; 569 } 570 if (FirstSnapshotSet) 571 { 572 GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION); 573 GUC_check_errmsg("SET TRANSACTION [NOT] DEFERRABLE must be called before any query"); 574 return false; 575 } 576 577 return true; 578 } 579 580 /* 581 * Random number seed 582 * 583 * We can't roll back the random sequence on error, and we don't want 584 * config file reloads to affect it, so we only want interactive SET SEED 585 * commands to set it. We use the "extra" storage to ensure that rollbacks 586 * don't try to do the operation again. 587 */ 588 589 bool 590 check_random_seed(double *newval, void **extra, GucSource source) 591 { 592 *extra = malloc(sizeof(int)); 593 if (!*extra) 594 return false; 595 /* Arm the assign only if source of value is an interactive SET */ 596 *((int *) *extra) = (source >= PGC_S_INTERACTIVE); 597 598 return true; 599 } 600 601 void 602 assign_random_seed(double newval, void *extra) 603 { 604 /* We'll do this at most once for any setting of the GUC variable */ 605 if (*((int *) extra)) 606 DirectFunctionCall1(setseed, Float8GetDatum(newval)); 607 *((int *) extra) = 0; 608 } 609 610 const char * 611 show_random_seed(void) 612 { 613 return "unavailable"; 614 } 615 616 617 /* 618 * SET CLIENT_ENCODING 619 */ 620 621 bool 622 check_client_encoding(char **newval, void **extra, GucSource source) 623 { 624 int encoding; 625 const char *canonical_name; 626 627 /* Look up the encoding by name */ 628 encoding = pg_valid_client_encoding(*newval); 629 if (encoding < 0) 630 return false; 631 632 /* Get the canonical name (no aliases, uniform case) */ 633 canonical_name = pg_encoding_to_char(encoding); 634 635 /* 636 * If we are not within a transaction then PrepareClientEncoding will not 637 * be able to look up the necessary conversion procs. If we are still 638 * starting up, it will return "OK" anyway, and InitializeClientEncoding 639 * will fix things once initialization is far enough along. After 640 * startup, we'll fail. This would only happen if someone tries to change 641 * client_encoding in postgresql.conf and then SIGHUP existing sessions. 642 * It seems like a bad idea for client_encoding to change that way anyhow, 643 * so we don't go out of our way to support it. 644 * 645 * Note: in the postmaster, or any other process that never calls 646 * InitializeClientEncoding, PrepareClientEncoding will always succeed, 647 * and so will SetClientEncoding; but they won't do anything, which is OK. 648 */ 649 if (PrepareClientEncoding(encoding) < 0) 650 { 651 if (IsTransactionState()) 652 { 653 /* Must be a genuine no-such-conversion problem */ 654 GUC_check_errcode(ERRCODE_FEATURE_NOT_SUPPORTED); 655 GUC_check_errdetail("Conversion between %s and %s is not supported.", 656 canonical_name, 657 GetDatabaseEncodingName()); 658 } 659 else 660 { 661 /* Provide a useful complaint */ 662 GUC_check_errdetail("Cannot change \"client_encoding\" now."); 663 } 664 return false; 665 } 666 667 /* 668 * Replace the user-supplied string with the encoding's canonical name. 669 * This gets rid of aliases and case-folding variations. 670 * 671 * XXX Although canonicalizing seems like a good idea in the abstract, it 672 * breaks pre-9.1 JDBC drivers, which expect that if they send "UNICODE" 673 * as the client_encoding setting then it will read back the same way. As 674 * a workaround, don't replace the string if it's "UNICODE". Remove that 675 * hack when pre-9.1 JDBC drivers are no longer in use. 676 */ 677 if (strcmp(*newval, canonical_name) != 0 && 678 strcmp(*newval, "UNICODE") != 0) 679 { 680 free(*newval); 681 *newval = strdup(canonical_name); 682 if (!*newval) 683 return false; 684 } 685 686 /* 687 * Save the encoding's ID in *extra, for use by assign_client_encoding. 688 */ 689 *extra = malloc(sizeof(int)); 690 if (!*extra) 691 return false; 692 *((int *) *extra) = encoding; 693 694 return true; 695 } 696 697 void 698 assign_client_encoding(const char *newval, void *extra) 699 { 700 int encoding = *((int *) extra); 701 702 /* 703 * Parallel workers send data to the leader, not the client. They always 704 * send data using the database encoding. 705 */ 706 if (IsParallelWorker()) 707 { 708 /* 709 * During parallel worker startup, we want to accept the leader's 710 * client_encoding setting so that anyone who looks at the value in 711 * the worker sees the same value that they would see in the leader. 712 */ 713 if (InitializingParallelWorker) 714 return; 715 716 /* 717 * A change other than during startup, for example due to a SET clause 718 * attached to a function definition, should be rejected, as there is 719 * nothing we can do inside the worker to make it take effect. 720 */ 721 ereport(ERROR, 722 (errcode(ERRCODE_INVALID_TRANSACTION_STATE), 723 errmsg("cannot change client_encoding during a parallel operation"))); 724 } 725 726 /* We do not expect an error if PrepareClientEncoding succeeded */ 727 if (SetClientEncoding(encoding) < 0) 728 elog(LOG, "SetClientEncoding(%d) failed", encoding); 729 } 730 731 732 /* 733 * SET SESSION AUTHORIZATION 734 */ 735 736 typedef struct 737 { 738 /* This is the "extra" state for both SESSION AUTHORIZATION and ROLE */ 739 Oid roleid; 740 bool is_superuser; 741 } role_auth_extra; 742 743 bool 744 check_session_authorization(char **newval, void **extra, GucSource source) 745 { 746 HeapTuple roleTup; 747 Form_pg_authid roleform; 748 Oid roleid; 749 bool is_superuser; 750 role_auth_extra *myextra; 751 752 /* Do nothing for the boot_val default of NULL */ 753 if (*newval == NULL) 754 return true; 755 756 if (!IsTransactionState()) 757 { 758 /* 759 * Can't do catalog lookups, so fail. The result of this is that 760 * session_authorization cannot be set in postgresql.conf, which seems 761 * like a good thing anyway, so we don't work hard to avoid it. 762 */ 763 return false; 764 } 765 766 /* Look up the username */ 767 roleTup = SearchSysCache1(AUTHNAME, PointerGetDatum(*newval)); 768 if (!HeapTupleIsValid(roleTup)) 769 { 770 /* 771 * When source == PGC_S_TEST, we don't throw a hard error for a 772 * nonexistent user name, only a NOTICE. See comments in guc.h. 773 */ 774 if (source == PGC_S_TEST) 775 { 776 ereport(NOTICE, 777 (errcode(ERRCODE_UNDEFINED_OBJECT), 778 errmsg("role \"%s\" does not exist", *newval))); 779 return true; 780 } 781 GUC_check_errmsg("role \"%s\" does not exist", *newval); 782 return false; 783 } 784 785 roleform = (Form_pg_authid) GETSTRUCT(roleTup); 786 roleid = roleform->oid; 787 is_superuser = roleform->rolsuper; 788 789 ReleaseSysCache(roleTup); 790 791 /* Set up "extra" struct for assign_session_authorization to use */ 792 myextra = (role_auth_extra *) malloc(sizeof(role_auth_extra)); 793 if (!myextra) 794 return false; 795 myextra->roleid = roleid; 796 myextra->is_superuser = is_superuser; 797 *extra = (void *) myextra; 798 799 return true; 800 } 801 802 void 803 assign_session_authorization(const char *newval, void *extra) 804 { 805 role_auth_extra *myextra = (role_auth_extra *) extra; 806 807 /* Do nothing for the boot_val default of NULL */ 808 if (!myextra) 809 return; 810 811 SetSessionAuthorization(myextra->roleid, myextra->is_superuser); 812 } 813 814 815 /* 816 * SET ROLE 817 * 818 * The SQL spec requires "SET ROLE NONE" to unset the role, so we hardwire 819 * a translation of "none" to InvalidOid. Otherwise this is much like 820 * SET SESSION AUTHORIZATION. 821 */ 822 extern char *role_string; /* in guc.c */ 823 824 bool 825 check_role(char **newval, void **extra, GucSource source) 826 { 827 HeapTuple roleTup; 828 Oid roleid; 829 bool is_superuser; 830 role_auth_extra *myextra; 831 Form_pg_authid roleform; 832 833 if (strcmp(*newval, "none") == 0) 834 { 835 /* hardwired translation */ 836 roleid = InvalidOid; 837 is_superuser = false; 838 } 839 else 840 { 841 if (!IsTransactionState()) 842 { 843 /* 844 * Can't do catalog lookups, so fail. The result of this is that 845 * role cannot be set in postgresql.conf, which seems like a good 846 * thing anyway, so we don't work hard to avoid it. 847 */ 848 return false; 849 } 850 851 /* 852 * When source == PGC_S_TEST, we don't throw a hard error for a 853 * nonexistent user name or insufficient privileges, only a NOTICE. 854 * See comments in guc.h. 855 */ 856 857 /* Look up the username */ 858 roleTup = SearchSysCache1(AUTHNAME, PointerGetDatum(*newval)); 859 if (!HeapTupleIsValid(roleTup)) 860 { 861 if (source == PGC_S_TEST) 862 { 863 ereport(NOTICE, 864 (errcode(ERRCODE_UNDEFINED_OBJECT), 865 errmsg("role \"%s\" does not exist", *newval))); 866 return true; 867 } 868 GUC_check_errmsg("role \"%s\" does not exist", *newval); 869 return false; 870 } 871 872 roleform = (Form_pg_authid) GETSTRUCT(roleTup); 873 roleid = roleform->oid; 874 is_superuser = roleform->rolsuper; 875 876 ReleaseSysCache(roleTup); 877 878 /* 879 * Verify that session user is allowed to become this role, but skip 880 * this in parallel mode, where we must blindly recreate the parallel 881 * leader's state. 882 */ 883 if (!InitializingParallelWorker && 884 !is_member_of_role(GetSessionUserId(), roleid)) 885 { 886 if (source == PGC_S_TEST) 887 { 888 ereport(NOTICE, 889 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), 890 errmsg("permission will be denied to set role \"%s\"", 891 *newval))); 892 return true; 893 } 894 GUC_check_errcode(ERRCODE_INSUFFICIENT_PRIVILEGE); 895 GUC_check_errmsg("permission denied to set role \"%s\"", 896 *newval); 897 return false; 898 } 899 } 900 901 /* Set up "extra" struct for assign_role to use */ 902 myextra = (role_auth_extra *) malloc(sizeof(role_auth_extra)); 903 if (!myextra) 904 return false; 905 myextra->roleid = roleid; 906 myextra->is_superuser = is_superuser; 907 *extra = (void *) myextra; 908 909 return true; 910 } 911 912 void 913 assign_role(const char *newval, void *extra) 914 { AlterRoleSet(AlterRoleSetStmt * stmt)915 role_auth_extra *myextra = (role_auth_extra *) extra; 916 917 SetCurrentRoleId(myextra->roleid, myextra->is_superuser); 918 } 919 920 const char * 921 show_role(void) 922 { 923 /* 924 * Check whether SET ROLE is active; if not return "none". This is a 925 * kluge to deal with the fact that SET SESSION AUTHORIZATION logically 926 * resets SET ROLE to NONE, but we cannot set the GUC role variable from 927 * assign_session_authorization (because we haven't got enough info to 928 * call set_config_option). 929 */ 930 if (!OidIsValid(GetCurrentRoleId())) 931 return "none"; 932 933 /* Otherwise we can just use the GUC string */ 934 return role_string ? role_string : "none"; 935 } 936