00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017 #include "postgres.h"
00018
00019 #include <ctype.h>
00020
00021 #include "access/htup_details.h"
00022 #include "access/xact.h"
00023 #include "catalog/pg_authid.h"
00024 #include "commands/variable.h"
00025 #include "miscadmin.h"
00026 #include "utils/acl.h"
00027 #include "utils/builtins.h"
00028 #include "utils/syscache.h"
00029 #include "utils/snapmgr.h"
00030 #include "utils/timestamp.h"
00031 #include "mb/pg_wchar.h"
00032
00033
00034
00035
00036
00037
00038
00039
00040 bool
00041 check_datestyle(char **newval, void **extra, GucSource source)
00042 {
00043 int newDateStyle = DateStyle;
00044 int newDateOrder = DateOrder;
00045 bool have_style = false;
00046 bool have_order = false;
00047 bool ok = true;
00048 char *rawstring;
00049 int *myextra;
00050 char *result;
00051 List *elemlist;
00052 ListCell *l;
00053
00054
00055 rawstring = pstrdup(*newval);
00056
00057
00058 if (!SplitIdentifierString(rawstring, ',', &elemlist))
00059 {
00060
00061 GUC_check_errdetail("List syntax is invalid.");
00062 pfree(rawstring);
00063 list_free(elemlist);
00064 return false;
00065 }
00066
00067 foreach(l, elemlist)
00068 {
00069 char *tok = (char *) lfirst(l);
00070
00071
00072
00073 if (pg_strcasecmp(tok, "ISO") == 0)
00074 {
00075 if (have_style && newDateStyle != USE_ISO_DATES)
00076 ok = false;
00077 newDateStyle = USE_ISO_DATES;
00078 have_style = true;
00079 }
00080 else if (pg_strcasecmp(tok, "SQL") == 0)
00081 {
00082 if (have_style && newDateStyle != USE_SQL_DATES)
00083 ok = false;
00084 newDateStyle = USE_SQL_DATES;
00085 have_style = true;
00086 }
00087 else if (pg_strncasecmp(tok, "POSTGRES", 8) == 0)
00088 {
00089 if (have_style && newDateStyle != USE_POSTGRES_DATES)
00090 ok = false;
00091 newDateStyle = USE_POSTGRES_DATES;
00092 have_style = true;
00093 }
00094 else if (pg_strcasecmp(tok, "GERMAN") == 0)
00095 {
00096 if (have_style && newDateStyle != USE_GERMAN_DATES)
00097 ok = false;
00098 newDateStyle = USE_GERMAN_DATES;
00099 have_style = true;
00100
00101 if (!have_order)
00102 newDateOrder = DATEORDER_DMY;
00103 }
00104 else if (pg_strcasecmp(tok, "YMD") == 0)
00105 {
00106 if (have_order && newDateOrder != DATEORDER_YMD)
00107 ok = false;
00108 newDateOrder = DATEORDER_YMD;
00109 have_order = true;
00110 }
00111 else if (pg_strcasecmp(tok, "DMY") == 0 ||
00112 pg_strncasecmp(tok, "EURO", 4) == 0)
00113 {
00114 if (have_order && newDateOrder != DATEORDER_DMY)
00115 ok = false;
00116 newDateOrder = DATEORDER_DMY;
00117 have_order = true;
00118 }
00119 else if (pg_strcasecmp(tok, "MDY") == 0 ||
00120 pg_strcasecmp(tok, "US") == 0 ||
00121 pg_strncasecmp(tok, "NONEURO", 7) == 0)
00122 {
00123 if (have_order && newDateOrder != DATEORDER_MDY)
00124 ok = false;
00125 newDateOrder = DATEORDER_MDY;
00126 have_order = true;
00127 }
00128 else if (pg_strcasecmp(tok, "DEFAULT") == 0)
00129 {
00130
00131
00132
00133
00134
00135
00136
00137 char *subval;
00138 void *subextra = NULL;
00139
00140 subval = strdup(GetConfigOptionResetString("datestyle"));
00141 if (!subval)
00142 {
00143 ok = false;
00144 break;
00145 }
00146 if (!check_datestyle(&subval, &subextra, source))
00147 {
00148 free(subval);
00149 ok = false;
00150 break;
00151 }
00152 myextra = (int *) subextra;
00153 if (!have_style)
00154 newDateStyle = myextra[0];
00155 if (!have_order)
00156 newDateOrder = myextra[1];
00157 free(subval);
00158 free(subextra);
00159 }
00160 else
00161 {
00162 GUC_check_errdetail("Unrecognized key word: \"%s\".", tok);
00163 pfree(rawstring);
00164 list_free(elemlist);
00165 return false;
00166 }
00167 }
00168
00169 pfree(rawstring);
00170 list_free(elemlist);
00171
00172 if (!ok)
00173 {
00174 GUC_check_errdetail("Conflicting \"datestyle\" specifications.");
00175 return false;
00176 }
00177
00178
00179
00180
00181 result = (char *) malloc(32);
00182 if (!result)
00183 return false;
00184
00185 switch (newDateStyle)
00186 {
00187 case USE_ISO_DATES:
00188 strcpy(result, "ISO");
00189 break;
00190 case USE_SQL_DATES:
00191 strcpy(result, "SQL");
00192 break;
00193 case USE_GERMAN_DATES:
00194 strcpy(result, "German");
00195 break;
00196 default:
00197 strcpy(result, "Postgres");
00198 break;
00199 }
00200 switch (newDateOrder)
00201 {
00202 case DATEORDER_YMD:
00203 strcat(result, ", YMD");
00204 break;
00205 case DATEORDER_DMY:
00206 strcat(result, ", DMY");
00207 break;
00208 default:
00209 strcat(result, ", MDY");
00210 break;
00211 }
00212
00213 free(*newval);
00214 *newval = result;
00215
00216
00217
00218
00219 myextra = (int *) malloc(2 * sizeof(int));
00220 if (!myextra)
00221 return false;
00222 myextra[0] = newDateStyle;
00223 myextra[1] = newDateOrder;
00224 *extra = (void *) myextra;
00225
00226 return true;
00227 }
00228
00229
00230
00231
00232 void
00233 assign_datestyle(const char *newval, void *extra)
00234 {
00235 int *myextra = (int *) extra;
00236
00237 DateStyle = myextra[0];
00238 DateOrder = myextra[1];
00239 }
00240
00241
00242
00243
00244
00245
00246 typedef struct
00247 {
00248 pg_tz *session_timezone;
00249 int CTimeZone;
00250 bool HasCTZSet;
00251 } timezone_extra;
00252
00253
00254
00255
00256 bool
00257 check_timezone(char **newval, void **extra, GucSource source)
00258 {
00259 timezone_extra myextra;
00260 char *endptr;
00261 double hours;
00262
00263
00264
00265
00266
00267
00268
00269
00270 myextra.session_timezone = session_timezone;
00271 myextra.CTimeZone = CTimeZone;
00272 myextra.HasCTZSet = HasCTZSet;
00273
00274 if (pg_strncasecmp(*newval, "interval", 8) == 0)
00275 {
00276
00277
00278
00279
00280 const char *valueptr = *newval;
00281 char *val;
00282 Interval *interval;
00283
00284 valueptr += 8;
00285 while (isspace((unsigned char) *valueptr))
00286 valueptr++;
00287 if (*valueptr++ != '\'')
00288 return false;
00289 val = pstrdup(valueptr);
00290
00291 endptr = strchr(val, '\'');
00292 if (!endptr || endptr[1] != '\0')
00293 {
00294 pfree(val);
00295 return false;
00296 }
00297 *endptr = '\0';
00298
00299
00300
00301
00302
00303
00304
00305 interval = DatumGetIntervalP(DirectFunctionCall3(interval_in,
00306 CStringGetDatum(val),
00307 ObjectIdGetDatum(InvalidOid),
00308 Int32GetDatum(-1)));
00309
00310 pfree(val);
00311 if (interval->month != 0)
00312 {
00313 GUC_check_errdetail("Cannot specify months in time zone interval.");
00314 pfree(interval);
00315 return false;
00316 }
00317 if (interval->day != 0)
00318 {
00319 GUC_check_errdetail("Cannot specify days in time zone interval.");
00320 pfree(interval);
00321 return false;
00322 }
00323
00324
00325 #ifdef HAVE_INT64_TIMESTAMP
00326 myextra.CTimeZone = -(interval->time / USECS_PER_SEC);
00327 #else
00328 myextra.CTimeZone = -interval->time;
00329 #endif
00330 myextra.HasCTZSet = true;
00331
00332 pfree(interval);
00333 }
00334 else
00335 {
00336
00337
00338
00339 hours = strtod(*newval, &endptr);
00340 if (endptr != *newval && *endptr == '\0')
00341 {
00342
00343 myextra.CTimeZone = -hours * SECS_PER_HOUR;
00344 myextra.HasCTZSet = true;
00345 }
00346 else
00347 {
00348
00349
00350
00351 pg_tz *new_tz;
00352
00353 new_tz = pg_tzset(*newval);
00354
00355 if (!new_tz)
00356 {
00357
00358 return false;
00359 }
00360
00361 if (!pg_tz_acceptable(new_tz))
00362 {
00363 GUC_check_errmsg("time zone \"%s\" appears to use leap seconds",
00364 *newval);
00365 GUC_check_errdetail("PostgreSQL does not support leap seconds.");
00366 return false;
00367 }
00368
00369 myextra.session_timezone = new_tz;
00370 myextra.HasCTZSet = false;
00371 }
00372 }
00373
00374
00375
00376
00377
00378
00379
00380
00381
00382
00383 if (myextra.HasCTZSet)
00384 {
00385 char *result = (char *) malloc(64);
00386
00387 if (!result)
00388 return false;
00389 snprintf(result, 64, "%.5f",
00390 (double) (-myextra.CTimeZone) / (double) SECS_PER_HOUR);
00391 free(*newval);
00392 *newval = result;
00393 }
00394
00395
00396
00397
00398 *extra = malloc(sizeof(timezone_extra));
00399 if (!*extra)
00400 return false;
00401 memcpy(*extra, &myextra, sizeof(timezone_extra));
00402
00403 return true;
00404 }
00405
00406
00407
00408
00409 void
00410 assign_timezone(const char *newval, void *extra)
00411 {
00412 timezone_extra *myextra = (timezone_extra *) extra;
00413
00414 session_timezone = myextra->session_timezone;
00415 CTimeZone = myextra->CTimeZone;
00416 HasCTZSet = myextra->HasCTZSet;
00417 }
00418
00419
00420
00421
00422
00423
00424
00425
00426
00427 const char *
00428 show_timezone(void)
00429 {
00430 const char *tzn;
00431
00432 if (HasCTZSet)
00433 {
00434 Interval interval;
00435
00436 interval.month = 0;
00437 interval.day = 0;
00438 #ifdef HAVE_INT64_TIMESTAMP
00439 interval.time = -(CTimeZone * USECS_PER_SEC);
00440 #else
00441 interval.time = -CTimeZone;
00442 #endif
00443
00444 tzn = DatumGetCString(DirectFunctionCall1(interval_out,
00445 IntervalPGetDatum(&interval)));
00446 }
00447 else
00448 tzn = pg_get_timezone_name(session_timezone);
00449
00450 if (tzn != NULL)
00451 return tzn;
00452
00453 return "unknown";
00454 }
00455
00456
00457
00458
00459
00460
00461
00462
00463
00464
00465
00466
00467
00468 bool
00469 check_log_timezone(char **newval, void **extra, GucSource source)
00470 {
00471 pg_tz *new_tz;
00472
00473
00474
00475
00476 new_tz = pg_tzset(*newval);
00477
00478 if (!new_tz)
00479 {
00480
00481 return false;
00482 }
00483
00484 if (!pg_tz_acceptable(new_tz))
00485 {
00486 GUC_check_errmsg("time zone \"%s\" appears to use leap seconds",
00487 *newval);
00488 GUC_check_errdetail("PostgreSQL does not support leap seconds.");
00489 return false;
00490 }
00491
00492
00493
00494
00495 *extra = malloc(sizeof(pg_tz *));
00496 if (!*extra)
00497 return false;
00498 memcpy(*extra, &new_tz, sizeof(pg_tz *));
00499
00500 return true;
00501 }
00502
00503
00504
00505
00506 void
00507 assign_log_timezone(const char *newval, void *extra)
00508 {
00509 log_timezone = *((pg_tz **) extra);
00510 }
00511
00512
00513
00514
00515 const char *
00516 show_log_timezone(void)
00517 {
00518 const char *tzn;
00519
00520 tzn = pg_get_timezone_name(log_timezone);
00521
00522 if (tzn != NULL)
00523 return tzn;
00524
00525 return "unknown";
00526 }
00527
00528
00529
00530
00531
00532
00533
00534
00535
00536
00537
00538
00539
00540
00541
00542
00543 bool
00544 check_transaction_read_only(bool *newval, void **extra, GucSource source)
00545 {
00546 if (*newval == false && XactReadOnly && IsTransactionState())
00547 {
00548
00549 if (IsSubTransaction())
00550 {
00551 GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
00552 GUC_check_errmsg("cannot set transaction read-write mode inside a read-only transaction");
00553 return false;
00554 }
00555
00556 if (FirstSnapshotSet)
00557 {
00558 GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
00559 GUC_check_errmsg("transaction read-write mode must be set before any query");
00560 return false;
00561 }
00562
00563 if (RecoveryInProgress())
00564 {
00565 GUC_check_errcode(ERRCODE_FEATURE_NOT_SUPPORTED);
00566 GUC_check_errmsg("cannot set transaction read-write mode during recovery");
00567 return false;
00568 }
00569 }
00570
00571 return true;
00572 }
00573
00574
00575
00576
00577
00578
00579
00580
00581
00582 bool
00583 check_XactIsoLevel(char **newval, void **extra, GucSource source)
00584 {
00585 int newXactIsoLevel;
00586
00587 if (strcmp(*newval, "serializable") == 0)
00588 {
00589 newXactIsoLevel = XACT_SERIALIZABLE;
00590 }
00591 else if (strcmp(*newval, "repeatable read") == 0)
00592 {
00593 newXactIsoLevel = XACT_REPEATABLE_READ;
00594 }
00595 else if (strcmp(*newval, "read committed") == 0)
00596 {
00597 newXactIsoLevel = XACT_READ_COMMITTED;
00598 }
00599 else if (strcmp(*newval, "read uncommitted") == 0)
00600 {
00601 newXactIsoLevel = XACT_READ_UNCOMMITTED;
00602 }
00603 else if (strcmp(*newval, "default") == 0)
00604 {
00605 newXactIsoLevel = DefaultXactIsoLevel;
00606 }
00607 else
00608 return false;
00609
00610 if (newXactIsoLevel != XactIsoLevel && IsTransactionState())
00611 {
00612 if (FirstSnapshotSet)
00613 {
00614 GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
00615 GUC_check_errmsg("SET TRANSACTION ISOLATION LEVEL must be called before any query");
00616 return false;
00617 }
00618
00619 if (IsSubTransaction())
00620 {
00621 GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
00622 GUC_check_errmsg("SET TRANSACTION ISOLATION LEVEL must not be called in a subtransaction");
00623 return false;
00624 }
00625
00626 if (newXactIsoLevel == XACT_SERIALIZABLE && RecoveryInProgress())
00627 {
00628 GUC_check_errcode(ERRCODE_FEATURE_NOT_SUPPORTED);
00629 GUC_check_errmsg("cannot use serializable mode in a hot standby");
00630 GUC_check_errhint("You can use REPEATABLE READ instead.");
00631 return false;
00632 }
00633 }
00634
00635 *extra = malloc(sizeof(int));
00636 if (!*extra)
00637 return false;
00638 *((int *) *extra) = newXactIsoLevel;
00639
00640 return true;
00641 }
00642
00643 void
00644 assign_XactIsoLevel(const char *newval, void *extra)
00645 {
00646 XactIsoLevel = *((int *) extra);
00647 }
00648
00649 const char *
00650 show_XactIsoLevel(void)
00651 {
00652
00653 switch (XactIsoLevel)
00654 {
00655 case XACT_READ_UNCOMMITTED:
00656 return "read uncommitted";
00657 case XACT_READ_COMMITTED:
00658 return "read committed";
00659 case XACT_REPEATABLE_READ:
00660 return "repeatable read";
00661 case XACT_SERIALIZABLE:
00662 return "serializable";
00663 default:
00664 return "bogus";
00665 }
00666 }
00667
00668
00669
00670
00671
00672 bool
00673 check_transaction_deferrable(bool *newval, void **extra, GucSource source)
00674 {
00675 if (IsSubTransaction())
00676 {
00677 GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
00678 GUC_check_errmsg("SET TRANSACTION [NOT] DEFERRABLE cannot be called within a subtransaction");
00679 return false;
00680 }
00681 if (FirstSnapshotSet)
00682 {
00683 GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
00684 GUC_check_errmsg("SET TRANSACTION [NOT] DEFERRABLE must be called before any query");
00685 return false;
00686 }
00687
00688 return true;
00689 }
00690
00691
00692
00693
00694
00695
00696
00697
00698
00699
00700 bool
00701 check_random_seed(double *newval, void **extra, GucSource source)
00702 {
00703 *extra = malloc(sizeof(int));
00704 if (!*extra)
00705 return false;
00706
00707 *((int *) *extra) = (source >= PGC_S_INTERACTIVE);
00708
00709 return true;
00710 }
00711
00712 void
00713 assign_random_seed(double newval, void *extra)
00714 {
00715
00716 if (*((int *) extra))
00717 DirectFunctionCall1(setseed, Float8GetDatum(newval));
00718 *((int *) extra) = 0;
00719 }
00720
00721 const char *
00722 show_random_seed(void)
00723 {
00724 return "unavailable";
00725 }
00726
00727
00728
00729
00730
00731
00732 bool
00733 check_client_encoding(char **newval, void **extra, GucSource source)
00734 {
00735 int encoding;
00736 const char *canonical_name;
00737
00738
00739 encoding = pg_valid_client_encoding(*newval);
00740 if (encoding < 0)
00741 return false;
00742
00743
00744 canonical_name = pg_encoding_to_char(encoding);
00745
00746
00747
00748
00749
00750
00751
00752
00753
00754
00755
00756
00757
00758
00759
00760 if (PrepareClientEncoding(encoding) < 0)
00761 {
00762 if (IsTransactionState())
00763 {
00764
00765 GUC_check_errcode(ERRCODE_FEATURE_NOT_SUPPORTED);
00766 GUC_check_errdetail("Conversion between %s and %s is not supported.",
00767 canonical_name,
00768 GetDatabaseEncodingName());
00769 }
00770 else
00771 {
00772
00773 GUC_check_errdetail("Cannot change \"client_encoding\" now.");
00774 }
00775 return false;
00776 }
00777
00778
00779
00780
00781
00782
00783
00784
00785
00786
00787
00788 if (strcmp(*newval, canonical_name) != 0 &&
00789 strcmp(*newval, "UNICODE") != 0)
00790 {
00791 free(*newval);
00792 *newval = strdup(canonical_name);
00793 if (!*newval)
00794 return false;
00795 }
00796
00797
00798
00799
00800 *extra = malloc(sizeof(int));
00801 if (!*extra)
00802 return false;
00803 *((int *) *extra) = encoding;
00804
00805 return true;
00806 }
00807
00808 void
00809 assign_client_encoding(const char *newval, void *extra)
00810 {
00811 int encoding = *((int *) extra);
00812
00813
00814 if (SetClientEncoding(encoding) < 0)
00815 elog(LOG, "SetClientEncoding(%d) failed", encoding);
00816 }
00817
00818
00819
00820
00821
00822
00823 typedef struct
00824 {
00825
00826 Oid roleid;
00827 bool is_superuser;
00828 } role_auth_extra;
00829
00830 bool
00831 check_session_authorization(char **newval, void **extra, GucSource source)
00832 {
00833 HeapTuple roleTup;
00834 Oid roleid;
00835 bool is_superuser;
00836 role_auth_extra *myextra;
00837
00838
00839 if (*newval == NULL)
00840 return true;
00841
00842 if (!IsTransactionState())
00843 {
00844
00845
00846
00847
00848
00849 return false;
00850 }
00851
00852
00853 roleTup = SearchSysCache1(AUTHNAME, PointerGetDatum(*newval));
00854 if (!HeapTupleIsValid(roleTup))
00855 {
00856 GUC_check_errmsg("role \"%s\" does not exist", *newval);
00857 return false;
00858 }
00859
00860 roleid = HeapTupleGetOid(roleTup);
00861 is_superuser = ((Form_pg_authid) GETSTRUCT(roleTup))->rolsuper;
00862
00863 ReleaseSysCache(roleTup);
00864
00865
00866 myextra = (role_auth_extra *) malloc(sizeof(role_auth_extra));
00867 if (!myextra)
00868 return false;
00869 myextra->roleid = roleid;
00870 myextra->is_superuser = is_superuser;
00871 *extra = (void *) myextra;
00872
00873 return true;
00874 }
00875
00876 void
00877 assign_session_authorization(const char *newval, void *extra)
00878 {
00879 role_auth_extra *myextra = (role_auth_extra *) extra;
00880
00881
00882 if (!myextra)
00883 return;
00884
00885 SetSessionAuthorization(myextra->roleid, myextra->is_superuser);
00886 }
00887
00888
00889
00890
00891
00892
00893
00894
00895
00896 extern char *role_string;
00897
00898 bool
00899 check_role(char **newval, void **extra, GucSource source)
00900 {
00901 HeapTuple roleTup;
00902 Oid roleid;
00903 bool is_superuser;
00904 role_auth_extra *myextra;
00905
00906 if (strcmp(*newval, "none") == 0)
00907 {
00908
00909 roleid = InvalidOid;
00910 is_superuser = false;
00911 }
00912 else
00913 {
00914 if (!IsTransactionState())
00915 {
00916
00917
00918
00919
00920
00921 return false;
00922 }
00923
00924
00925 roleTup = SearchSysCache1(AUTHNAME, PointerGetDatum(*newval));
00926 if (!HeapTupleIsValid(roleTup))
00927 {
00928 GUC_check_errmsg("role \"%s\" does not exist", *newval);
00929 return false;
00930 }
00931
00932 roleid = HeapTupleGetOid(roleTup);
00933 is_superuser = ((Form_pg_authid) GETSTRUCT(roleTup))->rolsuper;
00934
00935 ReleaseSysCache(roleTup);
00936
00937
00938
00939
00940 if (!is_member_of_role(GetSessionUserId(), roleid))
00941 {
00942 GUC_check_errcode(ERRCODE_INSUFFICIENT_PRIVILEGE);
00943 GUC_check_errmsg("permission denied to set role \"%s\"",
00944 *newval);
00945 return false;
00946 }
00947 }
00948
00949
00950 myextra = (role_auth_extra *) malloc(sizeof(role_auth_extra));
00951 if (!myextra)
00952 return false;
00953 myextra->roleid = roleid;
00954 myextra->is_superuser = is_superuser;
00955 *extra = (void *) myextra;
00956
00957 return true;
00958 }
00959
00960 void
00961 assign_role(const char *newval, void *extra)
00962 {
00963 role_auth_extra *myextra = (role_auth_extra *) extra;
00964
00965 SetCurrentRoleId(myextra->roleid, myextra->is_superuser);
00966 }
00967
00968 const char *
00969 show_role(void)
00970 {
00971
00972
00973
00974
00975
00976
00977
00978 if (!OidIsValid(GetCurrentRoleId()))
00979 return "none";
00980
00981
00982 return role_string ? role_string : "none";
00983 }