variable.c 18.6 KB
Newer Older
1 2 3
/*-------------------------------------------------------------------------
 *
 * variable.c
4 5
 *		Routines for handling specialized SET variables.
 *
6
 *
Bruce Momjian's avatar
Bruce Momjian committed
7
 * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
8 9
 * Portions Copyright (c) 1994, Regents of the University of California
 *
10
 *
11
 * IDENTIFICATION
12
 *	  $Header: /cvsroot/pgsql/src/backend/commands/variable.c,v 1.89 2003/11/06 22:08:14 petere Exp $
13
 *
14
 *-------------------------------------------------------------------------
15 16
 */

17 18
#include "postgres.h"

19
#include <ctype.h>
Bruce Momjian's avatar
Bruce Momjian committed
20
#include <time.h>
Bruce Momjian's avatar
Bruce Momjian committed
21 22

#include "access/xact.h"
23
#include "catalog/pg_shadow.h"
24
#include "commands/variable.h"
Bruce Momjian's avatar
Bruce Momjian committed
25 26
#include "miscadmin.h"
#include "utils/builtins.h"
27
#include "utils/guc.h"
28
#include "utils/syscache.h"
29
#include "utils/tqual.h"
Marc G. Fournier's avatar
Marc G. Fournier committed
30
#include "mb/pg_wchar.h"
31

32 33 34 35 36
/*
 * Some systems have tzname[] but don't declare it in <time.h>.  Use this
 * to duplicate the test in AC_STRUCT_TIMEZONE.
 */
#ifdef HAVE_TZNAME
Bruce Momjian's avatar
Bruce Momjian committed
37
#ifndef tzname					/* For SGI.  */
38 39 40 41 42
extern char *tzname[];
#endif
#endif


Bruce Momjian's avatar
Bruce Momjian committed
43
/*
44
 * DATESTYLE
Bruce Momjian's avatar
Bruce Momjian committed
45
 */
46

Bruce Momjian's avatar
Bruce Momjian committed
47
/*
48
 * assign_datestyle: GUC assign_hook for datestyle
Bruce Momjian's avatar
Bruce Momjian committed
49
 */
50 51
const char *
assign_datestyle(const char *value, bool doit, bool interactive)
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
52
{
53
	int			newDateStyle = DateStyle;
54
	int			newDateOrder = DateOrder;
55
	bool		ok = true;
56 57
	int			scnt = 0,
				ocnt = 0;
58 59 60 61
	char	   *rawstring;
	char	   *result;
	List	   *elemlist;
	List	   *l;
62

63 64
	/* Need a modifiable copy of string */
	rawstring = pstrdup(value);
65

66 67
	/* Parse string into list of identifiers */
	if (!SplitIdentifierString(rawstring, ',', &elemlist))
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
68
	{
69 70 71 72
		/* syntax error in list */
		pfree(rawstring);
		freeList(elemlist);
		if (interactive)
73 74
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
75
					 errmsg("invalid list syntax for parameter \"datestyle\"")));
76 77 78 79 80 81 82
		return NULL;
	}

	foreach(l, elemlist)
	{
		char	   *tok = (char *) lfirst(l);

83
		/* Ugh. Somebody ought to write a table driven version -- mjl */
84

85
		if (strcasecmp(tok, "ISO") == 0)
86
		{
87
			newDateStyle = USE_ISO_DATES;
88
			scnt++;
89
		}
90
		else if (strcasecmp(tok, "SQL") == 0)
91
		{
92
			newDateStyle = USE_SQL_DATES;
93
			scnt++;
94
		}
95
		else if (strncasecmp(tok, "POSTGRES", 8) == 0)
96
		{
97
			newDateStyle = USE_POSTGRES_DATES;
98
			scnt++;
99
		}
100
		else if (strcasecmp(tok, "GERMAN") == 0)
101
		{
102
			newDateStyle = USE_GERMAN_DATES;
103 104 105 106
			scnt++;
			/* GERMAN also sets DMY, unless explicitly overridden */
			if (ocnt == 0)
				newDateOrder = DATEORDER_DMY;
107
		}
108
		else if (strcasecmp(tok, "YMD") == 0)
109
		{
110 111
			newDateOrder = DATEORDER_YMD;
			ocnt++;
112
		}
113 114
		else if (strcasecmp(tok, "DMY") == 0 ||
				 strncasecmp(tok, "EURO", 4) == 0)
115
		{
116 117 118 119 120 121 122 123 124
			newDateOrder = DATEORDER_DMY;
			ocnt++;
		}
		else if (strcasecmp(tok, "MDY") == 0 ||
				 strcasecmp(tok, "US") == 0 ||
				 strncasecmp(tok, "NONEURO", 7) == 0)
		{
			newDateOrder = DATEORDER_MDY;
			ocnt++;
125
		}
126
		else if (strcasecmp(tok, "DEFAULT") == 0)
127
		{
128 129 130 131
			/*
			 * Easiest way to get the current DEFAULT state is to fetch
			 * the DEFAULT string from guc.c and recursively parse it.
			 *
Bruce Momjian's avatar
Bruce Momjian committed
132 133
			 * We can't simply "return assign_datestyle(...)" because we need
			 * to handle constructs like "DEFAULT, ISO".
134 135
			 */
			int			saveDateStyle = DateStyle;
136
			int			saveDateOrder = DateOrder;
137 138 139 140
			const char *subval;

			subval = assign_datestyle(GetConfigOptionResetString("datestyle"),
									  true, interactive);
141 142 143 144
			if (scnt == 0)
				newDateStyle = DateStyle;
			if (ocnt == 0)
				newDateOrder = DateOrder;
145
			DateStyle = saveDateStyle;
146
			DateOrder = saveDateOrder;
147 148 149 150 151 152 153 154
			if (!subval)
			{
				ok = false;
				break;
			}
			/* Here we know that our own return value is always malloc'd */
			/* when doit is true */
			free((char *) subval);
155
		}
156
		else
157 158
		{
			if (interactive)
159 160
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
161
						 errmsg("unrecognized \"datestyle\" key word: \"%s\"",
162
								tok)));
163 164 165
			ok = false;
			break;
		}
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
166
	}
167

168
	if (scnt > 1 || ocnt > 1)
169
		ok = false;
170

171 172
	pfree(rawstring);
	freeList(elemlist);
173

174
	if (!ok)
175
	{
176
		if (interactive)
177 178
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
179
					 errmsg("conflicting \"datestyle\" specifications")));
180 181
		return NULL;
	}
182

183 184 185 186 187
	/*
	 * If we aren't going to do the assignment, just return OK indicator.
	 */
	if (!doit)
		return value;
188

189
	/*
Bruce Momjian's avatar
Bruce Momjian committed
190
	 * Prepare the canonical string to return.	GUC wants it malloc'd.
191 192 193 194
	 */
	result = (char *) malloc(32);
	if (!result)
		return NULL;
195

196 197 198 199 200 201 202 203 204
	switch (newDateStyle)
	{
		case USE_ISO_DATES:
			strcpy(result, "ISO");
			break;
		case USE_SQL_DATES:
			strcpy(result, "SQL");
			break;
		case USE_GERMAN_DATES:
205
			strcpy(result, "German");
206 207
			break;
		default:
208
			strcpy(result, "Postgres");
209
			break;
210
	}
211 212 213 214 215 216 217 218 219 220 221 222
	switch (newDateOrder)
	{
		case DATEORDER_YMD:
			strcat(result, ", YMD");
			break;
		case DATEORDER_DMY:
			strcat(result, ", DMY");
			break;
		default:
			strcat(result, ", MDY");
			break;
	}
223 224

	/*
Bruce Momjian's avatar
Bruce Momjian committed
225 226
	 * Finally, it's safe to assign to the global variables; the
	 * assignment cannot fail now.
227 228
	 */
	DateStyle = newDateStyle;
229
	DateOrder = newDateOrder;
230

231
	return result;
232 233
}

234

235 236
/*
 * TIMEZONE
237 238
 */

239 240 241
/*
 * Storage for TZ env var is allocated with an arbitrary size of 64 bytes.
 */
242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
#define TZBUF_LEN	64

static char tzbuf[TZBUF_LEN];

/*
 * First time through, we remember the original environment TZ value, if any.
 */
static bool have_saved_tz = false;
static char orig_tzbuf[TZBUF_LEN];

/*
 * Convenience subroutine for assigning the value of TZ
 */
static void
set_tz(const char *tz)
{
	strcpy(tzbuf, "TZ=");
	strncpy(tzbuf + 3, tz, sizeof(tzbuf) - 4);
	if (putenv(tzbuf) != 0)		/* shouldn't happen? */
261
		elog(LOG, "could not set TZ environment variable");
262 263 264 265 266 267 268 269 270 271 272 273 274 275
	tzset();
}

/*
 * Remove any value of TZ we have established
 *
 * Note: this leaves us with *no* value of TZ in the environment, and
 * is therefore only appropriate for reverting to that state, not for
 * reverting to a state where TZ was set to something else.
 */
static void
clear_tz(void)
{
	/*
Bruce Momjian's avatar
Bruce Momjian committed
276 277 278 279 280
	 * unsetenv() works fine, but is BSD, not POSIX, and is not available
	 * under Solaris, among others. Apparently putenv() called as below
	 * clears the process-specific environment variables.  Other
	 * reasonable arguments to putenv() (e.g. "TZ=", "TZ", "") result in a
	 * core dump (under Linux anyway). - thomas 1998-01-26
281 282 283 284 285
	 */
	if (tzbuf[0] == 'T')
	{
		strcpy(tzbuf, "=");
		if (putenv(tzbuf) != 0)
286
			elog(LOG, "could not clear TZ environment variable");
287 288 289 290 291 292 293 294 295 296 297 298 299
		tzset();
	}
}

/*
 * Check whether tzset() succeeded
 *
 * Unfortunately, tzset doesn't offer any well-defined way to detect that the
 * value of TZ was bad.  Often it will just select UTC (GMT) as the effective
 * timezone.  We use the following heuristics:
 *
 * If tzname[1] is a nonempty string, *or* the global timezone variable is
 * not zero, then tzset must have recognized the TZ value as something
Bruce Momjian's avatar
Bruce Momjian committed
300
 * different from UTC.	Return true.
301 302 303 304 305 306
 *
 * Otherwise, check to see if the TZ name is a known spelling of "UTC"
 * (ie, appears in our internal tables as a timezone equivalent to UTC).
 * If so, accept it.
 *
 * This will reject nonstandard spellings of UTC unless tzset() chose to
Bruce Momjian's avatar
Bruce Momjian committed
307
 * set tzname[1] as well as tzname[0].	The glibc version of tzset() will
308 309 310 311 312 313 314 315 316 317
 * do so, but on other systems we may be tightening the spec a little.
 *
 * Another problem is that on some platforms (eg HPUX), if tzset thinks the
 * input is bogus then it will adopt the system default timezone, which we
 * really can't tell is not the intended translation of the input.
 *
 * Still, it beats failing to detect bad TZ names at all, and a silent
 * failure mode of adopting the system-wide default is much better than
 * a silent failure mode of adopting UTC.
 *
318
 * NB: this must NOT ereport(ERROR).  The caller must get control back so that
319 320 321 322 323 324 325 326 327 328 329 330
 * it can restore the old value of TZ if we don't like the new one.
 */
static bool
tzset_succeeded(const char *tz)
{
	char		tztmp[TZBUF_LEN];
	char	   *cp;
	int			tzval;

	/*
	 * Check first set of heuristics to say that tzset definitely worked.
	 */
331
#ifdef HAVE_TZNAME
332 333
	if (tzname[1] && tzname[1][0] != '\0')
		return true;
334
#endif
335 336 337 338
	if (TIMEZONE_GLOBAL != 0)
		return true;

	/*
Bruce Momjian's avatar
Bruce Momjian committed
339 340
	 * Check for known spellings of "UTC".	Note we must downcase the
	 * input before passing it to DecodePosixTimezone().
341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358
	 */
	StrNCpy(tztmp, tz, sizeof(tztmp));
	for (cp = tztmp; *cp; cp++)
		*cp = tolower((unsigned char) *cp);
	if (DecodePosixTimezone(tztmp, &tzval) == 0)
		if (tzval == 0)
			return true;

	return false;
}

/*
 * Check whether timezone is acceptable.
 *
 * What we are doing here is checking for leap-second-aware timekeeping.
 * We need to reject such TZ settings because they'll wreak havoc with our
 * date/time arithmetic.
 *
359
 * NB: this must NOT ereport(ERROR).  The caller must get control back so that
360 361 362 363 364 365 366 367 368 369
 * it can restore the old value of TZ if we don't like the new one.
 */
static bool
tz_acceptable(void)
{
	struct tm	tt;
	time_t		time2000;

	/*
	 * To detect leap-second timekeeping, compute the time_t value for
Bruce Momjian's avatar
Bruce Momjian committed
370
	 * local midnight, 2000-01-01.	Insist that this be a multiple of 60;
371 372 373 374 375 376 377 378 379 380 381 382 383
	 * any partial-minute offset has to be due to leap seconds.
	 */
	MemSet(&tt, 0, sizeof(tt));
	tt.tm_year = 100;
	tt.tm_mon = 0;
	tt.tm_mday = 1;
	tt.tm_isdst = -1;
	time2000 = mktime(&tt);
	if ((time2000 % 60) != 0)
		return false;

	return true;
}
384

Bruce Momjian's avatar
Bruce Momjian committed
385
/*
386
 * assign_timezone: GUC assign_hook for timezone
387
 */
388 389
const char *
assign_timezone(const char *value, bool doit, bool interactive)
390
{
391 392 393
	char	   *result;
	char	   *endptr;
	double		hours;
394

395 396 397 398 399 400
	/*
	 * On first call, see if there is a TZ in the original environment.
	 * Save that value permanently.
	 */
	if (!have_saved_tz)
	{
Bruce Momjian's avatar
Bruce Momjian committed
401
		char	   *orig_tz = getenv("TZ");
402 403 404 405 406 407 408 409

		if (orig_tz)
			StrNCpy(orig_tzbuf, orig_tz, sizeof(orig_tzbuf));
		else
			orig_tzbuf[0] = '\0';
		have_saved_tz = true;
	}

410 411 412 413
	/*
	 * Check for INTERVAL 'foo'
	 */
	if (strncasecmp(value, "interval", 8) == 0)
414
	{
415 416 417 418 419 420 421 422 423 424 425 426 427
		const char *valueptr = value;
		char	   *val;
		Interval   *interval;

		valueptr += 8;
		while (isspace((unsigned char) *valueptr))
			valueptr++;
		if (*valueptr++ != '\'')
			return NULL;
		val = pstrdup(valueptr);
		/* Check and remove trailing quote */
		endptr = strchr(val, '\'');
		if (!endptr || endptr[1] != '\0')
428
		{
429 430 431 432
			pfree(val);
			return NULL;
		}
		*endptr = '\0';
Bruce Momjian's avatar
Bruce Momjian committed
433

434 435
		/*
		 * Try to parse it.  XXX an invalid interval format will result in
Bruce Momjian's avatar
Bruce Momjian committed
436 437 438
		 * ereport, which is not desirable for GUC.  We did what we could
		 * to guard against this in flatten_set_variable_args, but a
		 * string coming in from postgresql.conf might contain anything.
439 440
		 */
		interval = DatumGetIntervalP(DirectFunctionCall3(interval_in,
Bruce Momjian's avatar
Bruce Momjian committed
441 442 443
													CStringGetDatum(val),
											ObjectIdGetDatum(InvalidOid),
													 Int32GetDatum(-1)));
444 445 446 447
		pfree(val);
		if (interval->month != 0)
		{
			if (interactive)
448 449
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
450
						 errmsg("invalid interval value for time zone: month not allowed")));
451 452 453 454 455
			pfree(interval);
			return NULL;
		}
		if (doit)
		{
456
			/* Here we change from SQL to Unix sign convention */
Bruce Momjian's avatar
Bruce Momjian committed
457
			CTimeZone = -interval->time;
458 459 460 461 462 463 464 465 466 467 468 469 470
			HasCTZSet = true;
		}
		pfree(interval);
	}
	else
	{
		/*
		 * Try it as a numeric number of hours (possibly fractional).
		 */
		hours = strtod(value, &endptr);
		if (endptr != value && *endptr == '\0')
		{
			if (doit)
471
			{
472
				/* Here we change from SQL to Unix sign convention */
Bruce Momjian's avatar
Bruce Momjian committed
473
				CTimeZone = -hours * 3600;
474
				HasCTZSet = true;
475
			}
476 477 478
		}
		else if (strcasecmp(value, "UNKNOWN") == 0)
		{
479
			/*
Bruce Momjian's avatar
Bruce Momjian committed
480 481 482 483 484
			 * UNKNOWN is the value shown as the "default" for TimeZone in
			 * guc.c.  We interpret it as meaning the original TZ
			 * inherited from the environment.	Note that if there is an
			 * original TZ setting, we will return that rather than
			 * UNKNOWN as the canonical spelling.
485
			 */
486
			if (doit)
487
			{
Bruce Momjian's avatar
Bruce Momjian committed
488
				bool		ok;
489 490 491

				/* Revert to original setting of TZ, whatever it was */
				if (orig_tzbuf[0])
492
				{
493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508
					set_tz(orig_tzbuf);
					ok = tzset_succeeded(orig_tzbuf) && tz_acceptable();
				}
				else
				{
					clear_tz();
					ok = tz_acceptable();
				}

				if (ok)
					HasCTZSet = false;
				else
				{
					/* Bogus, so force UTC (equivalent to INTERVAL 0) */
					CTimeZone = 0;
					HasCTZSet = true;
509
				}
510 511 512 513
			}
		}
		else
		{
514 515 516
			/*
			 * Otherwise assume it is a timezone name.
			 *
517
			 * We have to actually apply the change before we can have any
Bruce Momjian's avatar
Bruce Momjian committed
518 519 520
			 * hope of checking it.  So, save the old value in case we
			 * have to back out.  Note that it's possible the old setting
			 * is in tzbuf, so we'd better copy it.
521
			 */
Bruce Momjian's avatar
Bruce Momjian committed
522 523 524 525
			char		save_tzbuf[TZBUF_LEN];
			char	   *save_tz;
			bool		known,
						acceptable;
526 527 528 529 530 531 532 533 534 535 536

			save_tz = getenv("TZ");
			if (save_tz)
				StrNCpy(save_tzbuf, save_tz, sizeof(save_tzbuf));

			set_tz(value);

			known = tzset_succeeded(value);
			acceptable = tz_acceptable();

			if (doit && known && acceptable)
537
			{
538
				/* Keep the changed TZ */
539
				HasCTZSet = false;
540
			}
541 542 543 544 545 546 547 548 549 550 551 552 553 554
			else
			{
				/*
				 * Revert to prior TZ setting; note we haven't changed
				 * HasCTZSet in this path, so if we were previously using
				 * a fixed offset, we still are.
				 */
				if (save_tz)
					set_tz(save_tzbuf);
				else
					clear_tz();
				/* Complain if it was bad */
				if (!known)
				{
555 556
					ereport(interactive ? ERROR : LOG,
							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
557
							 errmsg("unrecognized time zone name: \"%s\"",
558
									value)));
559 560 561 562
					return NULL;
				}
				if (!acceptable)
				{
563 564
					ereport(interactive ? ERROR : LOG,
							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
565
					errmsg("time zone \"%s\" appears to use leap seconds",
Bruce Momjian's avatar
Bruce Momjian committed
566
						   value),
567
							 errdetail("PostgreSQL does not support leap seconds.")));
568 569 570
					return NULL;
				}
			}
571
		}
572 573
	}

574 575 576 577 578 579 580
	/*
	 * If we aren't going to do the assignment, just return OK indicator.
	 */
	if (!doit)
		return value;

	/*
Bruce Momjian's avatar
Bruce Momjian committed
581
	 * Prepare the canonical string to return.	GUC wants it malloc'd.
582 583 584 585 586 587
	 */
	result = (char *) malloc(sizeof(tzbuf));
	if (!result)
		return NULL;

	if (HasCTZSet)
588 589
		snprintf(result, sizeof(tzbuf), "%.5f",
				 (double) (-CTimeZone) / 3600.0);
590 591 592 593 594 595 596
	else if (tzbuf[0] == 'T')
		strcpy(result, tzbuf + 3);
	else
		strcpy(result, "UNKNOWN");

	return result;
}
597

598 599 600 601
/*
 * show_timezone: GUC show_hook for timezone
 */
const char *
602
show_timezone(void)
603
{
604
	char	   *tzn;
605 606 607

	if (HasCTZSet)
	{
608
		Interval	interval;
609

610
		interval.month = 0;
Bruce Momjian's avatar
Bruce Momjian committed
611
		interval.time = -CTimeZone;
612

613
		tzn = DatumGetCString(DirectFunctionCall1(interval_out,
Bruce Momjian's avatar
Bruce Momjian committed
614
										  IntervalPGetDatum(&interval)));
615 616 617
	}
	else
		tzn = getenv("TZ");
618

619
	if (tzn != NULL)
620
		return tzn;
621

622 623
	return "unknown";
}
624

625

626
/*
627
 * SET TRANSACTION ISOLATION LEVEL
628
 */
629

630 631
const char *
assign_XactIsoLevel(const char *value, bool doit, bool interactive)
632
{
633
	if (doit && interactive && SerializableSnapshot != NULL)
634 635 636
		ereport(ERROR,
				(errcode(ERRCODE_ACTIVE_SQL_TRANSACTION),
				 errmsg("SET TRANSACTION ISOLATION LEVEL must be called before any query")));
637

638
	if (strcmp(value, "serializable") == 0)
Bruce Momjian's avatar
Bruce Momjian committed
639 640 641 642
	{
		if (doit)
			XactIsoLevel = XACT_SERIALIZABLE;
	}
643 644 645 646 647
	else if (strcmp(value, "repeatable read") == 0)
	{
		if (doit)
			XactIsoLevel = XACT_REPEATABLE_READ;
	}
648
	else if (strcmp(value, "read committed") == 0)
Bruce Momjian's avatar
Bruce Momjian committed
649 650 651 652
	{
		if (doit)
			XactIsoLevel = XACT_READ_COMMITTED;
	}
653 654 655 656 657
	else if (strcmp(value, "read uncommitted") == 0)
	{
		if (doit)
			XactIsoLevel = XACT_READ_UNCOMMITTED;
	}
658
	else if (strcmp(value, "default") == 0)
Bruce Momjian's avatar
Bruce Momjian committed
659 660 661 662
	{
		if (doit)
			XactIsoLevel = DefaultXactIsoLevel;
	}
663
	else
664
		return NULL;
665

666
	return value;
667 668
}

669
const char *
670
show_XactIsoLevel(void)
671
{
672 673 674 675 676 677 678 679 680 681 682 683 684
	switch (XactIsoLevel)
	{
		case XACT_READ_UNCOMMITTED:
			return "read uncommitted";
		case XACT_READ_COMMITTED:
			return "read committed";
		case XACT_REPEATABLE_READ:
			return "repeatable read";
		case XACT_SERIALIZABLE:
			return "serializable";
		default:
			return "bogus";
	}
685 686 687
}


688 689 690
/*
 * Random number seed
 */
691

692 693
bool
assign_random_seed(double value, bool doit, bool interactive)
694
{
695 696 697 698
	/* Can't really roll back on error, so ignore non-interactive setting */
	if (doit && interactive)
		DirectFunctionCall1(setseed, Float8GetDatum(value));
	return true;
699 700
}

701 702
const char *
show_random_seed(void)
703
{
704
	return "unavailable";
705 706 707
}


708
/*
709
 * encoding handling functions
710 711
 */

712 713
const char *
assign_client_encoding(const char *value, bool doit, bool interactive)
714
{
715
	int			encoding;
716

717 718
	encoding = pg_valid_client_encoding(value);
	if (encoding < 0)
719
		return NULL;
Bruce Momjian's avatar
Bruce Momjian committed
720 721

	/*
722 723
	 * Note: if we are in startup phase then SetClientEncoding may not be
	 * able to really set the encoding.  In this case we will assume that
Bruce Momjian's avatar
Bruce Momjian committed
724 725
	 * the encoding is okay, and InitializeClientEncoding() will fix
	 * things once initialization is complete.
726
	 */
727
	if (SetClientEncoding(encoding, doit) < 0)
728
	{
729
		if (interactive)
730 731
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
Bruce Momjian's avatar
Bruce Momjian committed
732 733
				  errmsg("conversion between %s and %s is not supported",
						 value, GetDatabaseEncodingName())));
734
		return NULL;
735
	}
736
	return value;
737 738 739
}


740 741 742
/*
 * SET SESSION AUTHORIZATION
 *
743 744 745
 * When resetting session auth after an error, we can't expect to do catalog
 * lookups.  Hence, the stored form of the value must provide a numeric userid
 * that can be re-used directly.  We store the string in the form of
746 747 748
 * NAMEDATALEN 'x's, followed by T or F to indicate superuserness, followed
 * by the numeric userid --- this cannot conflict with any valid user name,
 * because of the NAMEDATALEN limit on names.
749 750 751
 */
const char *
assign_session_authorization(const char *value, bool doit, bool interactive)
752
{
753
	AclId		usesysid = 0;
754
	bool		is_superuser = false;
755
	char	   *result;
756

757 758
	if (strspn(value, "x") == NAMEDATALEN &&
		(value[NAMEDATALEN] == 'T' || value[NAMEDATALEN] == 'F'))
759
	{
760 761 762
		/* might be a saved numeric userid */
		char	   *endptr;

763
		usesysid = (AclId) strtoul(value + NAMEDATALEN + 1, &endptr, 10);
764

765
		if (endptr != value + NAMEDATALEN + 1 && *endptr == '\0')
766
		{
767 768
			/* syntactically valid, so use the numeric user ID and flag */
			is_superuser = (value[NAMEDATALEN] == 'T');
769 770 771
		}
		else
			usesysid = 0;
772
	}
773 774

	if (usesysid == 0)
775
	{
776
		/* not a saved ID, so look it up */
777
		HeapTuple	userTup;
778

Bruce Momjian's avatar
Bruce Momjian committed
779
		if (!IsTransactionState())
780 781 782
		{
			/*
			 * Can't do catalog lookups, so fail.  The upshot of this is
Bruce Momjian's avatar
Bruce Momjian committed
783 784
			 * that session_authorization cannot be set in
			 * postgresql.conf, which seems like a good thing anyway.
785 786 787 788
			 */
			return NULL;
		}

789 790 791 792
		userTup = SearchSysCache(SHADOWNAME,
								 PointerGetDatum(value),
								 0, 0, 0);
		if (!HeapTupleIsValid(userTup))
793
		{
794
			if (interactive)
795 796 797
				ereport(ERROR,
						(errcode(ERRCODE_UNDEFINED_OBJECT),
						 errmsg("user \"%s\" does not exist", value)));
798
			return NULL;
799
		}
800

801
		usesysid = ((Form_pg_shadow) GETSTRUCT(userTup))->usesysid;
802
		is_superuser = ((Form_pg_shadow) GETSTRUCT(userTup))->usesuper;
Bruce Momjian's avatar
Bruce Momjian committed
803

804
		ReleaseSysCache(userTup);
805
	}
806

807
	if (doit)
808
		SetSessionAuthorization(usesysid, is_superuser);
809

810
	result = (char *) malloc(NAMEDATALEN + 32);
811 812 813
	if (!result)
		return NULL;

814 815
	memset(result, 'x', NAMEDATALEN);

816 817 818
	snprintf(result + NAMEDATALEN, 32, "%c%lu",
			 is_superuser ? 'T' : 'F',
			 (unsigned long) usesysid);
819 820

	return result;
821
}
822

823 824
const char *
show_session_authorization(void)
825
{
826 827 828 829
	/*
	 * We can't use the stored string; see comments for
	 * assign_session_authorization
	 */
Jan Wieck's avatar
Jan Wieck committed
830
	return GetUserNameFromId(GetSessionUserId());
831
}