From: Vadim B. Mikheev Date: Wed, 6 Oct 1999 21:58:18 +0000 (+0000) Subject: XLOG (also known as WAL -:)) Bootstrap/Startup/Shutdown. X-Git-Tag: REL7_0~1360 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=47937403676d913c0e740eec6b85113865c6c8ab;p=postgresql XLOG (also known as WAL -:)) Bootstrap/Startup/Shutdown. First step in cleaning up backend initialization code. Fix for FATAL: now FATAL is ERROR + exit. --- diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 2a7d60d3aa..09605cf947 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -14,12 +14,14 @@ void UpdateControlFile(void); int XLOGShmemSize(void); +void XLOGShmemInit(void); void BootStrapXLOG(void); void StartupXLOG(void); +void ShutdownXLOG(void); void CreateCheckPoint(bool shutdown); -char *XLogDir = NULL; -char *ControlFilePath = NULL; +char XLogDir[MAXPGPATH+1]; +char ControlFilePath[MAXPGPATH+1]; uint32 XLOGbuffers = 0; XLogRecPtr MyLastRecPtr = {0, 0}; bool StopIfError = false; @@ -81,7 +83,8 @@ static XLogCtlData *XLogCtl = NULL; typedef enum DBState { - DB_SHUTDOWNED = 1, + DB_STARTUP = 0, + DB_SHUTDOWNED, DB_SHUTDOWNING, DB_IN_RECOVERY, DB_IN_PRODUCTION @@ -114,9 +117,9 @@ typedef struct CheckPoint } CheckPoint; /* - * We break each log file in 64Mb segments + * We break each log file in 16Mb segments */ -#define XLogSegSize (64*1024*1024) +#define XLogSegSize (16*1024*1024) #define XLogLastSeg (0xffffffff / XLogSegSize) #define XLogFileSize (XLogLastSeg * XLogSegSize) @@ -166,6 +169,7 @@ static void XLogWrite(char *buffer); static int XLogFileInit(uint32 log, uint32 seg); static int XLogFileOpen(uint32 log, uint32 seg, bool econt); static XLogRecord *ReadRecord(XLogRecPtr *RecPtr, char *buffer); +static char *str_time(time_t tnow); static XLgwrResult LgwrResult = {{0, 0}, {0, 0}}; static XLgwrRqst LgwrRqst = {{0, 0}, {0, 0}}; @@ -173,14 +177,14 @@ static XLgwrRqst LgwrRqst = {{0, 0}, {0, 0}}; static int logFile = -1; static uint32 logId = 0; static uint32 logSeg = 0; -static off_t logOff = 0; +static uint32 logOff = 0; static XLogRecPtr ReadRecPtr; static XLogRecPtr EndRecPtr; static int readFile = -1; static uint32 readId = 0; static uint32 readSeg = 0; -static off_t readOff = (off_t) -1; +static uint32 readOff = 0; static char readBuf[BLCKSZ]; static XLogRecord *nextRecord = NULL; @@ -262,7 +266,13 @@ XLogInsert(RmgrId rmid, char *hdr, uint32 hdrlen, char *buf, uint32 buflen) freespace -= SizeOfXLogRecord; record = (XLogRecord*) Insert->currpos; record->xl_prev = Insert->PrevRecord; - record->xl_xact_prev = MyLastRecPtr; + if (rmid != RM_XLOG_ID) + record->xl_xact_prev = MyLastRecPtr; + else + { + record->xl_xact_prev.xlogid = 0; + record->xl_xact_prev.xrecoff = 0; + } record->xl_xid = GetCurrentTransactionId(); record->xl_len = (len > freespace) ? freespace : len; record->xl_info = (len > freespace) ? XLR_TO_BE_CONTINUED : 0; @@ -271,7 +281,7 @@ XLogInsert(RmgrId rmid, char *hdr, uint32 hdrlen, char *buf, uint32 buflen) RecPtr.xrecoff = XLogCtl->xlblocks[curridx].xrecoff - BLCKSZ + Insert->currpos - ((char*) Insert->currpage); - if (MyLastRecPtr.xrecoff == 0) + if (MyLastRecPtr.xrecoff == 0 && rmid != RM_XLOG_ID) { SpinAcquire(SInvalLock); MyProc->logRec = RecPtr; @@ -489,7 +499,7 @@ XLogFlush(XLogRecPtr record) { logId = LgwrResult.Write.xlogid; logSeg = (LgwrResult.Write.xrecoff - 1) / XLogSegSize; - logOff = (off_t) 0; + logOff = 0; logFile = XLogFileOpen(logId, logSeg, false); } @@ -612,7 +622,7 @@ XLogWrite(char *buffer) } logId = LgwrResult.Write.xlogid; logSeg = (LgwrResult.Write.xrecoff - 1) / XLogSegSize; - logOff = (off_t) 0; + logOff = 0; logFile = XLogFileInit(logId, logSeg); SpinAcquire(ControlFileLockId); ControlFile->logId = logId; @@ -626,14 +636,14 @@ XLogWrite(char *buffer) { logId = LgwrResult.Write.xlogid; logSeg = (LgwrResult.Write.xrecoff - 1) / XLogSegSize; - logOff = (off_t) 0; + logOff = 0; logFile = XLogFileOpen(logId, logSeg, false); } if (logOff != (LgwrResult.Write.xrecoff - BLCKSZ) % XLogSegSize) { logOff = (LgwrResult.Write.xrecoff - BLCKSZ) % XLogSegSize; - if (lseek(logFile, logOff, SEEK_SET) < 0) + if (lseek(logFile, (off_t)logOff, SEEK_SET) < 0) elog(STOP, "Lseek(logfile %u seg %u off %u) failed: %d", logId, logSeg, logOff, errno); } @@ -717,6 +727,10 @@ tryAgain: elog(STOP, "Fsync(logfile %u seg %u) failed: %d", logId, logSeg, errno); + if (lseek(fd, 0, SEEK_SET) < 0) + elog(STOP, "Lseek(logfile %u seg %u off %u) failed: %d", + log, seg, 0, errno); + return(fd); } @@ -753,376 +767,56 @@ tryAgain: return(fd); } -void -UpdateControlFile() -{ - int fd; - -tryAgain: - fd = open(ControlFilePath, O_RDWR); - if (fd < 0 && (errno == EMFILE || errno == ENFILE)) - { - fd = errno; - if (!ReleaseDataFile()) - elog(STOP, "Open(cntlfile) failed: %d (and no one data file can be closed)", - fd); - goto tryAgain; - } - if (fd < 0) - elog(STOP, "Open(cntlfile) failed: %d", errno); - - if (write(fd, ControlFile, BLCKSZ) != BLCKSZ) - elog(STOP, "Write(cntlfile) failed: %d", errno); - - if (fsync(fd) != 0) - elog(STOP, "Fsync(cntlfile) failed: %d", errno); - - close(fd); - - return; -} - -int -XLOGShmemSize() -{ - if (XLOGbuffers < MinXLOGbuffers) - XLOGbuffers = MinXLOGbuffers; - - return(sizeof(XLogCtlData) + BLCKSZ * XLOGbuffers + - sizeof(XLogRecPtr) * XLOGbuffers + BLCKSZ); -} - -/* - * This func must be called ONCE on system install - */ -void -BootStrapXLOG() +static XLogRecord* +ReadRecord(XLogRecPtr *RecPtr, char *buffer) { - int fd; - char buffer[BLCKSZ]; - XLogPageHeader page = (XLogPageHeader)buffer; - CheckPoint checkPoint; XLogRecord *record; + XLogRecPtr tmpRecPtr = EndRecPtr; + bool nextmode = (RecPtr == NULL); + int emode = (nextmode) ? LOG : STOP; + bool noBlck = false; - fd = open(ControlFilePath, O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR); - if (fd < 0) - elog(STOP, "BootStrapXLOG failed to create control file: %d", errno); - - logFile = XLogFileInit(0, 0); - - checkPoint.redo.xlogid = 0; - checkPoint.redo.xrecoff = SizeOfXLogPHD; - checkPoint.undo = checkPoint.redo; - checkPoint.nextXid = FirstTransactionId; - checkPoint.nextOid = BootstrapObjectIdData; - - memset(buffer, 0, BLCKSZ); - page->xlp_magic = XLOG_PAGE_MAGIC; - page->xlp_info = 0; - record = (XLogRecord*) ((char*)page + SizeOfXLogPHD); - record->xl_prev.xlogid = 0; record->xl_prev.xrecoff = 0; - record->xl_xact_prev = record->xl_prev; - record->xl_xid = InvalidTransactionId; - record->xl_len = sizeof(checkPoint); - record->xl_info = 0; - record->xl_rmid = RM_XLOG_ID; - memcpy((char*)record + SizeOfXLogRecord, &checkPoint, sizeof(checkPoint)); - - if (write(logFile, buffer, BLCKSZ) != BLCKSZ) - elog(STOP, "BootStrapXLOG failed to write logfile: %d", errno); - - if (fsync(logFile) != 0) - elog(STOP, "BootStrapXLOG failed to fsync logfile: %d", errno); - - close(logFile); - logFile = -1; - - memset(buffer, 0, BLCKSZ); - ControlFile = (ControlFileData*) buffer; - ControlFile->logId = 0; - ControlFile->logSeg = 1; - ControlFile->checkPoint = checkPoint.redo; - ControlFile->time = time(NULL); - ControlFile->state = DB_SHUTDOWNED; - - if (write(fd, buffer, BLCKSZ) != BLCKSZ) - elog(STOP, "BootStrapXLOG failed to write control file: %d", errno); - - if (fsync(fd) != 0) - elog(STOP, "BootStrapXLOG failed to fsync control file: %d", errno); - - close(fd); - - return; - -} - -/* - * This func must be called ONCE on system startup - */ -void -StartupXLOG() -{ - XLogCtlInsert *Insert = &XLogCtl->Insert; - CheckPoint checkPoint; - XLogRecPtr RecPtr, - LastRec; - XLogRecord *record; - char buffer[MAXLOGRECSZ+SizeOfXLogRecord]; - int fd; - bool found; - bool recovery = false; - bool sie_saved = false; - - elog(LOG, "Starting up XLOG manager..."); - - 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); - - XLogCtl->xlblocks = (XLogRecPtr*) (((char *)XLogCtl) + sizeof(XLogCtlData)); - XLogCtl->pages = ((char *)XLogCtl->xlblocks + sizeof(XLogRecPtr) * XLOGbuffers); - XLogCtl->XLogCacheByte = BLCKSZ * XLOGbuffers; - XLogCtl->XLogCacheBlck = XLOGbuffers - 1; - memset(XLogCtl->xlblocks, 0, sizeof(XLogRecPtr) * XLOGbuffers); - XLogCtl->LgwrRqst = LgwrRqst; - XLogCtl->LgwrResult = LgwrResult; - XLogCtl->Insert.LgwrResult = LgwrResult; - XLogCtl->Insert.curridx = 0; - XLogCtl->Insert.currpage = (XLogPageHeader) (XLogCtl->pages); - XLogCtl->Write.LgwrResult = LgwrResult; - XLogCtl->Write.curridx = 0; - - /* - * Open/read Control file - */ -tryAgain: - fd = open(ControlFilePath, O_RDWR); - if (fd < 0 && (errno == EMFILE || errno == ENFILE)) + if (nextmode) { - fd = errno; - if (!ReleaseDataFile()) - elog(STOP, "Open(cntlfile) failed: %d (and no one data file can be closed)", - fd); - goto tryAgain; + RecPtr = &tmpRecPtr; + if (nextRecord != NULL) + { + record = nextRecord; + goto got_record; + } + if (tmpRecPtr.xrecoff % BLCKSZ != 0) + tmpRecPtr.xrecoff += (BLCKSZ - tmpRecPtr.xrecoff % BLCKSZ); + if (tmpRecPtr.xrecoff >= XLogFileSize) + { + (tmpRecPtr.xlogid)++; + tmpRecPtr.xrecoff = 0; + } + tmpRecPtr.xrecoff += SizeOfXLogPHD; } - if (fd < 0) - elog(STOP, "Open(cntlfile) failed: %d", errno); - - if (read(fd, ControlFile, BLCKSZ) != BLCKSZ) - elog(STOP, "Read(cntlfile) failed: %d", errno); - - close(fd); - - if (ControlFile->logSeg == 0 || - ControlFile->time <= 0 || - ControlFile->state < DB_SHUTDOWNED || - ControlFile->state > DB_IN_PRODUCTION || - ControlFile->checkPoint.xlogid == 0 || - ControlFile->checkPoint.xrecoff == 0) - elog(STOP, "Control file context is broken"); + else if (!XRecOffIsValid(RecPtr->xrecoff)) + elog(STOP, "ReadRecord: invalid record offset in (%u, %u)", + RecPtr->xlogid, RecPtr->xrecoff); - if (ControlFile->state == DB_SHUTDOWNED) - elog(LOG, "Data Base System was properly shutdowned at %s", - ctime(&(ControlFile->time))); - else if (ControlFile->state == DB_SHUTDOWNING) - elog(LOG, "Data Base System was interrupted while shutting down at %s", - ctime(&(ControlFile->time))); - else if (ControlFile->state == DB_IN_RECOVERY) + if (readFile >= 0 && (RecPtr->xlogid != readId || + RecPtr->xrecoff / XLogSegSize != readSeg)) { - elog(LOG, "Data Base System was interrupted being in recovery at %s\n" - "This propably means that some data blocks are corrupted\n" - "And you will have to use last backup for recovery", - ctime(&(ControlFile->time))); + close(readFile); + readFile = -1; } - else if (ControlFile->state == DB_IN_PRODUCTION) - elog(LOG, "Data Base System was interrupted being in production at %s", - ctime(&(ControlFile->time))); - - LastRec = RecPtr = ControlFile->checkPoint; - if (!XRecOffIsValid(RecPtr.xrecoff)) - elog(STOP, "Invalid checkPoint in control file"); - elog(LOG, "CheckPoint record at (%u, %u)", RecPtr.xlogid, RecPtr.xrecoff); - - record = ReadRecord(&RecPtr, buffer); - if (record->xl_rmid != RM_XLOG_ID) - elog(STOP, "Invalid RMID in checkPoint record"); - if (record->xl_len != sizeof(checkPoint)) - elog(STOP, "Invalid length of checkPoint record"); - checkPoint = *((CheckPoint*)((char*)record + SizeOfXLogRecord)); - - elog(LOG, "Redo record at (%u, %u); Undo record at (%u, %u)", - checkPoint.redo.xlogid, checkPoint.redo.xrecoff, - checkPoint.undo.xlogid, checkPoint.undo.xrecoff); - elog(LOG, "NextTransactionId: %u; NextOid: %u)", - checkPoint.nextXid, checkPoint.nextOid); - if (checkPoint.nextXid < FirstTransactionId || - checkPoint.nextOid < BootstrapObjectIdData) - elog(LOG, "Invalid NextTransactionId/NextOid"); - - ShmemVariableCache->nextXid = checkPoint.nextXid; - ShmemVariableCache->nextOid = checkPoint.nextOid; - - if (XLByteLT(RecPtr, checkPoint.redo)) - elog(STOP, "Invalid redo in checkPoint record"); - if (checkPoint.undo.xrecoff == 0) - checkPoint.undo = RecPtr; - if (XLByteLT(RecPtr, checkPoint.undo)) - elog(STOP, "Invalid undo in checkPoint record"); - - if (XLByteLT(checkPoint.undo, RecPtr) || XLByteLT(checkPoint.redo, RecPtr)) + readId = RecPtr->xlogid; + readSeg = RecPtr->xrecoff / XLogSegSize; + if (readFile < 0) { - if (ControlFile->state == DB_SHUTDOWNED) - elog(STOP, "Invalid Redo/Undo record in Shutdowned state"); - recovery = true; + noBlck = true; + readFile = XLogFileOpen(readId, readSeg, nextmode); + if (readFile < 0) + goto next_record_is_invalid; } - else if (ControlFile->state != DB_SHUTDOWNED) - recovery = true; - if (recovery) - { - elog(LOG, "The DataBase system was not properly shutdowned\n" - "Automatic recovery is in progress..."); - ControlFile->state = DB_IN_RECOVERY; - ControlFile->time = time(NULL); - UpdateControlFile(); - - sie_saved = StopIfError; - StopIfError = true; - - /* Is REDO required ? */ - if (XLByteLT(checkPoint.redo, RecPtr)) - record = ReadRecord(&(checkPoint.redo), buffer); - else /* read past CheckPoint record */ - record = ReadRecord(NULL, buffer); - - /* REDO */ - if (record->xl_len != 0) - { - elog(LOG, "Redo starts at (%u, %u)", - ReadRecPtr.xlogid, ReadRecPtr.xrecoff); - do - { - if (record->xl_xid >= ShmemVariableCache->nextXid) - ShmemVariableCache->nextXid = record->xl_xid + 1; - RmgrTable[record->xl_rmid].rm_redo(EndRecPtr, record); - record = ReadRecord(NULL, buffer); - } while (record->xl_len != 0); - elog(LOG, "Redo done at (%u, %u)", - ReadRecPtr.xlogid, ReadRecPtr.xrecoff); - LastRec = ReadRecPtr; - } - else - elog(LOG, "Redo is not required"); - /* UNDO */ - RecPtr = ReadRecPtr; - if (XLByteLT(checkPoint.undo, RecPtr)) - { - elog(LOG, "Undo starts at (%u, %u)", - RecPtr.xlogid, RecPtr.xrecoff); - do - { - record = ReadRecord(&RecPtr, buffer); - if (TransactionIdIsValid(record->xl_xid) && - !TransactionIdDidCommit(record->xl_xid)) - RmgrTable[record->xl_rmid].rm_undo(record); - RecPtr = record->xl_prev; - } while (XLByteLE(checkPoint.undo, RecPtr)); - elog(LOG, "Undo done at (%u, %u)", - ReadRecPtr.xlogid, ReadRecPtr.xrecoff); - } - else - elog(LOG, "Undo is not required"); - } - - /* Init xlog buffer cache */ - record = ReadRecord(&LastRec, buffer); - logId = EndRecPtr.xlogid; - logSeg = (EndRecPtr.xrecoff - 1) / XLogSegSize; - logOff = 0; - logFile = XLogFileOpen(logId, logSeg, false); - XLogCtl->xlblocks[0].xlogid = logId; - XLogCtl->xlblocks[0].xrecoff = - ((EndRecPtr.xrecoff - 1) / BLCKSZ + 1) * BLCKSZ; - Insert->currpos = ((char*) Insert->currpage) + - (EndRecPtr.xrecoff + BLCKSZ - XLogCtl->xlblocks[0].xrecoff); - - if (recovery) - { - int i; - - /* - * Let resource managers know that recovery is done - */ - for (i = 0; i <= RM_MAX_ID; i++) - RmgrTable[record->xl_rmid].rm_redo(ReadRecPtr, NULL); - CreateCheckPoint(true); - StopIfError = sie_saved; - } - - ControlFile->state = DB_IN_PRODUCTION; - ControlFile->time = time(NULL); - UpdateControlFile(); - - return; -} - -static XLogRecord* -ReadRecord(XLogRecPtr *RecPtr, char *buffer) -{ - XLogRecord *record; - XLogRecPtr tmpRecPtr = EndRecPtr; - bool nextmode = (RecPtr == NULL); - int emode = (nextmode) ? LOG : STOP; - - if (nextmode) - { - RecPtr = &tmpRecPtr; - if (nextRecord != NULL) - { - record = nextRecord; - goto got_record; - } - if (tmpRecPtr.xrecoff % BLCKSZ != 0) - tmpRecPtr.xrecoff += (BLCKSZ - tmpRecPtr.xrecoff % BLCKSZ); - if (tmpRecPtr.xrecoff >= XLogFileSize) - { - (tmpRecPtr.xlogid)++; - tmpRecPtr.xrecoff = 0; - } - tmpRecPtr.xrecoff += SizeOfXLogPHD; - } - else if (!XRecOffIsValid(RecPtr->xrecoff)) - elog(STOP, "ReadRecord: invalid record offset in (%u, %u)", - RecPtr->xlogid, RecPtr->xrecoff); - - if (readFile >= 0 && (RecPtr->xlogid != readId || - RecPtr->xrecoff / XLogSegSize != readSeg)) - { - close(readFile); - readFile = -1; - } - readId = RecPtr->xlogid; - readSeg = RecPtr->xrecoff / XLogSegSize; - if (readFile < 0) - { - readOff = (off_t) -1; - readFile = XLogFileOpen(readId, readSeg, nextmode); - if (readFile < 0) - goto next_record_is_invalid; - } - - if (readOff < 0 || readOff != (RecPtr->xrecoff % XLogSegSize) / BLCKSZ) + if (noBlck || readOff != (RecPtr->xrecoff % XLogSegSize) / BLCKSZ) { readOff = (RecPtr->xrecoff % XLogSegSize) / BLCKSZ; - if (lseek(readFile, readOff * BLCKSZ, SEEK_SET) < 0) + if (lseek(readFile, (off_t)(readOff * BLCKSZ), SEEK_SET) < 0) elog(STOP, "ReadRecord: lseek(logfile %u seg %u off %u) failed: %d", readId, readSeg, readOff, errno); if (read(readFile, readBuf, BLCKSZ) != BLCKSZ) @@ -1186,7 +880,7 @@ got_record:; readId++; } close(readFile); - readOff = (off_t) 0; + readOff = 0; readFile = XLogFileOpen(readId, readSeg, nextmode); if (readFile < 0) goto next_record_is_invalid; @@ -1280,7 +974,7 @@ next_record_is_invalid:; elog(LOG, "Formating logfile %u seg %u block %u at offset %u", readId, readSeg, readOff, EndRecPtr.xrecoff % BLCKSZ); readFile = XLogFileOpen(readId, readSeg, false); - if (lseek(readFile, readOff * BLCKSZ, SEEK_SET) < 0) + if (lseek(readFile, (off_t)(readOff * BLCKSZ), SEEK_SET) < 0) elog(STOP, "ReadRecord: lseek(logfile %u seg %u off %u) failed: %d", readId, readSeg, readOff, errno); if (read(readFile, readBuf, BLCKSZ) != BLCKSZ) @@ -1288,7 +982,7 @@ next_record_is_invalid:; readId, readSeg, readOff, errno); memset(readBuf + EndRecPtr.xrecoff % BLCKSZ, 0, BLCKSZ - EndRecPtr.xrecoff % BLCKSZ); - if (lseek(readFile, readOff * BLCKSZ, SEEK_SET) < 0) + if (lseek(readFile, (off_t)(readOff * BLCKSZ), SEEK_SET) < 0) elog(STOP, "ReadRecord: lseek(logfile %u seg %u off %u) failed: %d", readId, readSeg, readOff, errno); if (write(readFile, readBuf, BLCKSZ) != BLCKSZ) @@ -1303,15 +997,17 @@ next_record_is_invalid:; readId = tmpRecPtr.xlogid; readSeg = tmpRecPtr.xrecoff / XLogSegSize; readOff = (tmpRecPtr.xrecoff % XLogSegSize) / BLCKSZ; + Assert(readOff > 0); } if (readOff > 0) { - elog(LOG, "Formating logfile %u seg %u block %u at offset 0", - readId, readSeg, readOff); + if (!XLByteEQ(tmpRecPtr, EndRecPtr)) + elog(LOG, "Formating logfile %u seg %u block %u at offset 0", + readId, readSeg, readOff); readOff *= BLCKSZ; memset(readBuf, 0, BLCKSZ); readFile = XLogFileOpen(readId, readSeg, false); - if (lseek(readFile, readOff, SEEK_SET) < 0) + if (lseek(readFile, (off_t)readOff, SEEK_SET) < 0) elog(STOP, "ReadRecord: lseek(logfile %u seg %u off %u) failed: %d", readId, readSeg, readOff, errno); while (readOff < XLogSegSize) @@ -1357,6 +1053,381 @@ next_record_is_invalid:; return(record); } +void +UpdateControlFile() +{ + int fd; + +tryAgain: + fd = open(ControlFilePath, O_RDWR); + if (fd < 0 && (errno == EMFILE || errno == ENFILE)) + { + fd = errno; + if (!ReleaseDataFile()) + elog(STOP, "Open(cntlfile) failed: %d (and no one data file can be closed)", + fd); + goto tryAgain; + } + if (fd < 0) + elog(STOP, "Open(cntlfile) failed: %d", errno); + + if (write(fd, ControlFile, BLCKSZ) != BLCKSZ) + elog(STOP, "Write(cntlfile) failed: %d", errno); + + if (fsync(fd) != 0) + elog(STOP, "Fsync(cntlfile) failed: %d", errno); + + close(fd); + + return; +} + +int +XLOGShmemSize() +{ + if (XLOGbuffers < MinXLOGbuffers) + XLOGbuffers = MinXLOGbuffers; + + return(sizeof(XLogCtlData) + BLCKSZ * XLOGbuffers + + sizeof(XLogRecPtr) * XLOGbuffers + BLCKSZ); +} + +void +XLOGShmemInit(void) +{ + bool found; + + 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); +} + +/* + * This func must be called ONCE on system install + */ +void +BootStrapXLOG() +{ + int fd; + char buffer[BLCKSZ]; + XLogPageHeader page = (XLogPageHeader)buffer; + CheckPoint checkPoint; + XLogRecord *record; + + fd = open(ControlFilePath, O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR); + if (fd < 0) + elog(STOP, "BootStrapXLOG failed to create control file (%s): %d", + ControlFilePath, errno); + + logFile = XLogFileInit(0, 0); + + checkPoint.redo.xlogid = 0; + checkPoint.redo.xrecoff = SizeOfXLogPHD; + checkPoint.undo = checkPoint.redo; + checkPoint.nextXid = FirstTransactionId; + checkPoint.nextOid = BootstrapObjectIdData; + + memset(buffer, 0, BLCKSZ); + page->xlp_magic = XLOG_PAGE_MAGIC; + page->xlp_info = 0; + record = (XLogRecord*) ((char*)page + SizeOfXLogPHD); + record->xl_prev.xlogid = 0; record->xl_prev.xrecoff = 0; + record->xl_xact_prev = record->xl_prev; + record->xl_xid = InvalidTransactionId; + record->xl_len = sizeof(checkPoint); + record->xl_info = 0; + record->xl_rmid = RM_XLOG_ID; + memcpy((char*)record + SizeOfXLogRecord, &checkPoint, sizeof(checkPoint)); + + if (write(logFile, buffer, BLCKSZ) != BLCKSZ) + elog(STOP, "BootStrapXLOG failed to write logfile: %d", errno); + + if (fsync(logFile) != 0) + elog(STOP, "BootStrapXLOG failed to fsync logfile: %d", errno); + + close(logFile); + logFile = -1; + + memset(buffer, 0, BLCKSZ); + ControlFile = (ControlFileData*) buffer; + ControlFile->logId = 0; + ControlFile->logSeg = 1; + ControlFile->checkPoint = checkPoint.redo; + ControlFile->time = time(NULL); + ControlFile->state = DB_SHUTDOWNED; + + if (write(fd, buffer, BLCKSZ) != BLCKSZ) + elog(STOP, "BootStrapXLOG failed to write control file: %d", errno); + + if (fsync(fd) != 0) + elog(STOP, "BootStrapXLOG failed to fsync control file: %d", errno); + + close(fd); + + return; + +} + +static char* +str_time(time_t tnow) +{ + char *result = ctime(&tnow); + char *p = strchr(result, '\n'); + + if (p != NULL) + *p = 0; + + return(result); +} + +/* + * This func must be called ONCE on system startup + */ +void +StartupXLOG() +{ + XLogCtlInsert *Insert; + CheckPoint checkPoint; + XLogRecPtr RecPtr, + LastRec; + XLogRecord *record; + char buffer[MAXLOGRECSZ+SizeOfXLogRecord]; + int fd; + int recovery = 0; + bool sie_saved = false; + + elog(LOG, "Data Base System is starting up at %s", str_time(time(NULL))); + + XLogCtl->xlblocks = (XLogRecPtr*) (((char *)XLogCtl) + sizeof(XLogCtlData)); + XLogCtl->pages = ((char *)XLogCtl->xlblocks + sizeof(XLogRecPtr) * XLOGbuffers); + XLogCtl->XLogCacheByte = BLCKSZ * XLOGbuffers; + XLogCtl->XLogCacheBlck = XLOGbuffers - 1; + memset(XLogCtl->xlblocks, 0, sizeof(XLogRecPtr) * XLOGbuffers); + XLogCtl->LgwrRqst = LgwrRqst; + XLogCtl->LgwrResult = LgwrResult; + XLogCtl->Insert.LgwrResult = LgwrResult; + XLogCtl->Insert.curridx = 0; + XLogCtl->Insert.currpage = (XLogPageHeader) (XLogCtl->pages); + XLogCtl->Write.LgwrResult = LgwrResult; + XLogCtl->Write.curridx = 0; + S_INIT_LOCK(&(XLogCtl->insert_lck)); + S_INIT_LOCK(&(XLogCtl->info_lck)); + S_INIT_LOCK(&(XLogCtl->lgwr_lck)); + + /* + * Open/read Control file + */ +tryAgain: + fd = open(ControlFilePath, O_RDWR); + if (fd < 0 && (errno == EMFILE || errno == ENFILE)) + { + fd = errno; + if (!ReleaseDataFile()) + elog(STOP, "Open(cntlfile) failed: %d (and no one data file can be closed)", + fd); + goto tryAgain; + } + if (fd < 0) + elog(STOP, "Open(cntlfile) failed: %d", errno); + + if (read(fd, ControlFile, BLCKSZ) != BLCKSZ) + elog(STOP, "Read(cntlfile) failed: %d", errno); + + close(fd); + + if (ControlFile->logSeg == 0 || + ControlFile->time <= 0 || + ControlFile->state < DB_SHUTDOWNED || + ControlFile->state > DB_IN_PRODUCTION || + !XRecOffIsValid(ControlFile->checkPoint.xrecoff)) + elog(STOP, "Control file context is broken"); + + if (ControlFile->state == DB_SHUTDOWNED) + elog(LOG, "Data Base System was shutdowned at %s", + str_time(ControlFile->time)); + else if (ControlFile->state == DB_SHUTDOWNING) + elog(LOG, "Data Base System was interrupted when shutting down at %s", + str_time(ControlFile->time)); + else if (ControlFile->state == DB_IN_RECOVERY) + { + elog(LOG, "Data Base 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, "Data Base System was interrupted being in production at %s", + str_time(ControlFile->time)); + + LastRec = RecPtr = ControlFile->checkPoint; + if (!XRecOffIsValid(RecPtr.xrecoff)) + elog(STOP, "Invalid checkPoint in control file"); + elog(LOG, "CheckPoint record at (%u, %u)", RecPtr.xlogid, RecPtr.xrecoff); + + record = ReadRecord(&RecPtr, buffer); + if (record->xl_rmid != RM_XLOG_ID) + elog(STOP, "Invalid RMID in checkPoint record"); + if (record->xl_len != sizeof(checkPoint)) + elog(STOP, "Invalid length of checkPoint record"); + checkPoint = *((CheckPoint*)((char*)record + SizeOfXLogRecord)); + + elog(LOG, "Redo record at (%u, %u); Undo record at (%u, %u)", + checkPoint.redo.xlogid, checkPoint.redo.xrecoff, + checkPoint.undo.xlogid, checkPoint.undo.xrecoff); + elog(LOG, "NextTransactionId: %u; NextOid: %u", + checkPoint.nextXid, checkPoint.nextOid); + if (checkPoint.nextXid < FirstTransactionId || + checkPoint.nextOid < BootstrapObjectIdData) +#ifdef XLOG + elog(STOP, "Invalid NextTransactionId/NextOid"); +#else + elog(LOG, "Invalid NextTransactionId/NextOid"); +#endif + +#ifdef XLOG + ShmemVariableCache->nextXid = checkPoint.nextXid; + ShmemVariableCache->nextOid = checkPoint.nextOid; +#endif + + if (XLByteLT(RecPtr, checkPoint.redo)) + elog(STOP, "Invalid redo in checkPoint record"); + if (checkPoint.undo.xrecoff == 0) + checkPoint.undo = RecPtr; + if (XLByteLT(RecPtr, checkPoint.undo)) + elog(STOP, "Invalid undo in checkPoint record"); + + if (XLByteLT(checkPoint.undo, RecPtr) || XLByteLT(checkPoint.redo, RecPtr)) + { + if (ControlFile->state == DB_SHUTDOWNED) + elog(STOP, "Invalid Redo/Undo record in Shutdowned state"); + recovery = 2; + } + else if (ControlFile->state != DB_SHUTDOWNED) + recovery = 2; + + if (recovery > 0) + { + elog(LOG, "The DataBase system was not properly shutdowned\n" + "\tAutomatic recovery is in progress..."); + ControlFile->state = DB_IN_RECOVERY; + ControlFile->time = time(NULL); + UpdateControlFile(); + + sie_saved = StopIfError; + StopIfError = true; + + /* Is REDO required ? */ + if (XLByteLT(checkPoint.redo, RecPtr)) + record = ReadRecord(&(checkPoint.redo), buffer); + else /* read past CheckPoint record */ + record = ReadRecord(NULL, buffer); + + /* REDO */ + if (record->xl_len != 0) + { + elog(LOG, "Redo starts at (%u, %u)", + ReadRecPtr.xlogid, ReadRecPtr.xrecoff); + do + { +#ifdef XLOG + if (record->xl_xid >= ShmemVariableCache->nextXid) + ShmemVariableCache->nextXid = record->xl_xid + 1; +#endif + RmgrTable[record->xl_rmid].rm_redo(EndRecPtr, record); + record = ReadRecord(NULL, buffer); + } while (record->xl_len != 0); + elog(LOG, "Redo done at (%u, %u)", + ReadRecPtr.xlogid, ReadRecPtr.xrecoff); + LastRec = ReadRecPtr; + } + else + { + elog(LOG, "Redo is not required"); + recovery--; + } + + /* UNDO */ + RecPtr = ReadRecPtr; + if (XLByteLT(checkPoint.undo, RecPtr)) + { + elog(LOG, "Undo starts at (%u, %u)", + RecPtr.xlogid, RecPtr.xrecoff); + do + { + record = ReadRecord(&RecPtr, buffer); + if (TransactionIdIsValid(record->xl_xid) && + !TransactionIdDidCommit(record->xl_xid)) + RmgrTable[record->xl_rmid].rm_undo(record); + RecPtr = record->xl_prev; + } while (XLByteLE(checkPoint.undo, RecPtr)); + elog(LOG, "Undo done at (%u, %u)", + ReadRecPtr.xlogid, ReadRecPtr.xrecoff); + } + else + { + elog(LOG, "Undo is not required"); + recovery--; + } + } + + /* Init xlog buffer cache */ + record = ReadRecord(&LastRec, buffer); + logId = EndRecPtr.xlogid; + logSeg = (EndRecPtr.xrecoff - 1) / XLogSegSize; + logOff = 0; + logFile = XLogFileOpen(logId, logSeg, false); + XLogCtl->xlblocks[0].xlogid = logId; + XLogCtl->xlblocks[0].xrecoff = + ((EndRecPtr.xrecoff - 1) / BLCKSZ + 1) * BLCKSZ; + Insert = &XLogCtl->Insert; + memcpy((char*)(Insert->currpage), readBuf, BLCKSZ); + Insert->currpos = ((char*) Insert->currpage) + + (EndRecPtr.xrecoff + BLCKSZ - XLogCtl->xlblocks[0].xrecoff); + Insert->PrevRecord = ControlFile->checkPoint; + + if (recovery > 0) + { + int i; + + /* + * Let resource managers know that recovery is done + */ + for (i = 0; i <= RM_MAX_ID; i++) + RmgrTable[record->xl_rmid].rm_redo(ReadRecPtr, NULL); + CreateCheckPoint(true); + StopIfError = sie_saved; + } + + ControlFile->state = DB_IN_PRODUCTION; + ControlFile->time = time(NULL); + UpdateControlFile(); + + elog(LOG, "Data Base System is in production state at %s", str_time(time(NULL))); + + return; +} + +/* + * This func must be called ONCE on system shutdown + */ +void +ShutdownXLOG() +{ + + elog(LOG, "Data Base System is shutting down at %s", str_time(time(NULL))); + + CreateCheckPoint(true); + + elog(LOG, "Data Base System is shutdowned at %s", str_time(time(NULL))); +} + void CreateCheckPoint(bool shutdown) { @@ -1375,7 +1446,7 @@ CreateCheckPoint(bool shutdown) } /* Get REDO record ptr */ - while (!TAS(&(XLogCtl->insert_lck))) + while (TAS(&(XLogCtl->insert_lck))) { struct timeval delay = {0, 5000}; @@ -1410,6 +1481,7 @@ CreateCheckPoint(bool shutdown) FlushBufferPool(); /* Get UNDO record ptr */ + checkPoint.undo.xrecoff = 0; if (shutdown && checkPoint.undo.xrecoff != 0) elog(STOP, "Active transaction while data base is shutting down"); diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c index c20a63fc6d..8871e14835 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -7,7 +7,7 @@ * Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/bootstrap/bootstrap.c,v 1.68 1999/09/27 20:26:58 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/bootstrap/bootstrap.c,v 1.69 1999/10/06 21:58:02 vadim Exp $ * *------------------------------------------------------------------------- */ @@ -39,6 +39,14 @@ #define ALLOC(t, c) (t *)calloc((unsigned)(c), sizeof(t)) #define FIRST_TYPE_OID 16 /* OID of the first type */ +extern void BaseInit(void); +extern void StartupXLOG(void); +extern void ShutdownXLOG(void); +extern void BootStrapXLOG(void); + +extern char XLogDir[]; +extern char ControlFilePath[]; + extern int Int_yyparse(void); static hashnode *AddStr(char *str, int strlength, int mderef); static Form_pg_attribute AllocateAttribute(void); @@ -218,22 +226,13 @@ BootstrapMain(int argc, char *argv[]) */ { int i; - int portFd = -1; char *dbName; int flag; - int override = 1; /* use BootstrapProcessing or - * InitProcessing mode */ + bool xloginit = false; extern int optind; extern char *optarg; - /* ---------------- - * initialize signal handlers - * ---------------- - */ - pqsignal(SIGINT, (sig_func) die); - pqsignal(SIGHUP, (sig_func) die); - pqsignal(SIGTERM, (sig_func) die); /* -------------------- * initialize globals @@ -252,8 +251,9 @@ BootstrapMain(int argc, char *argv[]) Noversion = false; dbName = NULL; DataDir = getenv("PGDATA"); /* Null if no PGDATA variable */ + IsUnderPostmaster = false; - while ((flag = getopt(argc, argv, "D:dCOQP:F")) != EOF) + while ((flag = getopt(argc, argv, "D:dCQxpB:F")) != EOF) { switch (flag) { @@ -270,14 +270,17 @@ BootstrapMain(int argc, char *argv[]) case 'F': disableFsync = true; break; - case 'O': - override = true; - break; case 'Q': Quiet = true; break; - case 'P': /* specify port */ - portFd = atoi(optarg); + case 'x': + xloginit = true; + break; + case 'p': + IsUnderPostmaster = true; + break; + case 'B': + NBuffers = atoi(optarg); break; default: usage(); @@ -290,6 +293,8 @@ BootstrapMain(int argc, char *argv[]) else if (argc - optind == 1) dbName = argv[optind]; + SetProcessingMode(BootstrapProcessing); + if (!DataDir) { fprintf(stderr, "%s does not know where to find the database system " @@ -311,24 +316,50 @@ BootstrapMain(int argc, char *argv[]) } } - /* ---------------- - * initialize input fd - * ---------------- + BaseInit(); + + if (!IsUnderPostmaster) + { + pqsignal(SIGINT, (sig_func) die); + pqsignal(SIGHUP, (sig_func) die); + pqsignal(SIGTERM, (sig_func) die); + } + + /* + * Bootstrap under Postmaster means two things: + * (xloginit) ? StartupXLOG : ShutdownXLOG + * + * If !under Postmaster and xloginit then BootStrapXLOG. */ - if (IsUnderPostmaster && portFd < 0) + if (IsUnderPostmaster || xloginit) { - fputs("backend: failed, no -P option with -postmaster opt.\n", stderr); - proc_exit(1); + sprintf(XLogDir, "%s%cpg_xlog", DataDir, SEP_CHAR); + sprintf(ControlFilePath, "%s%cpg_control", DataDir, SEP_CHAR); } - /* ---------------- - * backend initialization - * ---------------- + if (IsUnderPostmaster && xloginit) + { + StartupXLOG(); + proc_exit(0); + } + + if (!IsUnderPostmaster && xloginit) + { + BootStrapXLOG(); + } + + /* + * backend initialization */ - SetProcessingMode((override) ? BootstrapProcessing : InitProcessing); InitPostgres(dbName); LockDisable(true); + if (IsUnderPostmaster && !xloginit) + { + ShutdownXLOG(); + proc_exit(0); + } + for (i = 0; i < MAXATTR; i++) { attrtypes[i] = (Form_pg_attribute) NULL; diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index 78d8bc5e30..bb6d5b638e 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.120 1999/09/30 02:45:17 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.121 1999/10/06 21:58:03 vadim Exp $ * * NOTES * @@ -87,6 +87,7 @@ #include "storage/fd.h" #include "storage/ipc.h" #include "storage/proc.h" +#include "access/xlog.h" #include "tcop/tcopprot.h" #include "utils/trace.h" #include "version.h" @@ -98,10 +99,13 @@ #define INVALID_SOCK (-1) #define ARGV_SIZE 64 - /* - * Max time in seconds for socket to linger (close() to block) waiting - * for frontend to retrieve its message from us. - */ +#ifdef HAVE_SIGPROCMASK +sigset_t UnBlockSig, + BlockSig; +#else +int UnBlockSig, + BlockSig; +#endif /* * Info for garbage collection. Whenever a process dies, the Postmaster @@ -124,7 +128,6 @@ static Dllist *BackendList; static Dllist *PortList; static unsigned short PostPortName = 0; -static short ActiveBackends = FALSE; /* * This is a boolean indicating that there is at least one backend that @@ -209,18 +212,16 @@ static bool SecureNetServer = false; /* if not zero, postmaster listens for only * non-local connections */ #endif -/* - * GH: For !HAVE_SIGPROCMASK (NEXTSTEP), TRH implemented an - * alternative interface. - */ -#ifdef HAVE_SIGPROCMASK -static sigset_t oldsigmask, - newsigmask; +static pid_t StartupPID = 0, + ShutdownPID = 0; -#else -static int orgsigmask = sigblock(0); +#define NoShutdown 0 +#define SmartShutdown 1 +#define FastShutdown 2 -#endif +static int Shutdown = NoShutdown; + +static bool FatalError = false; /* * State for assigning random salts and cancel keys. @@ -234,30 +235,35 @@ extern char *optarg; extern int optind, opterr; - /* * postmaster.c - function prototypes */ -static void pmdaemonize(void); -static Port *ConnCreate(int serverFd); -static void ConnFree(Port *port); -static void reset_shared(unsigned short port); -static void pmdie(SIGNAL_ARGS); -static void reaper(SIGNAL_ARGS); -static void dumpstatus(SIGNAL_ARGS); -static void CleanupProc(int pid, int exitstatus); -static int DoBackend(Port *port); -static void ExitPostmaster(int status); -static void usage(const char *); -static int ServerLoop(void); -static int BackendStartup(Port *port); -static int readStartupPacket(void *arg, PacketLen len, void *pkt); -static int processCancelRequest(Port *port, PacketLen len, void *pkt); -static int initMasks(fd_set *rmask, fd_set *wmask); -static long PostmasterRandom(void); -static void RandomSalt(char *salt); -static void SignalChildren(SIGNAL_ARGS); -static int CountChildren(void); +static void pmdaemonize(void); +static Port *ConnCreate(int serverFd); +static void ConnFree(Port *port); +static void reset_shared(unsigned short port); +static void pmdie(SIGNAL_ARGS); +static void reaper(SIGNAL_ARGS); +static void dumpstatus(SIGNAL_ARGS); +static void CleanupProc(int pid, int exitstatus); +static int DoBackend(Port *port); +static void ExitPostmaster(int status); +static void usage(const char *); +static int ServerLoop(void); +static int BackendStartup(Port *port); +static int readStartupPacket(void *arg, PacketLen len, void *pkt); +static int processCancelRequest(Port *port, PacketLen len, void *pkt); +static int initMasks(fd_set *rmask, fd_set *wmask); +static long PostmasterRandom(void); +static void RandomSalt(char *salt); +static void SignalChildren(SIGNAL_ARGS); +static int CountChildren(void); + +extern int BootstrapMain(int argc, char *argv[]); +static pid_t SSDataBase(bool startup); +#define StartupDataBase() SSDataBase(true) +#define ShutdownDataBase() SSDataBase(false) + #ifdef USE_SSL static void InitSSL(void); #endif @@ -613,18 +619,23 @@ PostmasterMain(int argc, char *argv[]) /* * Set up signal handlers for the postmaster process. */ - - pqsignal(SIGHUP, pmdie); /* send SIGHUP, don't die */ - pqsignal(SIGINT, pmdie); /* die */ - pqsignal(SIGQUIT, pmdie); /* send SIGTERM and die */ - pqsignal(SIGTERM, pmdie); /* send SIGTERM,SIGKILL and die */ - pqsignal(SIGPIPE, SIG_IGN); /* ignored */ - pqsignal(SIGUSR1, pmdie); /* send SIGUSR1 and die */ - pqsignal(SIGUSR2, pmdie); /* send SIGUSR2, don't die */ - pqsignal(SIGCHLD, reaper); /* handle child termination */ - pqsignal(SIGTTIN, SIG_IGN); /* ignored */ - pqsignal(SIGTTOU, SIG_IGN); /* ignored */ - pqsignal(SIGWINCH, dumpstatus); /* dump port status */ + PG_INITMASK(); + PG_SETMASK(&BlockSig); + + pqsignal(SIGHUP, pmdie); /* send SIGHUP, don't die */ + pqsignal(SIGINT, pmdie); /* send SIGTERM and ShutdownDataBase */ + pqsignal(SIGQUIT, pmdie); /* send SIGUSR1 and die */ + pqsignal(SIGTERM, pmdie); /* wait for children and ShutdownDataBase */ + pqsignal(SIGALRM, SIG_IGN); /* ignored */ + pqsignal(SIGPIPE, SIG_IGN); /* ignored */ + pqsignal(SIGUSR1, SIG_IGN); /* ignored */ + pqsignal(SIGUSR2, pmdie); /* send SIGUSR2, don't die */ + pqsignal(SIGCHLD, reaper); /* handle child termination */ + pqsignal(SIGTTIN, SIG_IGN); /* ignored */ + pqsignal(SIGTTOU, SIG_IGN); /* ignored */ + pqsignal(SIGWINCH, dumpstatus); /* dump port status */ + + StartupPID = StartupDataBase(); status = ServerLoop(); @@ -702,12 +713,6 @@ ServerLoop(void) nSockets = initMasks(&readmask, &writemask); -#ifdef HAVE_SIGPROCMASK - sigprocmask(0, NULL, &oldsigmask); - sigemptyset(&newsigmask); - sigaddset(&newsigmask, SIGCHLD); -#endif - for (;;) { Port *port; @@ -717,25 +722,25 @@ ServerLoop(void) int no_select = 0; #endif -#ifdef HAVE_SIGPROCMASK - sigprocmask(SIG_SETMASK, &oldsigmask, 0); -#else - sigsetmask(orgsigmask); -#endif - memmove((char *) &rmask, (char *) &readmask, sizeof(fd_set)); memmove((char *) &wmask, (char *) &writemask, sizeof(fd_set)); #ifdef USE_SSL for (curr = DLGetHead(PortList); curr; curr = DLGetSucc(curr)) - if (((Port *)DLE_VAL(curr))->ssl && - SSL_pending(((Port *)DLE_VAL(curr))->ssl) > 0) { - no_select = 1; - break; - } + { + if (((Port *)DLE_VAL(curr))->ssl && + SSL_pending(((Port *)DLE_VAL(curr))->ssl) > 0) + { + no_select = 1; + break; + } + } + PG_SETMASK(&UnBlockSig); if (no_select) FD_ZERO(&rmask); /* So we don't accept() anything below */ else +#else + PG_SETMASK(&UnBlockSig); #endif if (select(nSockets, &rmask, &wmask, (fd_set *) NULL, (struct timeval *) NULL) < 0) @@ -765,16 +770,9 @@ ServerLoop(void) } /* - * [TRH] To avoid race conditions, block SIGCHLD signals while we - * are handling the request. (both reaper() and ConnCreate() - * manipulate the BackEnd list, and reaper() calls free() which is - * usually non-reentrant.) + * Block all signals */ -#ifdef HAVE_SIGPROCMASK - sigprocmask(SIG_BLOCK, &newsigmask, &oldsigmask); -#else - sigblock(sigmask(SIGCHLD)); /* XXX[TRH] portability */ -#endif + PG_SETMASK(&BlockSig); /* new connection pending on our well-known port's socket */ @@ -817,8 +815,8 @@ ServerLoop(void) } else #endif - if (FD_ISSET(port->sock, &rmask)) - readyread = 1; + if (FD_ISSET(port->sock, &rmask)) + readyread = 1; if (readyread) { @@ -852,13 +850,25 @@ ServerLoop(void) if (status == STATUS_OK && port->pktInfo.state == Idle) { - /* Can't start backend if max backend count is exceeded. */ - if (CountChildren() >= MaxBackends) + /* + * Can't start backend if max backend count is exceeded. + * + * The same when shutdowning data base. + */ + if (Shutdown > NoShutdown) + PacketSendError(&port->pktInfo, + "The Data Base System is shutting down"); + else if (StartupPID) + PacketSendError(&port->pktInfo, + "The Data Base System is starting up"); + else if (FatalError) + PacketSendError(&port->pktInfo, + "The Data Base System is in recovery mode"); + else if (CountChildren() >= MaxBackends) PacketSendError(&port->pktInfo, "Sorry, too many clients already"); else { - /* * If the backend start fails then keep the connection * open to report it. Otherwise, pretend there is an @@ -1113,6 +1123,7 @@ ConnCreate(int serverFd) { fprintf(stderr, "%s: ConnCreate: malloc failed\n", progname); + SignalChildren(SIGUSR1); ExitPostmaster(1); } @@ -1154,7 +1165,6 @@ reset_shared(unsigned short port) { ipc_key = port * 1000 + shmem_seq * 100; CreateSharedMemoryAndSemaphores(ipc_key, MaxBackends); - ActiveBackends = FALSE; shmem_seq += 1; if (shmem_seq >= 10) shmem_seq -= 10; @@ -1166,49 +1176,94 @@ reset_shared(unsigned short port) static void pmdie(SIGNAL_ARGS) { - int i; - + PG_SETMASK(&BlockSig); + TPRINTF(TRACE_VERBOSE, "pmdie %d", postgres_signal_arg); - /* - * Kill self and/or children processes depending on signal number. - */ switch (postgres_signal_arg) { case SIGHUP: - /* Send SIGHUP to all children (update options flags) */ + /* + * Send SIGHUP to all children (update options flags) + */ + if (Shutdown > SmartShutdown) + return; SignalChildren(SIGHUP); - /* Don't die */ return; - case SIGINT: - /* Die without killing children */ - break; - case SIGQUIT: - /* Shutdown all children with SIGTERM */ - SignalChildren(SIGTERM); - /* Don't die */ + case SIGUSR2: + /* + * Send SIGUSR2 to all children (AsyncNotifyHandler) + */ + if (Shutdown > SmartShutdown) + return; + SignalChildren(SIGUSR2); return; + case SIGTERM: - /* Shutdown all children with SIGTERM and SIGKILL, then die */ - SignalChildren(SIGTERM); - for (i = 0; i < 10; i++) + /* + * Smart Shutdown: + * + * let children to end their work and ShutdownDataBase. + */ + if (Shutdown >= SmartShutdown) + return; + Shutdown = SmartShutdown; + if (DLGetHead(BackendList)) /* let reaper() handle this */ + return; + /* + * No children left. Shutdown data base system. + */ + if (StartupPID > 0 || FatalError) /* let reaper() handle this */ + return; + if (ShutdownPID > 0) + abort(); + + ShutdownPID = ShutdownDataBase(); + return; + + case SIGINT: + /* + * Fast Shutdown: + * + * abort all children with SIGTERM (rollback active + * transactions and exit) and ShutdownDataBase. + */ + if (Shutdown >= FastShutdown) + return; + if (DLGetHead(BackendList)) /* let reaper() handle this */ { - if (!DLGetHead(BackendList)) - break; - sleep(1); + if (!FatalError) + SignalChildren(SIGTERM); + return; } - if (DLGetHead(BackendList)) - SignalChildren(SIGKILL); - break; - case SIGUSR1: - /* Quick die all children with SIGUSR1 and die */ - SignalChildren(SIGUSR1); - break; - case SIGUSR2: - /* Send SIGUSR2 to all children (AsyncNotifyHandler) */ - SignalChildren(SIGUSR2); - /* Don't die */ + if (Shutdown > NoShutdown) + return; + Shutdown = FastShutdown; + /* + * No children left. Shutdown data base system. + */ + if (StartupPID > 0 || FatalError) /* let reaper() handle this */ + return; + if (ShutdownPID > 0) + abort(); + + ShutdownPID = ShutdownDataBase(); /* flag for reaper() */ return; + + case SIGQUIT: + /* + * Immediate Shutdown: + * + * abort all children with SIGUSR1 and exit without + * attempt to properly shutdown data base system. + */ + if (ShutdownPID > 0) + kill(ShutdownPID, SIGQUIT); + else if (StartupPID > 0) + kill(StartupPID, SIGQUIT); + else if (DLGetHead(BackendList)) + SignalChildren(SIGUSR1); + break; } /* exit postmaster */ @@ -1224,29 +1279,82 @@ reaper(SIGNAL_ARGS) /* GH: replace waitpid for !HAVE_WAITPID. Does this work ? */ #ifdef HAVE_WAITPID int status; /* backend exit status */ - #else - union wait statusp; /* backend exit status */ - + union wait status; /* backend exit status */ #endif + int exitstatus; int pid; /* process id of dead backend */ + PG_SETMASK(&BlockSig); + if (DebugLvl) fprintf(stderr, "%s: reaping dead processes...\n", progname); #ifdef HAVE_WAITPID while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { - CleanupProc(pid, status); - pqsignal(SIGCHLD, reaper); - } + exitstatus = status; #else - while ((pid = wait3(&statusp, WNOHANG, NULL)) > 0) + while ((pid = wait3(&status, WNOHANG, NULL)) > 0) { - CleanupProc(pid, statusp.w_status); - pqsignal(SIGCHLD, reaper); - } + exitstatus = status.w_status; #endif + if (ShutdownPID > 0) + { + if (pid != ShutdownPID) + abort(); + if (exitstatus != 0) + abort(); + proc_exit(0); + } + if (StartupPID > 0) + { + if (pid != StartupPID) + abort(); + if (exitstatus != 0) + abort(); + StartupPID = 0; + FatalError = false; + if (Shutdown > NoShutdown) + { + if (ShutdownPID > 0) + abort(); + ShutdownPID = ShutdownDataBase(); + } + pqsignal(SIGCHLD, reaper); + return; + } + CleanupProc(pid, exitstatus); + } + pqsignal(SIGCHLD, reaper); + + if (FatalError) + { + /* + * Wait for all children exit then StartupDataBase. + */ + if (DLGetHead(BackendList)) + return; + if (StartupPID > 0 || ShutdownPID > 0) + return; + if (DebugLvl) + fprintf(stderr, "%s: CleanupProc: reinitializing shared memory and semaphores\n", + progname); + shmem_exit(0); + reset_shared(PostPortName); + StartupPID = StartupDataBase(); + return; + } + + if (Shutdown > NoShutdown) + { + if (DLGetHead(BackendList)) + return; + if (StartupPID > 0 || ShutdownPID > 0) + return; + ShutdownPID = ShutdownDataBase(); + } + } /* @@ -1260,8 +1368,8 @@ static void CleanupProc(int pid, int exitstatus) /* child's exit status. */ { - Dlelem *prev, - *curr; + Dlelem *curr, + *next; Backend *bp; int sig; @@ -1298,18 +1406,19 @@ CleanupProc(int pid, return; } + FatalError = true; curr = DLGetHead(BackendList); while (curr) { + next = DLGetSucc(curr); bp = (Backend *) DLE_VAL(curr); - /* ----------------- + /* * SIGUSR1 is the special signal that says exit * without proc_exit and let the user know what's going on. * ProcSemaphoreKill() cleans up the backends semaphore. If * SendStop is set (-s on command line), then we send a SIGSTOP so * that we can core dumps from all backends by hand. - * ----------------- */ sig = (SendStop) ? SIGSTOP : SIGUSR1; if (bp->pid != pid) @@ -1322,36 +1431,25 @@ CleanupProc(int pid, bp->pid); kill(bp->pid, sig); } - ProcRemove(bp->pid); - - prev = DLGetPred(curr); - DLRemove(curr); - free(bp); - DLFreeElem(curr); - if (!prev) - { /* removed head */ - curr = DLGetHead(BackendList); - continue; + else + { + /* + * I don't like that we call ProcRemove() here, assuming that + * shmem may be corrupted! But is there another way to free + * backend semaphores? Actually, I believe that we need not + * in per backend semaphore at all (we use them to wait on lock + * only, couldn't we just sigpause?), so probably we'll + * remove this call from here someday. -- vadim 04-10-1999 + */ + ProcRemove(pid); + + DLRemove(curr); + free(bp); + DLFreeElem(curr); } - curr = DLGetSucc(prev); + curr = next; } - /* - * Nothing up my sleeve here, ActiveBackends means that since the last - * time we recreated shared memory and sems another frontend has - * requested and received a connection and I have forked off another - * backend. This prevents me from reinitializing shared stuff more - * than once for the set of backends that caused the failure and were - * killed off. - */ - if (ActiveBackends == TRUE && Reinit) - { - if (DebugLvl) - fprintf(stderr, "%s: CleanupProc: reinitializing shared memory and semaphores\n", - progname); - shmem_exit(0); - reset_shared(PostPortName); - } } /* @@ -1516,8 +1614,6 @@ BackendStartup(Port *port) bn->cancel_key = MyCancelKey; DLAddHead(BackendList, DLNewElem(bn)); - ActiveBackends = TRUE; - return STATUS_OK; } @@ -1586,27 +1682,9 @@ DoBackend(Port *port) /* We don't want the postmaster's proc_exit() handlers */ on_exit_reset(); - /* ---------------- - * register signal handlers. - * Thanks to the postmaster, these are currently blocked. - * ---------------- + /* + * Signal handlers setting is moved to tcop/postgres... */ - pqsignal(SIGINT, die); - - pqsignal(SIGHUP, die); - pqsignal(SIGTERM, die); - pqsignal(SIGPIPE, die); - pqsignal(SIGUSR1, quickdie); - pqsignal(SIGUSR2, Async_NotifyHandler); - pqsignal(SIGFPE, FloatExceptionHandler); - - pqsignal(SIGCHLD, SIG_DFL); - pqsignal(SIGTTIN, SIG_DFL); - pqsignal(SIGTTOU, SIG_DFL); - pqsignal(SIGCONT, SIG_DFL); - - /* OK, let's unblock our signals, all together now... */ - sigprocmask(SIG_SETMASK, &oldsigmask, 0); /* Close the postmaster sockets */ if (NetServer) @@ -1739,6 +1817,8 @@ ExitPostmaster(int status) /* * Not sure of the semantics here. When the Postmaster dies, should * the backends all be killed? probably not. + * + * MUST -- vadim 05-10-1999 */ if (ServerSock_INET != INVALID_SOCK) StreamClose(ServerSock_INET); @@ -1752,8 +1832,11 @@ ExitPostmaster(int status) static void dumpstatus(SIGNAL_ARGS) { - Dlelem *curr = DLGetHead(PortList); + Dlelem *curr; + PG_SETMASK(&BlockSig); + + curr = DLGetHead(PortList); while (curr) { Port *port = DLE_VAL(curr); @@ -1837,7 +1920,6 @@ CountChildren(void) return cnt; } - #ifdef USE_SSL /* * Initialize SSL library and structures @@ -1868,3 +1950,88 @@ static void InitSSL(void) { } } #endif + +static pid_t +SSDataBase(bool startup) +{ + pid_t pid; + int i; + static char ssEntry[4][2 * ARGV_SIZE]; + + for (i = 0; i < 4; ++i) + MemSet(ssEntry[i], 0, 2 * ARGV_SIZE); + + sprintf(ssEntry[0], "POSTPORT=%d", PostPortName); + putenv(ssEntry[0]); + sprintf(ssEntry[1], "POSTID=%d", NextBackendTag); + putenv(ssEntry[1]); + if (!getenv("PGDATA")) + { + sprintf(ssEntry[2], "PGDATA=%s", DataDir); + putenv(ssEntry[2]); + } + sprintf(ssEntry[3], "IPC_KEY=%d", ipc_key); + putenv(ssEntry[3]); + + fflush(stdout); + fflush(stderr); + + if ((pid = fork()) == 0) /* child */ + { + char *av[ARGV_SIZE * 2]; + int ac = 0; + char execbuf[MAXPATHLEN]; + char nbbuf[ARGV_SIZE]; + char dbbuf[ARGV_SIZE]; + + on_exit_reset(); + if (NetServer) + StreamClose(ServerSock_INET); +#ifndef __CYGWIN32__ + StreamClose(ServerSock_UNIX); +#endif + + StrNCpy(execbuf, Execfile, MAXPATHLEN); + av[ac++] = execbuf; + + av[ac++] = "-d"; + + sprintf(nbbuf, "-B%u", NBuffers); + av[ac++] = nbbuf; + + if (startup) + av[ac++] = "-x"; + + av[ac++] = "-p"; + + StrNCpy(dbbuf, "template1", ARGV_SIZE); + av[ac++] = dbbuf; + + av[ac] = (char *) NULL; + + optind = 1; + + pqsignal(SIGQUIT, SIG_DFL); +#ifdef HAVE_SIGPROCMASK + sigdelset(&BlockSig, SIGQUIT); +#else + BlockSig &= ~(sigmask(SIGQUIT)); +#endif + PG_SETMASK(&BlockSig); + + BootstrapMain(ac, av); + exit(0); + } + + /* in parent */ + if (pid < 0) + { + fprintf(stderr, "%s Data Base: fork failed: %s\n", + ((startup) ? "Startup" : "Shutdown"), strerror(errno)); + ExitPostmaster(1); + } + + NextBackendTag -= 1; + + return(pid); +} diff --git a/src/backend/storage/buffer/buf_init.c b/src/backend/storage/buffer/buf_init.c index bfd0561705..3fe2ec8be6 100644 --- a/src/backend/storage/buffer/buf_init.c +++ b/src/backend/storage/buffer/buf_init.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/storage/buffer/buf_init.c,v 1.30 1999/09/24 00:24:29 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/storage/buffer/buf_init.c,v 1.31 1999/10/06 21:58:04 vadim Exp $ * *------------------------------------------------------------------------- */ @@ -230,16 +230,18 @@ InitBufferPool(IPCKey key) #ifndef HAS_TEST_AND_SET { - int status; extern IpcSemaphoreId WaitIOSemId; extern IpcSemaphoreId WaitCLSemId; WaitIOSemId = IpcSemaphoreCreate(IPCKeyGetWaitIOSemaphoreKey(key), - 1, IPCProtection, 0, 1, &status); + 1, IPCProtection, 0, 1); + if (WaitIOSemId < 0) + elog(FATAL, "InitBufferPool: IpcSemaphoreCreate(WaitIOSemId) failed"); WaitCLSemId = IpcSemaphoreCreate(IPCKeyGetWaitCLSemaphoreKey(key), 1, IPCProtection, - IpcSemaphoreDefaultStartValue, - 1, &status); + IpcSemaphoreDefaultStartValue, 1); + if (WaitCLSemId < 0) + elog(FATAL, "InitBufferPool: IpcSemaphoreCreate(WaitCLSemId) failed"); } #endif PrivateRefCount = (long *) calloc(NBuffers, sizeof(long)); diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c index 59c991bd0a..ff326becf1 100644 --- a/src/backend/storage/ipc/ipc.c +++ b/src/backend/storage/ipc/ipc.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/storage/ipc/ipc.c,v 1.38 1999/07/17 20:17:43 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/storage/ipc/ipc.c,v 1.39 1999/10/06 21:58:06 vadim Exp $ * * NOTES * @@ -284,18 +284,6 @@ IPCPrivateMemoryKill(int status, } } - -/****************************************************************************/ -/* IpcSemaphoreCreate(semKey, semNum, permission, semStartValue) */ -/* */ -/* - returns a semaphore identifier: */ -/* */ -/* if key doesn't exist: return a new id, status:= IpcSemIdNotExist */ -/* if key exists: return the old id, status:= IpcSemIdExist */ -/* if semNum > MAX : return # of argument, status:=IpcInvalidArgument */ -/* */ -/****************************************************************************/ - /* * Note: * XXX This should be split into two different calls. One should @@ -312,8 +300,7 @@ IpcSemaphoreCreate(IpcSemaphoreKey semKey, int semNum, int permission, int semStartValue, - int removeOnExit, - int *status) + int removeOnExit) { int i; int errStatus; @@ -321,20 +308,14 @@ IpcSemaphoreCreate(IpcSemaphoreKey semKey, u_short array[IPC_NMAXSEM]; union semun semun; - /* get a semaphore if non-existent */ /* check arguments */ if (semNum > IPC_NMAXSEM || semNum <= 0) - { - *status = IpcInvalidArgument; - return 2; /* returns the number of the invalid - * argument */ - } + return(-1); semId = semget(semKey, 0, 0); if (semId == -1) { - *status = IpcSemIdNotExist; /* there doesn't exist a semaphore */ #ifdef DEBUG_IPC EPRINTF("calling semget with %d, %d , %d\n", semKey, @@ -348,7 +329,7 @@ IpcSemaphoreCreate(IpcSemaphoreKey semKey, EPRINTF("IpcSemaphoreCreate: semget failed (%s) " "key=%d, num=%d, permission=%o", strerror(errno), semKey, semNum, permission); - proc_exit(3); + return(-1); } for (i = 0; i < semNum; i++) array[i] = semStartValue; @@ -358,22 +339,16 @@ IpcSemaphoreCreate(IpcSemaphoreKey semKey, { EPRINTF("IpcSemaphoreCreate: semctl failed (%s) id=%d", strerror(errno), semId); + semctl(semId, 0, IPC_RMID, semun); + return(-1); } if (removeOnExit) on_shmem_exit(IPCPrivateSemaphoreKill, (caddr_t) semId); - - } - else - { - /* there is a semaphore id for this key */ - *status = IpcSemIdExist; } #ifdef DEBUG_IPC - EPRINTF("\nIpcSemaphoreCreate, status %d, returns %d\n", - *status, - semId); + EPRINTF("\nIpcSemaphoreCreate, returns %d\n", semId); fflush(stdout); fflush(stderr); #endif diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index 16c5266c6c..259a1f532c 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/storage/ipc/ipci.c,v 1.30 1999/07/17 20:17:44 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/storage/ipc/ipci.c,v 1.31 1999/10/06 21:58:06 vadim Exp $ * *------------------------------------------------------------------------- */ @@ -54,17 +54,17 @@ void CreateSharedMemoryAndSemaphores(IPCKey key, int maxBackends) { int size; + extern int XLOGShmemSize(void); + extern void XLOGShmemInit(void); #ifdef HAS_TEST_AND_SET - /* --------------- - * create shared memory for slocks - * -------------- + /* + * Create shared memory for slocks */ CreateAndInitSLockMemory(IPCKeyGetSLockSharedMemoryKey(key)); #endif - /* ---------------- - * kill and create the buffer manager buffer pool (and semaphore) - * ---------------- + /* + * Kill and create the buffer manager buffer pool (and semaphore) */ CreateSpinlocks(IPCKeyGetSpinLockSemaphoreKey(key)); @@ -73,7 +73,7 @@ CreateSharedMemoryAndSemaphores(IPCKey key, int maxBackends) * moderately-accurate estimates for the big hogs, plus 100K for the * stuff that's too small to bother with estimating. */ - size = BufferShmemSize() + LockShmemSize(maxBackends); + size = BufferShmemSize() + LockShmemSize(maxBackends) + XLOGShmemSize(); #ifdef STABLE_MEMORY_STORAGE size += MMShmemSize(); #endif @@ -89,6 +89,7 @@ CreateSharedMemoryAndSemaphores(IPCKey key, int maxBackends) ShmemCreate(IPCKeyGetBufferMemoryKey(key), size); ShmemIndexReset(); InitShmem(key, size); + XLOGShmemInit(); InitBufferPool(key); /* ---------------- diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c index c23952c191..e76829ec7c 100644 --- a/src/backend/storage/ipc/shmem.c +++ b/src/backend/storage/ipc/shmem.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/storage/ipc/shmem.c,v 1.46 1999/09/24 00:24:35 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/storage/ipc/shmem.c,v 1.47 1999/10/06 21:58:06 vadim Exp $ * *------------------------------------------------------------------------- */ @@ -187,8 +187,7 @@ InitShmem(unsigned int key, unsigned int size) * bootstrap initialize spin locks so we can start to use the * allocator and shmem index. */ - if (!InitSpinLocks(ShmemBootstrap, IPCKeyGetSpinLockSemaphoreKey(key))) - return FALSE; + InitSpinLocks(); /* * We have just allocated additional space for two spinlocks. Now diff --git a/src/backend/storage/ipc/spin.c b/src/backend/storage/ipc/spin.c index 4d9bd3f71b..700f5bfaf6 100644 --- a/src/backend/storage/ipc/spin.c +++ b/src/backend/storage/ipc/spin.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/storage/ipc/Attic/spin.c,v 1.20 1999/07/16 04:59:44 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/storage/ipc/Attic/spin.c,v 1.21 1999/10/06 21:58:06 vadim Exp $ * *------------------------------------------------------------------------- */ @@ -40,15 +40,15 @@ IpcSemaphoreId SpinLockId; #ifdef HAS_TEST_AND_SET /* real spin lock implementations */ -bool +void CreateSpinlocks(IPCKey key) { /* the spin lock shared memory must have been created by now */ - return TRUE; + return; } -bool -InitSpinLocks(int init, IPCKey key) +void +InitSpinLocks(void) { extern SPINLOCK ShmemLock; extern SPINLOCK ShmemIndexLock; @@ -57,7 +57,8 @@ InitSpinLocks(int init, IPCKey key) extern SPINLOCK ProcStructLock; extern SPINLOCK SInvalLock; extern SPINLOCK OidGenLockId; - + extern SPINLOCK XidGenLockId; + extern SPINLOCK ControlFileLockId; #ifdef STABLE_MEMORY_STORAGE extern SPINLOCK MMCacheLock; @@ -71,12 +72,14 @@ InitSpinLocks(int init, IPCKey key) ProcStructLock = (SPINLOCK) PROCSTRUCTLOCKID; SInvalLock = (SPINLOCK) SINVALLOCKID; OidGenLockId = (SPINLOCK) OIDGENLOCKID; + XidGenLockId = (SPINLOCK) XIDGENLOCKID; + ControlFileLockId = (SPINLOCK) CNTLFILELOCKID; #ifdef STABLE_MEMORY_STORAGE MMCacheLock = (SPINLOCK) MMCACHELOCKID; #endif - return TRUE; + return; } #ifdef LOCKDEBUG @@ -224,55 +227,17 @@ SpinIsLocked(SPINLOCK lock) * the spinlocks * */ -bool +void CreateSpinlocks(IPCKey key) { - int status; - IpcSemaphoreId semid; - - semid = IpcSemaphoreCreate(key, MAX_SPINS, IPCProtection, - IpcSemaphoreDefaultStartValue, 1, &status); - if (status == IpcSemIdExist) - { - IpcSemaphoreKill(key); - elog(NOTICE, "Destroying old spinlock semaphore"); - semid = IpcSemaphoreCreate(key, MAX_SPINS, IPCProtection, - IpcSemaphoreDefaultStartValue, 1, &status); - } + SpinLockId = IpcSemaphoreCreate(key, MAX_SPINS, IPCProtection, + IpcSemaphoreDefaultStartValue, 1); - if (semid >= 0) - { - SpinLockId = semid; - return TRUE; - } - /* cannot create spinlocks */ - elog(FATAL, "CreateSpinlocks: cannot create spin locks"); - return FALSE; -} - -/* - * Attach to existing spinlock set - */ -static bool -AttachSpinLocks(IPCKey key) -{ - IpcSemaphoreId id; + if (SpinLockId <= 0) + elog(STOP, "CreateSpinlocks: cannot create spin locks"); - id = semget(key, MAX_SPINS, 0); - if (id < 0) - { - if (errno == EEXIST) - { - /* key is the name of someone else's semaphore */ - elog(FATAL, "AttachSpinlocks: SPIN_KEY belongs to someone else"); - } - /* cannot create spinlocks */ - elog(FATAL, "AttachSpinlocks: cannot create spin locks"); - return FALSE; - } - SpinLockId = id; - return TRUE; + return; } /* @@ -287,8 +252,8 @@ AttachSpinLocks(IPCKey key) * (SJCacheLock) for it. Same story for the main memory storage mgr. * */ -bool -InitSpinLocks(int init, IPCKey key) +void +InitSpinLocks(void) { extern SPINLOCK ShmemLock; extern SPINLOCK ShmemIndexLock; @@ -297,26 +262,14 @@ InitSpinLocks(int init, IPCKey key) extern SPINLOCK ProcStructLock; extern SPINLOCK SInvalLock; extern SPINLOCK OidGenLockId; + extern SPINLOCK XidGenLockId; + extern SPINLOCK ControlFileLockId; #ifdef STABLE_MEMORY_STORAGE extern SPINLOCK MMCacheLock; #endif - if (!init || key != IPC_PRIVATE) - { - - /* - * if bootstrap and key is IPC_PRIVATE, it means that we are - * running backend by itself. no need to attach spinlocks - */ - if (!AttachSpinLocks(key)) - { - elog(FATAL, "InitSpinLocks: couldnt attach spin locks"); - return FALSE; - } - } - /* These five (or six) spinlocks have fixed location is shmem */ ShmemLock = (SPINLOCK) SHMEMLOCKID; ShmemIndexLock = (SPINLOCK) SHMEMINDEXLOCKID; @@ -325,12 +278,14 @@ InitSpinLocks(int init, IPCKey key) ProcStructLock = (SPINLOCK) PROCSTRUCTLOCKID; SInvalLock = (SPINLOCK) SINVALLOCKID; OidGenLockId = (SPINLOCK) OIDGENLOCKID; + XidGenLockId = (SPINLOCK) XIDGENLOCKID; + ControlFileLockId = (SPINLOCK) CNTLFILELOCKID; #ifdef STABLE_MEMORY_STORAGE MMCacheLock = (SPINLOCK) MMCACHELOCKID; #endif - return TRUE; + return; } #endif /* HAS_TEST_AND_SET */ diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c index 159edf0549..0270b0e116 100644 --- a/src/backend/storage/lmgr/proc.c +++ b/src/backend/storage/lmgr/proc.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/storage/lmgr/proc.c,v 1.61 1999/09/24 00:24:41 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/storage/lmgr/proc.c,v 1.62 1999/10/06 21:58:07 vadim Exp $ * *------------------------------------------------------------------------- */ @@ -46,7 +46,7 @@ * This is so that we can support more backends. (system-wide semaphore * sets run out pretty fast.) -ay 4/95 * - * $Header: /cvsroot/pgsql/src/backend/storage/lmgr/proc.c,v 1.61 1999/09/24 00:24:41 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/storage/lmgr/proc.c,v 1.62 1999/10/06 21:58:07 vadim Exp $ */ #include #include @@ -70,7 +70,7 @@ #include "storage/proc.h" #include "utils/trace.h" -static void HandleDeadLock(int sig); +void HandleDeadLock(SIGNAL_ARGS); static void ProcFreeAllSemaphores(void); #define DeadlockCheckTimer pg_options[OPT_DEADLOCKTIMEOUT] @@ -84,12 +84,6 @@ static void ProcFreeAllSemaphores(void); */ SPINLOCK ProcStructLock; -/* - * For cleanup routines. Don't cleanup if the initialization - * has not happened. - */ -static bool ProcInitialized = FALSE; - static PROC_HDR *ProcGlobal = NULL; PROC *MyProc = NULL; @@ -167,8 +161,9 @@ InitProcGlobal(IPCKey key, int maxBackends) PROC_NSEMS_PER_SET, IPCProtection, IpcSemaphoreDefaultStartValue, - 0, - &semstat); + 0); + if (semId < 0) + elog(FATAL, "InitProcGlobal: IpcSemaphoreCreate failed"); /* mark this sema set allocated */ ProcGlobal->freeSemMap[i] = (1 << PROC_NSEMS_PER_SET); } @@ -189,12 +184,6 @@ InitProcess(IPCKey key) unsigned long location, myOffset; - /* ------------------ - * Routine called if deadlock timer goes off. See ProcSleep() - * ------------------ - */ - pqsignal(SIGALRM, HandleDeadLock); - SpinAcquire(ProcStructLock); /* attach to the free list */ @@ -203,7 +192,7 @@ InitProcess(IPCKey key) if (!found) { /* this should not happen. InitProcGlobal() is called before this. */ - elog(ERROR, "InitProcess: Proc Header uninitialized"); + elog(STOP, "InitProcess: Proc Header uninitialized"); } if (MyProc != NULL) @@ -271,8 +260,7 @@ InitProcess(IPCKey key) PROC_NSEMS_PER_SET, IPCProtection, IpcSemaphoreDefaultStartValue, - 0, - &semstat); + 0); /* * we might be reusing a semaphore that belongs to a dead backend. @@ -316,14 +304,12 @@ InitProcess(IPCKey key) */ location = MAKE_OFFSET(MyProc); if ((!ShmemPIDLookup(MyProcPid, &location)) || (location != MAKE_OFFSET(MyProc))) - elog(FATAL, "InitProc: ShmemPID table broken"); + elog(STOP, "InitProc: ShmemPID table broken"); MyProc->errType = NO_ERROR; SHMQueueElemInit(&(MyProc->links)); on_shmem_exit(ProcKill, (caddr_t) MyProcPid); - - ProcInitialized = TRUE; } /* @@ -755,8 +741,8 @@ ProcAddLock(SHM_QUEUE *elem) * up my semaphore. * -------------------- */ -static void -HandleDeadLock(int sig) +void +HandleDeadLock(SIGNAL_ARGS) { LOCK *mywaitlock; diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index 2a3e7e7730..29486bd6f3 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.130 1999/09/29 16:06:10 wieck Exp $ + * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.131 1999/10/06 21:58:08 vadim Exp $ * * NOTES * this is the "main" module of the postgres backend and @@ -58,6 +58,7 @@ #include "tcop/pquery.h" #include "tcop/tcopprot.h" #include "tcop/utility.h" +#include "storage/proc.h" #include "utils/ps_status.h" #include "utils/temprel.h" #include "utils/trace.h" @@ -98,11 +99,20 @@ */ /*static bool EnableRewrite = true; , never changes why have it*/ -CommandDest whereToSendOutput; +CommandDest whereToSendOutput = Debug; /* Define status buffer needed by PS_SET_STATUS */ PS_DEFINE_BUFFER; +extern void BaseInit(void); +extern void StartupXLOG(void); +extern void ShutdownXLOG(void); + +extern void HandleDeadLock(SIGNAL_ARGS); + +extern char XLogDir[]; +extern char ControlFilePath[]; + extern int lockingOff; extern int NBuffers; @@ -115,21 +125,8 @@ char relname[80]; /* current relation name */ /* note: these declarations had better match tcopprot.h */ DLLIMPORT sigjmp_buf Warn_restart; -bool InError = true; - -/* - * Note: InError is a flag to elog() telling whether it is safe to longjmp - * back to PostgresMain. It is "false", allowing an error longjmp, during - * normal processing. It is "true" during startup, when we have not yet - * set the Warn_restart jmp_buf, and also "true" in the interval when we - * have executed a longjmp back to PostgresMain and not yet finished cleaning - * up after the error. In either case, elog(ERROR) should be treated as a - * fatal exit condition rather than attempting to recover --- since there is - * noplace to recover to in the first case, and we don't want to risk an - * infinite loop of "error recoveries" in the second case. - * - * Therefore, InError starts out "true" at program load time, as shown above. - */ +bool InError = false; +bool ExitAfterAbort = false; extern int NBuffers; @@ -773,6 +770,7 @@ handle_warn(SIGNAL_ARGS) void quickdie(SIGNAL_ARGS) { + PG_SETMASK(&BlockSig); elog(NOTICE, "Message from PostgreSQL backend:" "\n\tThe Postmaster has informed me that some other backend" " died abnormally and possibly corrupted shared memory." @@ -787,13 +785,25 @@ quickdie(SIGNAL_ARGS) * storage. Just nail the windows shut and get out of town. */ - exit(0); + exit(1); } +/* + * Abort transaction and exit + */ void die(SIGNAL_ARGS) { - ExitPostgres(0); + PG_SETMASK(&BlockSig); + /* + * If ERROR/FATAL is in progress... + */ + if (InError) + { + ExitAfterAbort = true; + return; + } + elog(FATAL, "The system is shutting down"); } /* signal handler for floating point exception */ @@ -907,6 +917,8 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[]) #endif DataDir = getenv("PGDATA"); + SetProcessingMode(InitProcessing); + /* * Try to get initial values for date styles and formats. Does not do * a complete job, but should be good enough for backend. Cannot call @@ -1265,10 +1277,68 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[]) break; } - /* ---------------- - * get user name (needed now in case it is the default database name) - * and check command line validity - * ---------------- + if (ShowStats && + (ShowParserStats || ShowPlannerStats || ShowExecutorStats)) + { + fprintf(stderr, "-s can not be used together with -t.\n"); + proc_exit(1); + } + + if (!DataDir) + { + fprintf(stderr, "%s does not know where to find the database system " + "data. You must specify the directory that contains the " + "database system either by specifying the -D invocation " + "option or by setting the PGDATA environment variable.\n\n", + argv[0]); + proc_exit(1); + } + + /* + * 1. Set BlockSig and UnBlockSig masks. + * 2. Set up signal handlers. + * 3. Allow only SIGUSR1 signal (we never block it) + * during initialization. + * + * Note that postmaster already blocked ALL signals to make us happy. + */ + if (!IsUnderPostmaster) + { + PG_INITMASK(); + PG_SETMASK(&BlockSig); + } + +#ifdef HAVE_SIGPROCMASK + sigdelset(&BlockSig, SIGUSR1); +#else + BlockSig &= ~(sigmask(SIGUSR1)); +#endif + + pqsignal(SIGHUP, read_pg_options); /* update pg_options from file */ + pqsignal(SIGINT, QueryCancelHandler); /* cancel current query */ + pqsignal(SIGQUIT, handle_warn); /* handle error */ + pqsignal(SIGTERM, die); + pqsignal(SIGALRM, HandleDeadLock); + /* + * Ignore failure to write to frontend. Note: if frontend closes + * connection, we will notice it and exit cleanly when control next + * returns to outer loop. This seems safer than forcing exit in the + * midst of output during who-knows-what operation... + */ + pqsignal(SIGPIPE, SIG_IGN); + pqsignal(SIGUSR1, quickdie); + pqsignal(SIGUSR2, Async_NotifyHandler); /* flush also sinval cache */ + pqsignal(SIGFPE, FloatExceptionHandler); + pqsignal(SIGCHLD, SIG_IGN); /* ignored, sent by LockOwners */ + pqsignal(SIGTTIN, SIG_DFL); + pqsignal(SIGTTOU, SIG_DFL); + pqsignal(SIGCONT, SIG_DFL); + + PG_SETMASK(&BlockSig); /* block everything except SIGUSR1 */ + + /* + * Get user name (needed now in case it is the default database name) + * and check command line validity */ SetPgUserName(); userName = GetPgUserName(); @@ -1281,10 +1351,14 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[]) usage(argv[0]); proc_exit(1); } + pq_init(); /* initialize libpq at backend startup */ + whereToSendOutput = Remote; + BaseInit(); } else { /* interactive case: database name can be last arg on command line */ + whereToSendOutput = Debug; if (errs || argc - optind > 1) { usage(argv[0]); @@ -1298,23 +1372,10 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[]) argv[0]); proc_exit(1); } - } - - if (ShowStats && - (ShowParserStats || ShowPlannerStats || ShowExecutorStats)) - { - fprintf(stderr, "-s can not be used together with -t.\n"); - proc_exit(1); - } - - if (!DataDir) - { - fprintf(stderr, "%s does not know where to find the database system " - "data. You must specify the directory that contains the " - "database system either by specifying the -D invocation " - "option or by setting the PGDATA environment variable.\n\n", - argv[0]); - proc_exit(1); + BaseInit(); + sprintf(XLogDir, "%s%cpg_xlog", DataDir, SEP_CHAR); + sprintf(ControlFilePath, "%s%cpg_control", DataDir, SEP_CHAR); + StartupXLOG(); } /* @@ -1367,19 +1428,15 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[]) remote_info = remote_host = "unknown"; break; } - } - - /* ---------------- - * set process params for ps - * ---------------- - */ - if (IsUnderPostmaster) - { + /* + * Set process params for ps + */ PS_INIT_STATUS(real_argc, real_argv, argv[0], remote_info, userName, DBName); PS_SET_STATUS("startup"); } + /* ---------------- * print flags * ---------------- @@ -1409,23 +1466,10 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[]) } } - /* ---------------- - * initialize I/O - * ---------------- - */ - if (IsUnderPostmaster) - { - pq_init(); /* initialize libpq at backend startup */ - whereToSendOutput = Remote; - } - else - whereToSendOutput = Debug; - /* ---------------- - * general initialization - * ---------------- + /* + * general initialization */ - SetProcessingMode(InitProcessing); if (Verbose) TPRINTF(TRACE_VERBOSE, "InitPostgres"); @@ -1445,30 +1489,9 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[]) parser_input = makeStringInfo(); /* initialize input buffer */ - /* ---------------- - * Set up handler for cancel-request signal, and - * send this backend's cancellation info to the frontend. - * This should not be done until we are sure startup is successful. - * ---------------- - */ - - pqsignal(SIGHUP, read_pg_options); /* update pg_options from file */ - pqsignal(SIGINT, QueryCancelHandler); /* cancel current query */ - pqsignal(SIGQUIT, handle_warn); /* handle error */ - pqsignal(SIGTERM, die); - pqsignal(SIGPIPE, SIG_IGN); /* ignore failure to write to frontend */ - - /* - * Note: if frontend closes connection, we will notice it and exit - * cleanly when control next returns to outer loop. This seems safer - * than forcing exit in the midst of output during who-knows-what - * operation... + /* + * Send this backend's cancellation info to the frontend. */ - pqsignal(SIGUSR1, quickdie); - pqsignal(SIGUSR2, Async_NotifyHandler); /* flush also sinval cache */ - pqsignal(SIGCHLD, SIG_IGN); /* ignored, sent by LockOwners */ - pqsignal(SIGFPE, FloatExceptionHandler); - if (whereToSendOutput == Remote && PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2) { @@ -1485,40 +1508,41 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[]) if (!IsUnderPostmaster) { puts("\nPOSTGRES backend interactive interface "); - puts("$Revision: 1.130 $ $Date: 1999/09/29 16:06:10 $\n"); + puts("$Revision: 1.131 $ $Date: 1999/10/06 21:58:08 $\n"); } - /* ---------------- + /* * Initialize the deferred trigger manager - * ---------------- */ if (DeferredTriggerInit() != 0) ExitPostgres(1); - /* ---------------- - * POSTGRES main processing loop begins here - * - * if an exception is encountered, processing resumes here - * so we abort the current transaction and start a new one. + /* + * POSTGRES main processing loop begins here * - * Note: elog(ERROR) does a siglongjmp() to transfer control here. - * See comments with the declaration of InError, above. - * ---------------- + * If an exception is encountered, processing resumes here + * so we abort the current transaction and start a new one. */ + SetProcessingMode(NormalProcessing); + if (sigsetjmp(Warn_restart, 1) != 0) { - InError = true; - time(&tim); if (Verbose) TPRINTF(TRACE_VERBOSE, "AbortCurrentTransaction"); AbortCurrentTransaction(); + InError = false; + if (ExitAfterAbort) + { + ProcReleaseLocks(); /* Just to be sure... */ + ExitPostgres(0); + } } - InError = false; + PG_SETMASK(&UnBlockSig); /* * Non-error queries loop here. @@ -1636,6 +1660,8 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[]) */ case 'X': case EOF: + if (!IsUnderPostmaster) + ShutdownXLOG(); pq_close(); proc_exit(0); break; diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c index 11cf762e7d..342a1029d1 100644 --- a/src/backend/utils/error/elog.c +++ b/src/backend/utils/error/elog.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/error/elog.c,v 1.48 1999/09/11 19:06:31 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/error/elog.c,v 1.49 1999/10/06 21:58:09 vadim Exp $ * *------------------------------------------------------------------------- */ @@ -37,6 +37,7 @@ extern int errno; extern int sys_nerr; +extern CommandDest whereToSendOutput; #ifdef USE_SYSLOG /* @@ -107,6 +108,19 @@ elog(int lev, const char *fmt, ...) if (lev <= DEBUG && Debugfile < 0) return; /* ignore debug msgs if noplace to send */ + if (lev == ERROR || lev == FATAL) + { + if (IsInitProcessingMode()) + { + extern TransactionState CurrentTransactionState; + + if (CurrentTransactionState->state != TRANS_DEFAULT && + CurrentTransactionState->state != TRANS_DISABLED) + abort(); + lev = FATAL; + } + } + /* choose message prefix and indent level */ switch (lev) { @@ -304,7 +318,7 @@ elog(int lev, const char *fmt, ...) #ifndef PG_STANDALONE - if (lev > DEBUG && IsUnderPostmaster) + if (lev > DEBUG && whereToSendOutput == Remote) { /* Send IPC message to the front-end program */ char msgtype; @@ -336,7 +350,7 @@ elog(int lev, const char *fmt, ...) pq_flush(); } - if (lev > DEBUG && ! IsUnderPostmaster) + if (lev > DEBUG && whereToSendOutput != Remote) { /* We are running as an interactive backend, so just send * the message to stderr. @@ -355,36 +369,29 @@ elog(int lev, const char *fmt, ...) /* * Perform error recovery action as specified by lev. */ - if (lev == ERROR) + if (lev == ERROR || lev == FATAL) { if (InError) { /* error reported during error recovery; don't loop forever */ elog(REALLYFATAL, "elog: error during error recovery, giving up!"); } - /* exit to main loop */ + InError = true; ProcReleaseSpins(NULL); /* get rid of spinlocks we hold */ + if (lev == FATAL) + { + if (IsInitProcessingMode()) + ExitPostgres(0); + ExitAfterAbort = true; + } + /* exit to main loop */ siglongjmp(Warn_restart, 1); } - if (lev == FATAL) - { - /* - * Assume that if we have detected the failure we can exit with a - * normal exit status. This will prevent the postmaster from - * cleaning up when it's not needed. - */ - fflush(stdout); - fflush(stderr); - ProcReleaseSpins(NULL); /* get rid of spinlocks we hold */ - ProcReleaseLocks(); /* get rid of real locks we hold */ - proc_exit(0); - } - if (lev > FATAL) { /* - * Serious crash time. Postmaster will observe nonzero + * Serious crash time. Postmaster will observe nonzero * process exit status and kill the other backends too. */ fflush(stdout); diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index 88baaf4a89..11ac358026 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/init/miscinit.c,v 1.34 1999/07/17 20:18:08 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/init/miscinit.c,v 1.35 1999/10/06 21:58:10 vadim Exp $ * *------------------------------------------------------------------------- */ @@ -133,20 +133,7 @@ StatusPostmasterExit(int status) * processing mode support stuff (used to be in pmod.c) * ---------------------------------------------------------------- */ -static ProcessingMode Mode = NoProcessing; - -#ifdef NOT_USED -/* - * IsNoProcessingMode - * True iff processing mode is NoProcessing. - */ -bool -IsNoProcessingMode() -{ - return (bool) (Mode == NoProcessing); -} - -#endif +static ProcessingMode Mode = InitProcessing; /* * IsBootstrapProcessingMode @@ -186,13 +173,13 @@ IsNormalProcessingMode() * BadArg if called with invalid mode. * * Note: - * Mode is NoProcessing before the first time this is called. + * Mode is InitProcessing before the first time this is called. */ void SetProcessingMode(ProcessingMode mode) { - AssertArg(mode == NoProcessing || mode == BootstrapProcessing || - mode == InitProcessing || mode == NormalProcessing); + AssertArg(mode == BootstrapProcessing || mode == InitProcessing || + mode == NormalProcessing); Mode = mode; } diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c index 1285c23e84..20db5c34bb 100644 --- a/src/backend/utils/init/postinit.c +++ b/src/backend/utils/init/postinit.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/init/postinit.c,v 1.50 1999/09/28 11:41:09 vadim Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/init/postinit.c,v 1.51 1999/10/06 21:58:10 vadim Exp $ * * NOTES * InitPostgres() is the function called from PostgresMain @@ -53,12 +53,13 @@ #include "mb/pg_wchar.h" #endif +void BaseInit(void); + static void VerifySystemDatabase(void); static void VerifyMyDatabase(void); static void ReverifyMyDatabase(char *name); static void InitCommunication(void); static void InitMyDatabaseInfo(char *name); -static void InitStdio(void); static void InitUserid(void); @@ -385,37 +386,6 @@ InitCommunication() { if (MyBackendTag == -1) elog(FATAL, "InitCommunication: missing POSTID"); - - /* - * Enable this if you are trying to force the backend to run as if - * it is running under the postmaster. - * - * This goto forces Postgres to attach to shared memory instead of - * using malloc'ed memory (which is the normal behavior if run - * directly). - * - * To enable emulation, run the following shell commands (in addition - * to enabling this goto) - * - * % setenv POSTID 1 % setenv POSTPORT 4321 % setenv IPC_KEY 4321000 - * % postmaster & % kill -9 %1 - * - * Upon doing this, Postmaster will have allocated the shared memory - * resources that Postgres will attach to if you enable - * EMULATE_UNDER_POSTMASTER. - * - * This comment may well age with time - it is current as of 8 - * January 1990 - * - * Greg - */ - -#ifdef EMULATE_UNDER_POSTMASTER - - goto forcesharedmemory; - -#endif - } else if (IsUnderPostmaster) { @@ -439,12 +409,6 @@ InitCommunication() * initialize shared memory and semaphores appropriately. * ---------------- */ -#ifdef EMULATE_UNDER_POSTMASTER - -forcesharedmemory: - -#endif - if (!IsUnderPostmaster) /* postmaster already did this */ { PostgresIpcKey = key; @@ -452,21 +416,6 @@ forcesharedmemory: } } - -/* -------------------------------- - * InitStdio - * - * this routine consists of a bunch of code fragments - * that used to be randomly scattered through cinit(). - * they all seem to do stuff associated with io. - * -------------------------------- - */ -static void -InitStdio() -{ - DebugFileOpen(); -} - /* -------------------------------- * InitPostgres * Initialize POSTGRES. @@ -477,8 +426,6 @@ InitStdio() */ extern int NBuffers; -bool PostgresIsInitialized = false; - int lockingOff = 0; /* backend -L switch */ /* @@ -488,37 +435,11 @@ InitPostgres(char *name) /* database name */ { bool bootstrap; /* true if BootstrapProcessing */ - /* ---------------- - * see if we're running in BootstrapProcessing mode - * ---------------- + /* + * See if we're running in BootstrapProcessing mode */ bootstrap = IsBootstrapProcessingMode(); - /* ---------------- - * turn on the exception handler. Note: we cannot use elog, Assert, - * AssertState, etc. until after exception handling is on. - * ---------------- - */ - EnableExceptionHandling(true); - - /* ---------------- - * A stupid check to make sure we don't call this more than once. - * But things like ReinitPostgres() get around this by just diddling - * the PostgresIsInitialized flag. - * ---------------- - */ - AssertState(!PostgresIsInitialized); - - /* ---------------- - * Memory system initialization. - * (we may call palloc after EnableMemoryContext()) - * - * Note EnableMemoryContext() must happen before EnablePortalManager(). - * ---------------- - */ - EnableMemoryContext(true); /* initializes the "top context" */ - EnablePortalManager(true); /* memory for portal/transaction stuff */ - /* ---------------- * initialize the backend local portal stack used by * internal PQ function calls. see src/lib/libpq/be-dumpdata.c @@ -528,14 +449,6 @@ InitPostgres(char *name) /* database name */ */ be_portalinit(); - /* ---------------- - * attach to shared memory and semaphores, and initialize our - * input/output/debugging file descriptors. - * ---------------- - */ - InitCommunication(); - InitStdio(); - /* * initialize the local buffer manager */ @@ -574,13 +487,9 @@ InitPostgres(char *name) /* database name */ * Will try that, but may not work... - thomas 1997-11-01 */ - /* Does not touch files (?) - thomas 1997-11-01 */ - smgrinit(); - - /* ---------------- - * initialize the transaction system and the relation descriptor cache. - * Note we have to make certain the lock manager is off while we do this. - * ---------------- + /* + * Initialize the transaction system and the relation descriptor cache. + * Note we have to make certain the lock manager is off while we do this. */ AmiTransactionOverride(IsBootstrapProcessingMode()); LockDisable(true); @@ -598,20 +507,18 @@ InitPostgres(char *name) /* database name */ LockDisable(false); - /* ---------------- + /* * Set up my per-backend PROC struct in shared memory. - * ---------------- */ InitProcess(PostgresIpcKey); - /* ---------------- - * Initialize my entry in the shared-invalidation manager's - * array of per-backend data. (Formerly this came before - * InitProcess, but now it must happen after, because it uses - * MyProc.) Once I have done this, I am visible to other backends! + /* + * Initialize my entry in the shared-invalidation manager's + * array of per-backend data. (Formerly this came before + * InitProcess, but now it must happen after, because it uses + * MyProc.) Once I have done this, I am visible to other backends! * - * Sets up MyBackendId, a unique backend identifier. - * ---------------- + * Sets up MyBackendId, a unique backend identifier. */ InitSharedInvalidationState(); @@ -622,16 +529,14 @@ InitPostgres(char *name) /* database name */ MyBackendId); } - /* ---------------- - * initialize the access methods. - * Does not touch files (?) - thomas 1997-11-01 - * ---------------- + /* + * Initialize the access methods. + * Does not touch files (?) - thomas 1997-11-01 */ initam(); - /* ---------------- - * initialize all the system catalog caches. - * ---------------- + /* + * Initialize all the system catalog caches. */ zerocaches(); @@ -641,34 +546,19 @@ InitPostgres(char *name) /* database name */ */ InitCatalogCache(); - /* ---------------- - * set ourselves to the proper user id and figure out our postgres - * user id. If we ever add security so that we check for valid - * postgres users, we might do it here. - * ---------------- + /* + * Set ourselves to the proper user id and figure out our postgres + * user id. If we ever add security so that we check for valid + * postgres users, we might do it here. */ InitUserid(); - /* ---------------- - * initialize local data in cache invalidation stuff - * ---------------- + /* + * Initialize local data in cache invalidation stuff */ if (!bootstrap) InitLocalInvalidateData(); - /* ---------------- - * ok, all done, now let's make sure we don't do it again. - * ---------------- - */ - PostgresIsInitialized = true; - - /* ---------------- - * Done with "InitPostgres", now change to NormalProcessing unless - * we're in BootstrapProcessing mode. - * ---------------- - */ - if (!bootstrap) - SetProcessingMode(NormalProcessing); if (lockingOff) LockDisable(true); @@ -680,3 +570,30 @@ InitPostgres(char *name) /* database name */ if (!bootstrap) ReverifyMyDatabase(name); } + +void +BaseInit(void) +{ + + /* + * Turn on the exception handler. Note: we cannot use elog, Assert, + * AssertState, etc. until after exception handling is on. + */ + EnableExceptionHandling(true); + + /* + * Memory system initialization - we may call palloc after + * EnableMemoryContext()). Note that EnableMemoryContext() + * must happen before EnablePortalManager(). + */ + EnableMemoryContext(true); /* initializes the "top context" */ + EnablePortalManager(true); /* memory for portal/transaction stuff */ + + /* + * Attach to shared memory and semaphores, and initialize our + * input/output/debugging file descriptors. + */ + InitCommunication(); + DebugFileOpen(); + smgrinit(); +} diff --git a/src/backend/utils/time/tqual.c b/src/backend/utils/time/tqual.c index 3c2a9c71e8..97080e010f 100644 --- a/src/backend/utils/time/tqual.c +++ b/src/backend/utils/time/tqual.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/time/tqual.c,v 1.31 1999/07/15 22:40:15 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/time/tqual.c,v 1.32 1999/10/06 21:58:11 vadim Exp $ * *------------------------------------------------------------------------- */ @@ -18,8 +18,6 @@ #include "utils/tqual.h" -extern bool PostgresIsInitialized; - SnapshotData SnapshotDirtyData; Snapshot SnapshotDirty = &SnapshotDirtyData; @@ -194,17 +192,6 @@ HeapTupleSatisfiesNow(HeapTupleHeader tuple) if (AMI_OVERRIDE) return true; - /* - * If the transaction system isn't yet initialized, then we assume - * that transactions committed. We only look at system catalogs - * during startup, so this is less awful than it seems, but it's still - * pretty awful. - */ - - if (!PostgresIsInitialized) - return ((bool) (TransactionIdIsValid(tuple->t_xmin) && - !TransactionIdIsValid(tuple->t_xmax))); - if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED)) { if (tuple->t_infomask & HEAP_XMIN_INVALID) diff --git a/src/bin/initdb/initdb.sh b/src/bin/initdb/initdb.sh index 139c967b05..63733a5f7e 100644 --- a/src/bin/initdb/initdb.sh +++ b/src/bin/initdb/initdb.sh @@ -26,7 +26,7 @@ # # # IDENTIFICATION -# $Header: /cvsroot/pgsql/src/bin/initdb/Attic/initdb.sh,v 1.60 1999/05/20 16:50:06 wieck Exp $ +# $Header: /cvsroot/pgsql/src/bin/initdb/Attic/initdb.sh,v 1.61 1999/10/06 21:58:12 vadim Exp $ # #------------------------------------------------------------------------- @@ -300,6 +300,12 @@ else mkdir $PGDATA/base if [ $? -ne 0 ]; then exit 5; fi fi + if [ ! -d $PGDATA/pg_xlog ]; then + echo "Creating Postgres database XLOG directory $PGDATA/pg_xlog" + echo + mkdir $PGDATA/pg_xlog + if [ $? -ne 0 ]; then exit 5; fi + fi fi #---------------------------------------------------------------------------- @@ -316,6 +322,7 @@ else fi BACKENDARGS="-boot -C -F -D$PGDATA $BACKEND_TALK_ARG" +FIRSTRUN="-boot -x -C -F -D$PGDATA $BACKEND_TALK_ARG" echo "Creating template database in $PGDATA/base/template1" [ "$debug" -ne 0 ] && echo "Running: postgres $BACKENDARGS template1" @@ -323,7 +330,7 @@ echo "Creating template database in $PGDATA/base/template1" cat $TEMPLATE \ | sed -e "s/postgres PGUID/$POSTGRES_SUPERUSERNAME $POSTGRES_SUPERUID/" \ -e "s/PGUID/$POSTGRES_SUPERUID/" \ -| postgres $BACKENDARGS template1 +| postgres $FIRSTRUN template1 if [ $? -ne 0 ]; then echo "$CMDNAME: could not create template database" diff --git a/src/include/libpq/pqsignal.h b/src/include/libpq/pqsignal.h index 08da41d652..be521ffe8d 100644 --- a/src/include/libpq/pqsignal.h +++ b/src/include/libpq/pqsignal.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: pqsignal.h,v 1.9 1999/02/13 23:21:36 momjian Exp $ + * $Id: pqsignal.h,v 1.10 1999/10/06 21:58:16 vadim Exp $ * * NOTES * This shouldn't be in libpq, but the monitor and some other @@ -17,6 +17,28 @@ #ifndef PQSIGNAL_H #define PQSIGNAL_H +#ifdef HAVE_SIGPROCMASK +extern sigset_t UnBlockSig, + BlockSig; +#define PG_INITMASK() ( \ + sigemptyset(&UnBlockSig), \ + sigfillset(&BlockSig) \ + ) +#define PG_SETMASK(mask) sigprocmask(SIG_SETMASK, mask, NULL) +#else +extern int UnBlockSig, + BlockSig; +#define PG_INITMASK() ( \ + UnBlockSig = 0, \ + BlockSig = sigmask(SIGHUP) | sigmask(SIGQUIT) | \ + sigmask(SIGTERM) | sigmask(SIGALRM) | \ + sigmask(SIGINT) | sigmask(SIGUSR1) | \ + sigmask(SIGUSR2) | sigmask(SIGCHLD) | \ + sigmask(SIGWINCH) | sigmask(SIGFPE) \ + ) +#define PG_SETMASK(mask) sigsetmask(*((int*)(mask))) +#endif + typedef void (*pqsigfunc) (int); extern pqsigfunc pqsignal(int signo, pqsigfunc func); diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h index 12eb3f87ba..9faaac1bde 100644 --- a/src/include/miscadmin.h +++ b/src/include/miscadmin.h @@ -11,7 +11,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: miscadmin.h,v 1.42 1999/09/27 20:27:26 momjian Exp $ + * $Id: miscadmin.h,v 1.43 1999/10/06 21:58:13 vadim Exp $ * * NOTES * some of the information in this file will be moved to @@ -143,28 +143,25 @@ extern int CheckPathAccess(char *path, char *name, int open_mode); *****************************************************************************/ /* * Description: - * There are four processing modes in POSTGRES. They are NoProcessing - * or "none," BootstrapProcessing or "bootstrap," InitProcessing or + * There are three processing modes in POSTGRES. They are + * "BootstrapProcessing or "bootstrap," InitProcessing or * "initialization," and NormalProcessing or "normal." * - * If a POSTGRES binary is in normal mode, then all code may be executed - * normally. In the none mode, only bookkeeping code may be called. In - * particular, access method calls may not occur in this mode since the - * execution state is outside a transaction. - * - * The final two processing modes are used during special times. When the + * The first two processing modes are used during special times. When the * system state indicates bootstrap processing, transactions are all given - * transaction id "one" and are consequently guarenteed to commit. This mode + * transaction id "one" and are consequently guarenteed to commit. This mode * is used during the initial generation of template databases. * - * Finally, the execution state is in initialization mode until all normal - * initialization is complete. Some code behaves differently when executed in - * this mode to enable system bootstrapping. + * Initialization mode until all normal initialization is complete. + * Some code behaves differently when executed in this mode to enable + * system bootstrapping. + * + * If a POSTGRES binary is in normal mode, then all code may be executed + * normally. */ typedef enum ProcessingMode { - NoProcessing, /* "nothing" can be done */ BootstrapProcessing, /* bootstrap creation of template database */ InitProcessing, /* initializing system */ NormalProcessing /* normal processing */ diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h index 8ae09e1990..a123448e2a 100644 --- a/src/include/storage/ipc.h +++ b/src/include/storage/ipc.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: ipc.h,v 1.35 1999/07/15 23:04:10 momjian Exp $ + * $Id: ipc.h,v 1.36 1999/10/06 21:58:17 vadim Exp $ * * NOTES * This file is very architecture-specific. This stuff should actually @@ -79,7 +79,7 @@ extern void on_exit_reset(void); extern IpcSemaphoreId IpcSemaphoreCreate(IpcSemaphoreKey semKey, int semNum, int permission, int semStartValue, - int removeOnExit, int *status); + int removeOnExit); extern void IpcSemaphoreKill(IpcSemaphoreKey key); extern void IpcSemaphoreLock(IpcSemaphoreId semId, int sem, int lock); extern void IpcSemaphoreUnlock(IpcSemaphoreId semId, int sem, int lock); @@ -105,6 +105,8 @@ typedef enum _LockId_ BUFMGRLOCKID, LOCKLOCKID, OIDGENLOCKID, + XIDGENLOCKID, + CNTLFILELOCKID, SHMEMLOCKID, SHMEMINDEXLOCKID, LOCKMGRLOCKID, @@ -147,6 +149,8 @@ typedef enum _LockId_ PROCSTRUCTLOCKID, OIDGENLOCKID, + XIDGENLOCKID, + CNTLFILELOCKID, FIRSTFREELOCKID } _LockId_; diff --git a/src/include/storage/spin.h b/src/include/storage/spin.h index be976c16b2..858f6e791f 100644 --- a/src/include/storage/spin.h +++ b/src/include/storage/spin.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: spin.h,v 1.9 1999/07/15 23:04:16 momjian Exp $ + * $Id: spin.h,v 1.10 1999/10/06 21:58:17 vadim Exp $ * *------------------------------------------------------------------------- */ @@ -27,8 +27,8 @@ typedef int SPINLOCK; -extern bool CreateSpinlocks(IPCKey key); -extern bool InitSpinLocks(int init, IPCKey key); +extern void CreateSpinlocks(IPCKey key); +extern void InitSpinLocks(void); extern void SpinAcquire(SPINLOCK lockid); extern void SpinRelease(SPINLOCK lockid); diff --git a/src/include/tcop/tcopprot.h b/src/include/tcop/tcopprot.h index d0351a5f95..65bad2234e 100644 --- a/src/include/tcop/tcopprot.h +++ b/src/include/tcop/tcopprot.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: tcopprot.h,v 1.21 1999/05/26 12:56:58 momjian Exp $ + * $Id: tcopprot.h,v 1.22 1999/10/06 21:58:18 vadim Exp $ * * OLD COMMENTS * This file was created so that other c files could get the two @@ -38,6 +38,7 @@ #endif extern DLLIMPORT sigjmp_buf Warn_restart; extern bool InError; +extern bool ExitAfterAbort; #ifndef BOOTSTRAP_INCLUDE extern List *pg_parse_and_plan(char *query_string, Oid *typev, int nargs,