Store current LC_COLLATE and LC_CTYPE settings in pg_control during initdb;
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 25 Nov 2000 20:33:54 +0000 (20:33 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 25 Nov 2000 20:33:54 +0000 (20:33 +0000)
re-adopt these settings at every postmaster or standalone-backend startup.
This should fix problems with indexes becoming corrupt due to failure to
provide consistent locale environment for postmaster at all times.  Also,
refuse to start up a non-locale-enabled compilation in a database originally
initdb'd with a non-C locale.  Suppress LIKE index optimization if locale
is not "C" or "POSIX" (are there any other locales where it's safe?).
Issue NOTICE during initdb if selected locale disables LIKE optimization.

doc/src/sgml/runtime.sgml
src/backend/access/transam/xlog.c
src/backend/bootstrap/bootstrap.c
src/backend/optimizer/path/indxpath.c
src/backend/postmaster/postmaster.c
src/backend/tcop/postgres.c
src/backend/utils/adt/cash.c
src/backend/utils/adt/selfuncs.c
src/include/access/xlog.h
src/include/catalog/catversion.h
src/include/utils/builtins.h

index 0166c5a0d9f8a07ddf0494d8a1575da74cbae4d6..41e0f6f44b67e09181accc38006b7286ccbe1827 100644 (file)
@@ -79,9 +79,9 @@ $Header$
    <command>initdb</command> will attempt to create the directory you
    specify if it does not already exist. It is likely that it won't
    have the permission to do so (if you followed our advice and
-   created an unprivileged account). In that case you can create the
-   directory yourself (as root) and transfer ownership of it or grant
-   write access to it. Here is how this might work:
+   created an unprivileged account). In that case you should create the
+   directory yourself (as root) and transfer ownership of it to the
+   Postgres user account. Here is how this might work:
 <screen>
 root# <userinput>mkdir /usr/local/pgsql/data</userinput>
 root# <userinput>chown postgres /usr/local/pgsql/data</userinput>
@@ -101,6 +101,28 @@ postgres&gt; <userinput>initdb -D /usr/local/pgsql/data</userinput>
    access. <command>initdb</command> therefore revokes access
    permissions from everyone but the Postgres user account.
   </para>
+
+  <para>
+   One surprise you might encounter while running <command>initdb</command> is
+   a notice similar to this one:
+<screen>
+NOTICE:  Initializing database with en_US collation order.
+        This locale setting will prevent use of index optimization for
+        LIKE and regexp searches.  If you are concerned about speed of
+        such queries, you may wish to set LC_COLLATE to "C" and
+        re-initdb.  For more information see the Administrator's Guide.
+</screen>
+   This notice is intended to warn you that the currently selected locale
+   will cause indexes to be sorted in an order that prevents them from
+   being used for LIKE and regular-expression searches.  If you need
+   good performance of such searches, you should set your current locale
+   to "C" and re-run <command>initdb</command>.  On most systems, setting the
+   current locale is done by changing the value of the environment variable
+   <literal>LC_ALL</literal> or <literal>LANG</literal>.  The sort order used
+   within a particular database cluster is set by <command>initdb</command>
+   and cannot be changed later, short of dumping all data, re-initdb,
+   reload data.  So it's important to make this choice correctly now.
+  </para>
  </sect1>
 
  <sect1 id="postmaster-start">
index 6e6d44a1fa87fc3a59bf32102eb08952ec6d62d7..a6faa88edbaf15c479a047322d2d5b8538760b10 100644 (file)
@@ -20,6 +20,9 @@
 #include <sys/time.h>
 #include <sys/types.h>
 #include <dirent.h>
+#ifdef USE_LOCALE
+#include <locale.h>
+#endif
 
 #include "access/transam.h"
 #include "access/xact.h"
 #include "storage/s_lock.h"
 #include "access/xlog.h"
 #include "access/xlogutils.h"
+#include "utils/builtins.h"
 #include "utils/relcache.h"
 
 #include "miscadmin.h"
 
-char           XLogDir[MAXPGPATH];
-char           ControlFilePath[MAXPGPATH];
 int                    XLOGbuffers = 8;
 XLogRecPtr     MyLastRecPtr = {0, 0};
 bool           StopIfError = false;
@@ -50,6 +52,9 @@ SPINLOCK      ControlFileLockId;
 /* To generate new xid */
 SPINLOCK       XidGenLockId;
 
+static char            XLogDir[MAXPGPATH];
+static char            ControlFilePath[MAXPGPATH];
+
 #define MinXLOGbuffers 4
 
 typedef struct XLgwrRqst
@@ -107,6 +112,10 @@ typedef struct XLogCtlData
 
 static XLogCtlData *XLogCtl = NULL;
 
+/*
+ * Contents of pg_control
+ */
+
 typedef enum DBState
 {
        DB_STARTUP = 0,
@@ -116,31 +125,39 @@ typedef enum DBState
        DB_IN_PRODUCTION
 } DBState;
 
+#define LOCALE_NAME_BUFLEN  128
+
 typedef struct ControlFileData
 {
+       /*
+        * XLOG state
+        */
        uint32          logId;                  /* current log file id */
        uint32          logSeg;                 /* current log file segment (1-based) */
        XLogRecPtr      checkPoint;             /* last check point record ptr */
        time_t          time;                   /* time stamp of last modification */
-       DBState         state;                  /* */
+       DBState         state;                  /* see enum above */
 
        /*
         * this data is used to make sure that configuration of this DB is
-        * compatible with the current backend
+        * compatible with the backend executable
         */
        uint32          blcksz;                 /* block size for this DB */
        uint32          relseg_size;    /* blocks per segment of large relation */
        uint32          catalog_version_no;             /* internal version number */
-       char            archdir[MAXPGPATH];             /* where to move offline log files */
+       /* active locales --- "C" if compiled without USE_LOCALE: */
+       char            lc_collate[LOCALE_NAME_BUFLEN];
+       char            lc_ctype[LOCALE_NAME_BUFLEN];
 
        /*
-        * MORE DATA FOLLOWS AT THE END OF THIS STRUCTURE - locations of data
-        * dirs
+        * important directory locations
         */
+       char            archdir[MAXPGPATH];             /* where to move offline log files */
 } ControlFileData;
 
 static ControlFileData *ControlFile = NULL;
 
+
 typedef struct CheckPoint
 {
        XLogRecPtr              redo;           /* next RecPtr available when we */
@@ -204,6 +221,8 @@ static void XLogWrite(char *buffer);
 static int     XLogFileInit(uint32 log, uint32 seg, bool *usexistent);
 static int     XLogFileOpen(uint32 log, uint32 seg, bool econt);
 static XLogRecord *ReadRecord(XLogRecPtr *RecPtr, char *buffer);
+static void WriteControlFile(void);
+static void ReadControlFile(void);
 static char *str_time(time_t tnow);
 static void xlog_outrec(char *buf, XLogRecord *record);
 
@@ -1210,26 +1229,170 @@ next_record_is_invalid:;
        return (record);
 }
 
+/*
+ * I/O routines for pg_control
+ *
+ * *ControlFile is a buffer in shared memory that holds an image of the
+ * contents of pg_control.  WriteControlFile() initializes pg_control
+ * given a preloaded buffer, ReadControlFile() loads the buffer from
+ * the pg_control file (during postmaster or standalone-backend startup),
+ * and UpdateControlFile() rewrites pg_control after we modify xlog state.
+ *
+ * For simplicity, WriteControlFile() initializes the fields of pg_control
+ * that are related to checking backend/database compatibility, and
+ * ReadControlFile() verifies they are correct.  We could split out the
+ * I/O and compatibility-check functions, but there seems no need currently.
+ */
+
+void
+XLOGPathInit(void)
+{
+       /* Init XLOG file paths */
+       snprintf(XLogDir, MAXPGPATH, "%s/pg_xlog", DataDir);
+       snprintf(ControlFilePath, MAXPGPATH, "%s/global/pg_control", DataDir);
+}
+
+static void
+WriteControlFile(void)
+{
+       int                     fd;
+       char            buffer[BLCKSZ];
+#ifdef USE_LOCALE
+       char       *localeptr;
+#endif
+
+       /*
+        * Initialize compatibility-check fields
+        */
+       ControlFile->blcksz = BLCKSZ;
+       ControlFile->relseg_size = RELSEG_SIZE;
+       ControlFile->catalog_version_no = CATALOG_VERSION_NO;
+#ifdef USE_LOCALE
+       localeptr = setlocale(LC_COLLATE, NULL);
+       if (!localeptr)
+               elog(STOP, "Invalid LC_COLLATE setting");
+       StrNCpy(ControlFile->lc_collate, localeptr, LOCALE_NAME_BUFLEN);
+       localeptr = setlocale(LC_CTYPE, NULL);
+       if (!localeptr)
+               elog(STOP, "Invalid LC_CTYPE setting");
+       StrNCpy(ControlFile->lc_ctype, localeptr, LOCALE_NAME_BUFLEN);
+       /*
+        * Issue warning notice if initdb'ing in a locale that will not permit
+        * LIKE index optimization.  This is not a clean place to do it, but
+        * I don't see a better place either...
+        */
+       if (!locale_is_like_safe())
+               elog(NOTICE, "Initializing database with %s collation order."
+                        "\n\tThis locale setting will prevent use of index optimization for"
+                        "\n\tLIKE and regexp searches.  If you are concerned about speed of"
+                        "\n\tsuch queries, you may wish to set LC_COLLATE to \"C\" and"
+                        "\n\tre-initdb.  For more information see the Administrator's Guide.",
+                        ControlFile->lc_collate);
+#else
+       strcpy(ControlFile->lc_collate, "C");
+       strcpy(ControlFile->lc_ctype, "C");
+#endif
+
+       /*
+        * We write out BLCKSZ bytes into pg_control, zero-padding the
+        * excess over sizeof(ControlFileData).  This reduces the odds
+        * of premature-EOF errors when reading pg_control.  We'll still
+        * fail when we check the contents of the file, but hopefully with
+        * a more specific error than "couldn't read pg_control".
+        */
+       if (sizeof(ControlFileData) > BLCKSZ)
+               elog(STOP, "sizeof(ControlFileData) is too large ... fix xlog.c");
+       memset(buffer, 0, BLCKSZ);
+       memcpy(buffer, ControlFile, sizeof(ControlFileData));
+
+       fd = BasicOpenFile(ControlFilePath, O_RDWR | O_CREAT | O_EXCL | PG_BINARY, S_IRUSR | S_IWUSR);
+       if (fd < 0)
+               elog(STOP, "WriteControlFile failed to create control file (%s): %m",
+                        ControlFilePath);
+
+       if (write(fd, buffer, BLCKSZ) != BLCKSZ)
+               elog(STOP, "WriteControlFile failed to write control file: %m");
+
+       if (fsync(fd) != 0)
+               elog(STOP, "WriteControlFile failed to fsync control file: %m");
+
+       close(fd);
+}
+
+static void
+ReadControlFile(void)
+{
+       int                     fd;
+
+       /*
+        * Read data...
+        */
+       fd = BasicOpenFile(ControlFilePath, O_RDWR | PG_BINARY, S_IRUSR | S_IWUSR);
+       if (fd < 0)
+               elog(STOP, "open(\"%s\") failed: %m", ControlFilePath);
+
+       if (read(fd, ControlFile, sizeof(ControlFileData)) != sizeof(ControlFileData))
+               elog(STOP, "read(\"%s\") failed: %m", ControlFilePath);
+
+       close(fd);
+
+       /*
+        * Do compatibility checking immediately.  We do this here for 2 reasons:
+        *
+        * (1) if the database isn't compatible with the backend executable,
+        * we want to abort before we can possibly do any damage;
+        *
+        * (2) this code is executed in the postmaster, so the setlocale() will
+        * propagate to forked backends, which aren't going to read this file
+        * for themselves.  (These locale settings are considered critical
+        * compatibility items because they can affect sort order of indexes.)
+        */
+       if (ControlFile->blcksz != BLCKSZ)
+               elog(STOP, "database was initialized with BLCKSZ %d,\n\tbut the backend was compiled with BLCKSZ %d.\n\tlooks like you need to initdb.",
+                        ControlFile->blcksz, BLCKSZ);
+       if (ControlFile->relseg_size != RELSEG_SIZE)
+               elog(STOP, "database was initialized with RELSEG_SIZE %d,\n\tbut the backend was compiled with RELSEG_SIZE %d.\n\tlooks like you need to initdb.",
+                        ControlFile->relseg_size, RELSEG_SIZE);
+       if (ControlFile->catalog_version_no != CATALOG_VERSION_NO)
+               elog(STOP, "database was initialized with CATALOG_VERSION_NO %d,\n\tbut the backend was compiled with CATALOG_VERSION_NO %d.\n\tlooks like you need to initdb.",
+                        ControlFile->catalog_version_no, CATALOG_VERSION_NO);
+#ifdef USE_LOCALE
+       if (setlocale(LC_COLLATE, ControlFile->lc_collate) == NULL)
+               elog(STOP, "database was initialized with LC_COLLATE '%s',\n\twhich is not recognized by setlocale().\n\tlooks like you need to initdb.",
+                        ControlFile->lc_collate);
+       if (setlocale(LC_CTYPE, ControlFile->lc_ctype) == NULL)
+               elog(STOP, "database was initialized with LC_CTYPE '%s',\n\twhich is not recognized by setlocale().\n\tlooks like you need to initdb.",
+                        ControlFile->lc_ctype);
+#else
+       if (strcmp(ControlFile->lc_collate, "C") != 0 ||
+               strcmp(ControlFile->lc_ctype, "C") != 0)
+               elog(STOP, "database was initialized with LC_COLLATE '%s' and LC_CTYPE '%s',\n\tbut the backend was compiled without locale support.\n\tlooks like you need to initdb or recompile.",
+                        ControlFile->lc_collate, ControlFile->lc_ctype);
+#endif
+}
+
 void
-UpdateControlFile()
+UpdateControlFile(void)
 {
        int                     fd;
 
        fd = BasicOpenFile(ControlFilePath, O_RDWR | PG_BINARY, S_IRUSR | S_IWUSR);
        if (fd < 0)
-               elog(STOP, "open(cntlfile) failed: %m");
+               elog(STOP, "open(\"%s\") failed: %m", ControlFilePath);
 
-       if (write(fd, ControlFile, BLCKSZ) != BLCKSZ)
+       if (write(fd, ControlFile, sizeof(ControlFileData)) != sizeof(ControlFileData))
                elog(STOP, "write(cntlfile) failed: %m");
 
        if (fsync(fd) != 0)
                elog(STOP, "fsync(cntlfile) failed: %m");
 
        close(fd);
-
-       return;
 }
 
+/*
+ * Management of shared memory for XLOG
+ */
+
 int
 XLOGShmemSize(void)
 {
@@ -1237,7 +1400,8 @@ XLOGShmemSize(void)
                XLOGbuffers = MinXLOGbuffers;
 
        return (sizeof(XLogCtlData) + BLCKSZ * XLOGbuffers +
-                       sizeof(XLogRecPtr) * XLOGbuffers + BLCKSZ);
+                       sizeof(XLogRecPtr) * XLOGbuffers +
+                       sizeof(ControlFileData));
 }
 
 void
@@ -1245,16 +1409,25 @@ XLOGShmemInit(void)
 {
        bool            found;
 
+       /* this must agree with space requested by XLOGShmemSize() */
        if (XLOGbuffers < MinXLOGbuffers)
                XLOGbuffers = MinXLOGbuffers;
 
-       ControlFile = (ControlFileData *)
-               ShmemInitStruct("Control File", BLCKSZ, &found);
-       Assert(!found);
        XLogCtl = (XLogCtlData *)
                ShmemInitStruct("XLOG Ctl", sizeof(XLogCtlData) + BLCKSZ * XLOGbuffers +
                                                sizeof(XLogRecPtr) * XLOGbuffers, &found);
        Assert(!found);
+       ControlFile = (ControlFileData *)
+               ShmemInitStruct("Control File", sizeof(ControlFileData), &found);
+       Assert(!found);
+
+       /*
+        * If we are not in bootstrap mode, pg_control should already exist.
+        * Read and validate it immediately (see comments in ReadControlFile()
+        * for the reasons why).
+        */
+       if (!IsBootstrapProcessingMode())
+               ReadControlFile();
 }
 
 /*
@@ -1263,22 +1436,14 @@ XLOGShmemInit(void)
 void
 BootStrapXLOG()
 {
-       int                     fd;
-       char            buffer[BLCKSZ];
        CheckPoint      checkPoint;
-
 #ifdef XLOG
+       char            buffer[BLCKSZ];
        bool        usexistent = false;
        XLogPageHeader page = (XLogPageHeader) buffer;
        XLogRecord *record;
-
 #endif
 
-       fd = BasicOpenFile(ControlFilePath, O_RDWR | O_CREAT | O_EXCL | PG_BINARY, S_IRUSR | S_IWUSR);
-       if (fd < 0)
-               elog(STOP, "BootStrapXLOG failed to create control file (%s): %m",
-                        ControlFilePath);
-
        checkPoint.redo.xlogid = 0;
        checkPoint.redo.xrecoff = SizeOfXLogPHD;
        checkPoint.undo = checkPoint.redo;
@@ -1319,23 +1484,15 @@ BootStrapXLOG()
 
 #endif
 
-       memset(ControlFile, 0, BLCKSZ);
+       memset(ControlFile, 0, sizeof(ControlFileData));
        ControlFile->logId = 0;
        ControlFile->logSeg = 1;
        ControlFile->checkPoint = checkPoint.redo;
        ControlFile->time = time(NULL);
        ControlFile->state = DB_SHUTDOWNED;
-       ControlFile->blcksz = BLCKSZ;
-       ControlFile->relseg_size = RELSEG_SIZE;
-       ControlFile->catalog_version_no = CATALOG_VERSION_NO;
+       /* some additional ControlFile fields are set in WriteControlFile() */
 
-       if (write(fd, ControlFile, BLCKSZ) != BLCKSZ)
-               elog(STOP, "BootStrapXLOG failed to write control file: %m");
-
-       if (fsync(fd) != 0)
-               elog(STOP, "BootStrapXLOG failed to fsync control file: %m");
-
-       close(fd);
+       WriteControlFile();
 }
 
 static char *
@@ -1367,7 +1524,6 @@ StartupXLOG()
        bool            sie_saved = false;
 
 #endif
-       int                     fd;
 
        elog(LOG, "starting up");
 
@@ -1389,16 +1545,12 @@ StartupXLOG()
        S_INIT_LOCK(&(XLogCtl->chkp_lck));
 
        /*
-        * Open/read Control file
+        * Read control file and check XLOG status looks valid.
+        *
+        * Note: in most control paths, *ControlFile is already valid and we
+        * need not do ReadControlFile() here, but might as well do it to be sure.
         */
-       fd = BasicOpenFile(ControlFilePath, O_RDWR | PG_BINARY, S_IRUSR | S_IWUSR);
-       if (fd < 0)
-               elog(STOP, "open(\"%s\") failed: %m", ControlFilePath);
-
-       if (read(fd, ControlFile, BLCKSZ) != BLCKSZ)
-               elog(STOP, "read(\"%s\") failed: %m", ControlFilePath);
-
-       close(fd);
+       ReadControlFile();
 
        if (ControlFile->logSeg == 0 ||
                ControlFile->time <= 0 ||
@@ -1407,17 +1559,6 @@ StartupXLOG()
                !XRecOffIsValid(ControlFile->checkPoint.xrecoff))
                elog(STOP, "control file context is broken");
 
-       /* Check for incompatible database */
-       if (ControlFile->blcksz != BLCKSZ)
-               elog(STOP, "database was initialized with BLCKSZ %d,\n\tbut the backend was compiled with BLCKSZ %d.\n\tlooks like you need to initdb.",
-                        ControlFile->blcksz, BLCKSZ);
-       if (ControlFile->relseg_size != RELSEG_SIZE)
-               elog(STOP, "database was initialized with RELSEG_SIZE %d,\n\tbut the backend was compiled with RELSEG_SIZE %d.\n\tlooks like you need to initdb.",
-                        ControlFile->relseg_size, RELSEG_SIZE);
-       if (ControlFile->catalog_version_no != CATALOG_VERSION_NO)
-               elog(STOP, "database was initialized with CATALOG_VERSION_NO %d,\n\tbut the backend was compiled with CATALOG_VERSION_NO %d.\n\tlooks like you need to initdb.",
-                        ControlFile->catalog_version_no, CATALOG_VERSION_NO);
-
        if (ControlFile->state == DB_SHUTDOWNED)
                elog(LOG, "database system was shut down at %s",
                         str_time(ControlFile->time));
@@ -1425,12 +1566,10 @@ StartupXLOG()
                elog(LOG, "database system shutdown was interrupted at %s",
                         str_time(ControlFile->time));
        else if (ControlFile->state == DB_IN_RECOVERY)
-       {
                elog(LOG, "database system was interrupted being in recovery at %s\n"
                         "\tThis propably means that some data blocks are corrupted\n"
                         "\tand you will have to use last backup for recovery.",
                         str_time(ControlFile->time));
-       }
        else if (ControlFile->state == DB_IN_PRODUCTION)
                elog(LOG, "database system was interrupted at %s",
                         str_time(ControlFile->time));
index eb4ea3bdac4aafd08b70a5035c2c03bff76a95df..143b1f7f2ed49500be4e1971e268ffcf2ea6f322 100644 (file)
@@ -321,6 +321,8 @@ BootstrapMain(int argc, char *argv[])
                }
        }
 
+       XLOGPathInit();
+
        BaseInit();
 
        if (!IsUnderPostmaster)
@@ -333,8 +335,6 @@ BootstrapMain(int argc, char *argv[])
        /*
         * XLOG operations
         */
-       snprintf(XLogDir, MAXPGPATH, "%s/pg_xlog", DataDir);
-       snprintf(ControlFilePath, MAXPGPATH, "%s/global/pg_control", DataDir);
        SetProcessingMode(NormalProcessing);
        if (xlogop == BS_XLOG_NOP)
                StartupXLOG();
index 38a23394efe902ae4f47ae510667db37a59534c7..c7e8bd9d19d39c1c0ec14247efe94633f69d8da1 100644 (file)
@@ -1746,56 +1746,68 @@ match_special_index_operator(Expr *clause, Oid opclass, Oid relam,
                case OID_BPCHAR_LIKE_OP:
                case OID_VARCHAR_LIKE_OP:
                case OID_NAME_LIKE_OP:
-                       /* the right-hand const is type text for all of these */
-                       patt = DatumGetCString(DirectFunctionCall1(textout,
-                                                                                                          constvalue));
-                       isIndexable = pattern_fixed_prefix(patt, Pattern_Type_Like,
-                                                                                          &prefix, &rest) != Pattern_Prefix_None;
-                       if (prefix)
-                               pfree(prefix);
-                       pfree(patt);
+                       if (locale_is_like_safe())
+                       {
+                               /* the right-hand const is type text for all of these */
+                               patt = DatumGetCString(DirectFunctionCall1(textout,
+                                                                                                                  constvalue));
+                               isIndexable = pattern_fixed_prefix(patt, Pattern_Type_Like,
+                                                                                                  &prefix, &rest) != Pattern_Prefix_None;
+                               if (prefix)
+                                       pfree(prefix);
+                               pfree(patt);
+                       }
                        break;
 
                case OID_TEXT_ICLIKE_OP:
                case OID_BPCHAR_ICLIKE_OP:
                case OID_VARCHAR_ICLIKE_OP:
                case OID_NAME_ICLIKE_OP:
-                       /* the right-hand const is type text for all of these */
-                       patt = DatumGetCString(DirectFunctionCall1(textout,
-                                                                                                          constvalue));
-                       isIndexable = pattern_fixed_prefix(patt, Pattern_Type_Like_IC,
-                                                                                          &prefix, &rest) != Pattern_Prefix_None;
-                       if (prefix)
-                               pfree(prefix);
-                       pfree(patt);
+                       if (locale_is_like_safe())
+                       {
+                               /* the right-hand const is type text for all of these */
+                               patt = DatumGetCString(DirectFunctionCall1(textout,
+                                                                                                                  constvalue));
+                               isIndexable = pattern_fixed_prefix(patt, Pattern_Type_Like_IC,
+                                                                                                  &prefix, &rest) != Pattern_Prefix_None;
+                               if (prefix)
+                                       pfree(prefix);
+                               pfree(patt);
+                       }
                        break;
 
                case OID_TEXT_REGEXEQ_OP:
                case OID_BPCHAR_REGEXEQ_OP:
                case OID_VARCHAR_REGEXEQ_OP:
                case OID_NAME_REGEXEQ_OP:
-                       /* the right-hand const is type text for all of these */
-                       patt = DatumGetCString(DirectFunctionCall1(textout,
-                                                                                                          constvalue));
-                       isIndexable = pattern_fixed_prefix(patt, Pattern_Type_Regex,
-                                                                                          &prefix, &rest) != Pattern_Prefix_None;
-                       if (prefix)
-                               pfree(prefix);
-                       pfree(patt);
+                       if (locale_is_like_safe())
+                       {
+                               /* the right-hand const is type text for all of these */
+                               patt = DatumGetCString(DirectFunctionCall1(textout,
+                                                                                                                  constvalue));
+                               isIndexable = pattern_fixed_prefix(patt, Pattern_Type_Regex,
+                                                                                                  &prefix, &rest) != Pattern_Prefix_None;
+                               if (prefix)
+                                       pfree(prefix);
+                               pfree(patt);
+                       }
                        break;
 
                case OID_TEXT_ICREGEXEQ_OP:
                case OID_BPCHAR_ICREGEXEQ_OP:
                case OID_VARCHAR_ICREGEXEQ_OP:
                case OID_NAME_ICREGEXEQ_OP:
-                       /* the right-hand const is type text for all of these */
-                       patt = DatumGetCString(DirectFunctionCall1(textout,
-                                                                                                          constvalue));
-                       isIndexable = pattern_fixed_prefix(patt, Pattern_Type_Regex_IC,
-                                                                                          &prefix, &rest) != Pattern_Prefix_None;
-                       if (prefix)
-                               pfree(prefix);
-                       pfree(patt);
+                       if (locale_is_like_safe())
+                       {
+                               /* the right-hand const is type text for all of these */
+                               patt = DatumGetCString(DirectFunctionCall1(textout,
+                                                                                                                  constvalue));
+                               isIndexable = pattern_fixed_prefix(patt, Pattern_Type_Regex_IC,
+                                                                                                  &prefix, &rest) != Pattern_Prefix_None;
+                               if (prefix)
+                                       pfree(prefix);
+                               pfree(patt);
+                       }
                        break;
        }
 
@@ -2053,8 +2065,8 @@ prefix_quals(Var *leftop, Oid expr_op,
        result = makeList1(expr);
 
        /*
-        * If we can create a string larger than the prefix, say "x <
-        * greaterstr".
+        * If we can create a string larger than the prefix, we can say
+        * "x < greaterstr".
         */
        greaterstr = make_greater_string(prefix, datatype);
        if (greaterstr)
index fe47bece244f97f15a81842175cfca3bd9e551b9..1ed115a8c5c8d2b043f3e26e4860aa72af43106b 100644 (file)
@@ -617,13 +617,11 @@ PostmasterMain(int argc, char *argv[])
        }
 #endif
 
+       XLOGPathInit();
+
        /* set up shared memory and semaphores */
        reset_shared(PostPortNumber);
 
-       /* Init XLOG paths */
-       snprintf(XLogDir, MAXPGPATH, "%s/pg_xlog", DataDir);
-       snprintf(ControlFilePath, MAXPGPATH, "%s/global/pg_control", DataDir);
-
        /*
         * Initialize the list of active backends.      This list is only used for
         * garbage collecting the backend processes.
index 03fe246be36aea42abf535dc2f86a1044e05a08a..f04f05a6c90293870c5bb2768a46a27aaf498c31 100644 (file)
@@ -1525,9 +1525,8 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[], const cha
                 */
                on_proc_exit(UnlinkPidFile, 0);
 
+               XLOGPathInit();
                BaseInit();
-               snprintf(XLogDir, MAXPGPATH, "%s/pg_xlog", DataDir);
-               snprintf(ControlFilePath, MAXPGPATH, "%s/global/pg_control", DataDir);
                StartupXLOG();
        }
 
@@ -1636,7 +1635,7 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[], const cha
        if (!IsUnderPostmaster)
        {
                puts("\nPOSTGRES backend interactive interface ");
-               puts("$Revision: 1.190 $ $Date: 2000/11/25 19:05:42 $\n");
+               puts("$Revision: 1.191 $ $Date: 2000/11/25 20:33:52 $\n");
        }
 
        /*
index 3cdc7cb1d225180b829ec8b981beb21c457d97f9..70795f1193af3a26c7af549acc3031abe8277e80 100644 (file)
@@ -84,10 +84,6 @@ cash_in(PG_FUNCTION_ARGS)
                           *nsymbol;
 
 #ifdef USE_LOCALE
-#ifdef CASHDEBUG
-       setlocale(LC_ALL, "");
-       lconvert = localeconv();
-#endif
        if (lconvert == NULL)
                lconvert = localeconv();
 
index f83440b15d822d3d4e3b76372abe395c7a7641f4..2da05fdadfbf603200000b812d0189118d9e2bc4 100644 (file)
@@ -24,6 +24,9 @@
 
 #include <ctype.h>
 #include <math.h>
+#ifdef USE_LOCALE
+#include <locale.h>
+#endif
 
 #include "access/heapam.h"
 #include "catalog/catname.h"
@@ -1581,6 +1584,11 @@ pattern_fixed_prefix(char *patt, Pattern_Type ptype,
  *
  * A fixed prefix "foo" is estimated as the selectivity of the expression
  * "var >= 'foo' AND var < 'fop'" (see also indxqual.c).
+ *
+ * XXX Note: we make use of the upper bound to estimate operator selectivity
+ * even if the locale is such that we cannot rely on the upper-bound string.
+ * The selectivity only needs to be approximately right anyway, so it seems
+ * more useful to use the upper-bound code than not.
  */
 static Selectivity
 prefix_selectivity(char *prefix,
@@ -1862,6 +1870,44 @@ pattern_selectivity(char *patt, Pattern_Type ptype)
        return result;
 }
 
+/*
+ * Test whether the database's LOCALE setting is safe for LIKE/regexp index
+ * optimization.  The key requirement here is that given a prefix string,
+ * say "foo", we must be able to generate another string "fop" that is
+ * greater than all strings "foobar" starting with "foo".  Unfortunately,
+ * many non-C locales have bizarre collation rules in which "fop" > "foo"
+ * is not sufficient to ensure "fop" > "foobar".  Until we can come up
+ * with a more bulletproof way of generating the upper-bound string,
+ * disable the optimization in locales where it is not known to be safe.
+ */
+bool
+locale_is_like_safe(void)
+{
+#ifdef USE_LOCALE
+       /* Cache result so we only have to compute it once */
+       static int      result = -1;
+       char       *localeptr;
+
+       if (result >= 0)
+               return (bool) result;
+       localeptr = setlocale(LC_COLLATE, NULL);
+       if (!localeptr)
+               elog(STOP, "Invalid LC_COLLATE setting");
+       /*
+        * Currently we accept only "C" and "POSIX" (do any systems still
+        * return "POSIX"?).  Which other locales allow safe optimization?
+        */
+       if (strcmp(localeptr, "C") == 0)
+               result = true;
+       else if (strcmp(localeptr, "POSIX") == 0)
+               result = true;
+       else
+               result = false;
+       return (bool) result;
+#else /* not USE_LOCALE */
+       return true;                            /* We must be in C locale, which is OK */
+#endif /* USE_LOCALE */
+}
 
 /*
  * Try to generate a string greater than the given string or any string it is
@@ -1878,9 +1924,12 @@ pattern_selectivity(char *patt, Pattern_Type ptype)
  * This could be rather slow in the worst case, but in most cases we won't
  * have to try more than one or two strings before succeeding.
  *
- * XXX in a sufficiently weird locale, this might produce incorrect results?
- * For example, in German I believe "ss" is treated specially --- if we are
- * given "foos" and return "foot", will this actually be greater than "fooss"?
+ * XXX this is actually not sufficient, since it only copes with the case
+ * where individual characters collate in an order different from their
+ * numeric code assignments.  It does not handle cases where there are
+ * cross-character effects, such as specially sorted digraphs, multiple
+ * sort passes, etc.  For now, we just shut down the whole thing in locales
+ * that do such things :-(
  */
 char *
 make_greater_string(const char *str, Oid datatype)
index 793a6539b5d71a100970e81294bee5488908a147..05adfafd71ccf14c2660003e7287f70f591cad12 100644 (file)
@@ -107,13 +107,11 @@ extern void xlog_desc(char *buf, uint8 xl_info, char* rec);
 extern void UpdateControlFile(void);
 extern int XLOGShmemSize(void);
 extern void XLOGShmemInit(void);
+extern void XLOGPathInit(void);
 extern void BootStrapXLOG(void);
 extern void StartupXLOG(void);
 extern void ShutdownXLOG(void);
 extern void CreateCheckPoint(bool shutdown);
 extern void SetThisStartUpID(void);
 
-extern char XLogDir[];
-extern char ControlFilePath[];
-
 #endif  /* XLOG_H */
index 92824ee6a8c0355f62628fd5dbce30a14ab67c6d..9a47d1a060403dcf739cd203c4ddb2ebe7580479 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                             yyyymmddN */
-#define CATALOG_VERSION_NO     200011211
+#define CATALOG_VERSION_NO     200011251
 
 #endif
index 42f244bf578cc0c1357c3b2859eef667ef0cc733..70b21fe821e4f21792c66d4a293ef74c01776960 100644 (file)
@@ -376,6 +376,7 @@ extern Pattern_Prefix_Status pattern_fixed_prefix(char *patt,
                                                                                                  Pattern_Type ptype,
                                                                                                  char **prefix,
                                                                                                  char **rest);
+extern bool locale_is_like_safe(void);
 extern char *make_greater_string(const char *str, Oid datatype);
 
 /* tid.c */