]> granicus.if.org Git - postgresql/blob - src/backend/commands/variable.c
Support SET/SHOW/RESET client_encoding and server_encoding even when
[postgresql] / src / backend / commands / variable.c
1 /*-------------------------------------------------------------------------
2  *
3  * variable.c
4  *              Routines for handling of 'SET var TO',
5  *              'SHOW var' and 'RESET var' statements.
6  *
7  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
8  * Portions Copyright (c) 1994, Regents of the University of California
9  *
10  *
11  * IDENTIFICATION
12  *        $Header: /cvsroot/pgsql/src/backend/commands/variable.c,v 1.42 2000/10/25 19:44:44 tgl Exp $
13  *
14  *-------------------------------------------------------------------------
15  */
16
17 #include "postgres.h"
18
19 #include <ctype.h>
20 #include <time.h>
21
22 #include "access/xact.h"
23 #include "catalog/pg_shadow.h"
24 #include "commands/variable.h"
25 #include "miscadmin.h"
26 #include "optimizer/cost.h"
27 #include "optimizer/paths.h"
28 #include "parser/parse_expr.h"
29 #include "utils/builtins.h"
30 #include "utils/guc.h"
31 #include "utils/tqual.h"
32
33 #ifdef MULTIBYTE
34 #include "mb/pg_wchar.h"
35 #else
36 /* Grand unified hard-coded badness */
37 #define pg_encoding_to_char(x) "SQL_ASCII"
38 #define pg_get_client_encoding()  0
39 #endif
40
41
42 static bool show_date(void);
43 static bool reset_date(void);
44 static bool parse_date(char *);
45 static bool show_timezone(void);
46 static bool reset_timezone(void);
47 static bool parse_timezone(char *);
48
49 static bool show_DefaultXactIsoLevel(void);
50 static bool reset_DefaultXactIsoLevel(void);
51 static bool parse_DefaultXactIsoLevel(char *);
52 static bool show_XactIsoLevel(void);
53 static bool reset_XactIsoLevel(void);
54 static bool parse_XactIsoLevel(char *);
55 static bool parse_random_seed(char *);
56 static bool show_random_seed(void);
57 static bool reset_random_seed(void);
58
59 static bool show_client_encoding(void);
60 static bool reset_client_encoding(void);
61 static bool parse_client_encoding(char *);
62 static bool show_server_encoding(void);
63 static bool reset_server_encoding(void);
64 static bool parse_server_encoding(char *);
65
66
67 /*
68  * get_token
69  *              Obtain the next item in a comma-separated list of items,
70  *              where each item can be either "word" or "word=word".
71  *              The "word=word" form is only accepted if 'val' is not NULL.
72  *              Words are any sequences not containing whitespace, ',', or '='.
73  *              Whitespace can appear between the words and punctuation.
74  *
75  * 'tok': receives a pointer to first word of item, or NULL if none.
76  * 'val': if not NULL, receives a pointer to second word, or NULL if none.
77  * 'str': start of input string.
78  *
79  * Returns NULL if input string contained no more words, else pointer
80  * to just past this item, which can be used as 'str' for next call.
81  * (If this is the last item, returned pointer will point at a null char,
82  * so caller can alternatively check for that instead of calling again.)
83  *
84  * NB: input string is destructively modified by placing null characters
85  * at ends of words!
86  *
87  * A former version of this code avoided modifying the input string by
88  * returning palloc'd copies of the words.  However, we want to use this
89  * code early in backend startup to parse the PGDATESTYLE environment var,
90  * and palloc/pfree aren't initialized at that point.  Cleanest answer
91  * seems to be to palloc in SetPGVariable() so that we can treat the string
92  * as modifiable here.
93  */
94 static char *
95 get_token(char **tok, char **val, char *str)
96 {
97         char            ch;
98
99         *tok = NULL;
100         if (val != NULL)
101                 *val = NULL;
102
103         if (!str || *str == '\0')
104                 return NULL;
105
106         /* skip leading white space */
107         while (isspace((int) *str))
108                 str++;
109
110         /* end of string? then return NULL */
111         if (*str == '\0')
112                 return NULL;
113
114         if (*str == ',' || *str == '=')
115                 elog(ERROR, "Syntax error near \"%s\": empty setting", str);
116
117         /* OK, at beginning of non-empty item */
118         *tok = str;
119
120         /* Advance to end of word */
121         while (*str && !isspace((int) *str) && *str != ',' && *str != '=')
122                 str++;
123
124         /* Terminate word string for caller */
125         ch = *str;
126         *str = '\0';
127
128         /* Skip any whitespace */
129         while (isspace((int) ch))
130                 ch = *(++str);
131
132         /* end of string? */
133         if (ch == '\0')
134                 return str;
135         /* delimiter? */
136         if (ch == ',')
137                 return ++str;
138
139         /* Had better be '=', and caller must be expecting it */
140         if (val == NULL || ch != '=')
141                 elog(ERROR, "Syntax error near \"%s\"", str);
142
143         /* '=': get the value */
144         str++;
145
146         /* skip whitespace after '=' */
147         while (isspace((int) *str))
148                 str++;
149
150         if (*str == ',' || *str == '\0')
151                 elog(ERROR, "Syntax error near \"=%s\"", str);
152
153         /* OK, at beginning of non-empty value */
154         *val = str;
155
156         /* Advance to end of word */
157         while (*str && !isspace((int) *str) && *str != ',')
158                 str++;
159
160         /* Terminate word string for caller */
161         ch = *str;
162         *str = '\0';
163
164         /* Skip any whitespace */
165         while (isspace((int) ch))
166                 ch = *(++str);
167
168         /* end of string? */
169         if (ch == '\0')
170                 return str;
171         /* delimiter? */
172         if (ch == ',')
173                 return ++str;
174
175         elog(ERROR, "Syntax error near \"%s\"", str);
176
177         return str;
178 }
179
180
181 /*
182  * DATE_STYLE
183  *
184  * NOTE: set_default_datestyle() is called during backend startup to check
185  * if the PGDATESTYLE environment variable is set.      We want the env var
186  * to determine the value that "RESET DateStyle" will reset to!
187  */
188
189 /* These get initialized from the "master" values in init/globals.c */
190 static int      DefaultDateStyle;
191 static bool DefaultEuroDates;
192
193 static bool
194 parse_date(char *value)
195 {
196         char       *tok;
197         int                     dcnt = 0,
198                                 ecnt = 0;
199
200         if (value == NULL)
201         {
202                 reset_date();
203                 return TRUE;
204         }
205
206         while ((value = get_token(&tok, NULL, value)) != 0)
207         {
208                 /* Ugh. Somebody ought to write a table driven version -- mjl */
209
210                 if (!strcasecmp(tok, "ISO"))
211                 {
212                         DateStyle = USE_ISO_DATES;
213                         dcnt++;
214                 }
215                 else if (!strcasecmp(tok, "SQL"))
216                 {
217                         DateStyle = USE_SQL_DATES;
218                         dcnt++;
219                 }
220                 else if (!strcasecmp(tok, "POSTGRES"))
221                 {
222                         DateStyle = USE_POSTGRES_DATES;
223                         dcnt++;
224                 }
225                 else if (!strcasecmp(tok, "GERMAN"))
226                 {
227                         DateStyle = USE_GERMAN_DATES;
228                         dcnt++;
229                         EuroDates = TRUE;
230                         if ((ecnt > 0) && (!EuroDates))
231                                 ecnt++;
232                 }
233                 else if (!strncasecmp(tok, "EURO", 4))
234                 {
235                         EuroDates = TRUE;
236                         if ((dcnt <= 0) || (DateStyle != USE_GERMAN_DATES))
237                                 ecnt++;
238                 }
239                 else if ((!strcasecmp(tok, "US"))
240                                  || (!strncasecmp(tok, "NONEURO", 7)))
241                 {
242                         EuroDates = FALSE;
243                         if ((dcnt <= 0) || (DateStyle == USE_GERMAN_DATES))
244                                 ecnt++;
245                 }
246                 else if (!strcasecmp(tok, "DEFAULT"))
247                 {
248                         DateStyle = DefaultDateStyle;
249                         EuroDates = DefaultEuroDates;
250                         ecnt++;
251                 }
252                 else
253                         elog(ERROR, "Bad value for date style (%s)", tok);
254         }
255
256         if (dcnt > 1 || ecnt > 1)
257                 elog(NOTICE, "Conflicting settings for date");
258
259         return TRUE;
260 }
261
262 static bool
263 show_date(void)
264 {
265         char            buf[64];
266
267         strcpy(buf, "DateStyle is ");
268         switch (DateStyle)
269         {
270                 case USE_ISO_DATES:
271                         strcat(buf, "ISO");
272                         break;
273                 case USE_SQL_DATES:
274                         strcat(buf, "SQL");
275                         break;
276                 case USE_GERMAN_DATES:
277                         strcat(buf, "German");
278                         break;
279                 default:
280                         strcat(buf, "Postgres");
281                         break;
282         };
283         strcat(buf, " with ");
284         strcat(buf, ((EuroDates) ? "European" : "US (NonEuropean)"));
285         strcat(buf, " conventions");
286
287         elog(NOTICE, buf, NULL);
288
289         return TRUE;
290 }
291
292 static bool
293 reset_date(void)
294 {
295         DateStyle = DefaultDateStyle;
296         EuroDates = DefaultEuroDates;
297
298         return TRUE;
299 }
300
301 void
302 set_default_datestyle(void)
303 {
304         char       *DBDate;
305
306         /*
307          * Initialize from compile-time defaults in init/globals.c. NB: this
308          * is a necessary step; consider PGDATESTYLE="DEFAULT".
309          */
310         DefaultDateStyle = DateStyle;
311         DefaultEuroDates = EuroDates;
312
313         /* If the environment var is set, override compiled-in values */
314         DBDate = getenv("PGDATESTYLE");
315         if (DBDate == NULL)
316                 return;
317
318         /*
319          * Make a modifiable copy --- overwriting the env var doesn't seem
320          * like a good idea, even though we currently won't look at it again.
321          * Note that we cannot use palloc at this early stage of
322          * initialization.
323          */
324         DBDate = strdup(DBDate);
325
326         /* Parse desired setting into DateStyle/EuroDates */
327         parse_date(DBDate);
328
329         free(DBDate);
330
331         /* And make it the default for future RESETs */
332         DefaultDateStyle = DateStyle;
333         DefaultEuroDates = EuroDates;
334 }
335
336
337 /* Timezone support
338  * Working storage for strings is allocated with an arbitrary size of 64 bytes.
339  */
340
341 static char *defaultTZ = NULL;
342 static char TZvalue[64];
343 static char tzbuf[64];
344
345 /*
346  *
347  * TIMEZONE
348  *
349  */
350 /* parse_timezone()
351  * Handle SET TIME ZONE...
352  * Try to save existing TZ environment variable for later use in RESET TIME ZONE.
353  * - thomas 1997-11-10
354  */
355 static bool
356 parse_timezone(char *value)
357 {
358         char       *tok;
359
360         if (value == NULL)
361         {
362                 reset_timezone();
363                 return TRUE;
364         }
365
366         while ((value = get_token(&tok, NULL, value)) != 0)
367         {
368                 /* Not yet tried to save original value from environment? */
369                 if (defaultTZ == NULL)
370                 {
371                         /* found something? then save it for later */
372                         if ((defaultTZ = getenv("TZ")) != NULL)
373                                 strcpy(TZvalue, defaultTZ);
374
375                         /* found nothing so mark with an invalid pointer */
376                         else
377                                 defaultTZ = (char *) -1;
378                 }
379
380                 strcpy(tzbuf, "TZ=");
381                 strcat(tzbuf, tok);
382                 if (putenv(tzbuf) != 0)
383                         elog(ERROR, "Unable to set TZ environment variable to %s", tok);
384
385                 tzset();
386         }
387
388         return TRUE;
389 }       /* parse_timezone() */
390
391 static bool
392 show_timezone(void)
393 {
394         char       *tz;
395
396         tz = getenv("TZ");
397
398         elog(NOTICE, "Time zone is %s", ((tz != NULL) ? tz : "unknown"));
399
400         return TRUE;
401 }       /* show_timezone() */
402
403 /* reset_timezone()
404  * Set TZ environment variable to original value.
405  * Note that if TZ was originally not set, TZ should be cleared.
406  * unsetenv() works fine, but is BSD, not POSIX, and is not available
407  * under Solaris, among others. Apparently putenv() called as below
408  * clears the process-specific environment variables.
409  * Other reasonable arguments to putenv() (e.g. "TZ=", "TZ", "") result
410  * in a core dump (under Linux anyway).
411  * - thomas 1998-01-26
412  */
413 static bool
414 reset_timezone(void)
415 {
416         /* no time zone has been set in this session? */
417         if (defaultTZ == NULL)
418         {
419         }
420
421         /* time zone was set and original explicit time zone available? */
422         else if (defaultTZ != (char *) -1)
423         {
424                 strcpy(tzbuf, "TZ=");
425                 strcat(tzbuf, TZvalue);
426                 if (putenv(tzbuf) != 0)
427                         elog(ERROR, "Unable to set TZ environment variable to %s", TZvalue);
428                 tzset();
429         }
430
431         /*
432          * otherwise, time zone was set but no original explicit time zone
433          * available
434          */
435         else
436         {
437                 strcpy(tzbuf, "=");
438                 if (putenv(tzbuf) != 0)
439                         elog(ERROR, "Unable to clear TZ environment variable");
440                 tzset();
441         }
442
443         return TRUE;
444 }       /* reset_timezone() */
445
446
447
448 /* SET TRANSACTION */
449
450 static bool
451 parse_DefaultXactIsoLevel(char *value)
452 {
453 #if 0
454         TransactionState s = CurrentTransactionState;
455 #endif
456
457         if (value == NULL)
458         {
459                 reset_DefaultXactIsoLevel();
460                 return TRUE;
461         }
462
463 #if 0
464         if (s->state != TRANS_DEFAULT)
465         {
466                 elog(ERROR, "ALTER SESSION/SET TRANSACTION ISOLATION LEVEL"
467                          " can not be called within a transaction");
468                 return TRUE;
469         }
470 #endif
471
472         if (strcasecmp(value, "SERIALIZABLE") == 0)
473                 DefaultXactIsoLevel = XACT_SERIALIZABLE;
474         else if (strcasecmp(value, "COMMITTED") == 0)
475                 DefaultXactIsoLevel = XACT_READ_COMMITTED;
476         else
477                 elog(ERROR, "Bad TRANSACTION ISOLATION LEVEL (%s)", value);
478
479         return TRUE;
480 }
481
482 static bool
483 show_DefaultXactIsoLevel(void)
484 {
485
486         if (DefaultXactIsoLevel == XACT_SERIALIZABLE)
487                 elog(NOTICE, "Default TRANSACTION ISOLATION LEVEL is SERIALIZABLE");
488         else
489                 elog(NOTICE, "Default TRANSACTION ISOLATION LEVEL is READ COMMITTED");
490         return TRUE;
491 }
492
493 static bool
494 reset_DefaultXactIsoLevel(void)
495 {
496 #if 0
497         TransactionState s = CurrentTransactionState;
498
499         if (s->state != TRANS_DEFAULT)
500         {
501                 elog(ERROR, "ALTER SESSION/SET TRANSACTION ISOLATION LEVEL"
502                          " can not be called within a transaction");
503                 return TRUE;
504         }
505 #endif
506
507         DefaultXactIsoLevel = XACT_READ_COMMITTED;
508
509         return TRUE;
510 }
511
512 static bool
513 parse_XactIsoLevel(char *value)
514 {
515
516         if (value == NULL)
517         {
518                 reset_XactIsoLevel();
519                 return TRUE;
520         }
521
522         if (SerializableSnapshot != NULL)
523         {
524                 elog(ERROR, "SET TRANSACTION ISOLATION LEVEL must be called before any query");
525                 return TRUE;
526         }
527
528
529         if (strcasecmp(value, "SERIALIZABLE") == 0)
530                 XactIsoLevel = XACT_SERIALIZABLE;
531         else if (strcasecmp(value, "COMMITTED") == 0)
532                 XactIsoLevel = XACT_READ_COMMITTED;
533         else
534                 elog(ERROR, "Bad TRANSACTION ISOLATION LEVEL (%s)", value);
535
536         return TRUE;
537 }
538
539 static bool
540 show_XactIsoLevel(void)
541 {
542
543         if (XactIsoLevel == XACT_SERIALIZABLE)
544                 elog(NOTICE, "TRANSACTION ISOLATION LEVEL is SERIALIZABLE");
545         else
546                 elog(NOTICE, "TRANSACTION ISOLATION LEVEL is READ COMMITTED");
547         return TRUE;
548 }
549
550 static bool
551 reset_XactIsoLevel(void)
552 {
553
554         if (SerializableSnapshot != NULL)
555         {
556                 elog(ERROR, "SET TRANSACTION ISOLATION LEVEL must be called before any query");
557                 return TRUE;
558         }
559
560         XactIsoLevel = DefaultXactIsoLevel;
561
562         return TRUE;
563 }
564
565
566 /*
567  * Random number seed
568  */
569 static bool
570 parse_random_seed(char *value)
571 {
572         double          seed = 0;
573
574         if (value == NULL)
575                 reset_random_seed();
576         else
577         {
578                 sscanf(value, "%lf", &seed);
579                 DirectFunctionCall1(setseed, Float8GetDatum(seed));
580         }
581         return (TRUE);
582 }
583
584 static bool
585 show_random_seed(void)
586 {
587         elog(NOTICE, "Seed for random number generator is not known");
588         return (TRUE);
589 }
590
591 static bool
592 reset_random_seed(void)
593 {
594         double          seed = 0.5;
595
596         DirectFunctionCall1(setseed, Float8GetDatum(seed));
597         return (TRUE);
598 }
599
600
601 /*
602  * MULTIBYTE-related functions
603  *
604  * If MULTIBYTE support was not compiled, we still allow these variables
605  * to exist, but you can't set them to anything but "SQL_ASCII".  This
606  * minimizes interoperability problems between non-MB servers and MB-enabled
607  * clients.
608  */
609
610 static bool
611 parse_client_encoding(char *value)
612 {
613 #ifdef MULTIBYTE
614         int                     encoding;
615
616         encoding = pg_valid_client_encoding(value);
617         if (encoding < 0)
618         {
619                 if (value)
620                         elog(ERROR, "Client encoding %s is not supported", value);
621                 else
622                         elog(ERROR, "No client encoding is specified");
623         }
624         else
625         {
626                 if (pg_set_client_encoding(encoding))
627                 {
628                         elog(ERROR, "Conversion between %s and %s is not supported",
629                                  value, pg_encoding_to_char(GetDatabaseEncoding()));
630                 }
631         }
632 #else
633         if (value &&
634                 strcasecmp(value, pg_encoding_to_char(pg_get_client_encoding())) != 0)
635                 elog(ERROR, "Client encoding %s is not supported", value);
636 #endif
637         return TRUE;
638 }
639
640 static bool
641 show_client_encoding(void)
642 {
643         elog(NOTICE, "Current client encoding is %s",
644                  pg_encoding_to_char(pg_get_client_encoding()));
645         return TRUE;
646 }
647
648 static bool
649 reset_client_encoding(void)
650 {
651 #ifdef MULTIBYTE
652         int                     encoding;
653         char       *env = getenv("PGCLIENTENCODING");
654
655         if (env)
656         {
657                 encoding = pg_char_to_encoding(env);
658                 if (encoding < 0)
659                         encoding = GetDatabaseEncoding();
660         }
661         else
662                 encoding = GetDatabaseEncoding();
663         pg_set_client_encoding(encoding);
664 #endif
665         return TRUE;
666 }
667
668 static bool
669 parse_server_encoding(char *value)
670 {
671         elog(NOTICE, "SET SERVER_ENCODING is not supported");
672         return TRUE;
673 }
674
675 static bool
676 show_server_encoding(void)
677 {
678         elog(NOTICE, "Current server encoding is %s",
679                  pg_encoding_to_char(GetDatabaseEncoding()));
680         return TRUE;
681 }
682
683 static bool
684 reset_server_encoding(void)
685 {
686         elog(NOTICE, "RESET SERVER_ENCODING is not supported");
687         return TRUE;
688 }
689
690
691
692 void
693 SetPGVariable(const char *name, const char *value)
694 {
695         char       *mvalue = value ? pstrdup(value) : ((char*) NULL);
696
697     /*
698      * Special cases ought to be removed and handled separately
699      * by TCOP
700      */
701     if (strcasecmp(name, "datestyle")==0)
702         parse_date(mvalue);
703     else if (strcasecmp(name, "timezone")==0)
704         parse_timezone(mvalue);
705     else if (strcasecmp(name, "DefaultXactIsoLevel")==0)
706         parse_DefaultXactIsoLevel(mvalue);
707     else if (strcasecmp(name, "XactIsoLevel")==0)
708         parse_XactIsoLevel(mvalue);
709     else if (strcasecmp(name, "client_encoding")==0)
710         parse_client_encoding(mvalue);
711     else if (strcasecmp(name, "server_encoding")==0)
712         parse_server_encoding(mvalue);
713     else if (strcasecmp(name, "random_seed")==0)
714         parse_random_seed(mvalue);
715     else
716         SetConfigOption(name, value, superuser() ? PGC_SUSET : PGC_USERSET);
717
718         if (mvalue)
719                 pfree(mvalue);
720 }
721
722
723 void
724 GetPGVariable(const char *name)
725 {
726     if (strcasecmp(name, "datestyle")==0)
727         show_date();
728     else if (strcasecmp(name, "timezone")==0)
729         show_timezone();
730     else if (strcasecmp(name, "DefaultXactIsoLevel")==0)
731         show_DefaultXactIsoLevel();
732     else if (strcasecmp(name, "XactIsoLevel")==0)
733         show_XactIsoLevel();
734     else if (strcasecmp(name, "client_encoding")==0)
735         show_client_encoding();
736     else if (strcasecmp(name, "server_encoding")==0)
737         show_server_encoding();
738     else if (strcasecmp(name, "random_seed")==0)
739         show_random_seed();
740     else
741     {
742         const char * val = GetConfigOption(name);
743         elog(NOTICE, "%s is %s", name, val);
744     }
745
746
747 void
748 ResetPGVariable(const char *name)
749 {
750     if (strcasecmp(name, "datestyle")==0)
751         reset_date();
752     else if (strcasecmp(name, "timezone")==0)
753         reset_timezone();
754     else if (strcasecmp(name, "DefaultXactIsoLevel")==0)
755                         reset_DefaultXactIsoLevel();
756     else if (strcasecmp(name, "XactIsoLevel")==0)
757                         reset_XactIsoLevel();
758     else if (strcasecmp(name, "client_encoding")==0)
759         reset_client_encoding();
760     else if (strcasecmp(name, "server_encoding")==0)
761         reset_server_encoding();
762     else if (strcasecmp(name, "random_seed")==0)
763         reset_random_seed();
764     else
765         SetConfigOption(name, NULL, superuser() ? PGC_SUSET : PGC_USERSET);
766 }