diff options
Diffstat (limited to 'src/backend/access/transam/xlog.c')
-rw-r--r-- | src/backend/access/transam/xlog.c | 259 |
1 files changed, 199 insertions, 60 deletions
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 46aa03edf69..fd99732d213 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Header: /cvsroot/pgsql/src/backend/access/transam/xlog.c,v 1.33 2000/11/21 22:27:26 petere Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/transam/xlog.c,v 1.34 2000/11/25 20:33:50 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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" @@ -30,12 +33,11 @@ #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)); |