XLOG (also known as WAL -:)) Bootstrap/Startup/Shutdown.
First step in cleaning up backend initialization code. Fix for FATAL: now FATAL is ERROR + exit.
This commit is contained in:
parent
9dcd8c528f
commit
4793740367
@ -14,12 +14,14 @@
|
|||||||
|
|
||||||
void UpdateControlFile(void);
|
void UpdateControlFile(void);
|
||||||
int XLOGShmemSize(void);
|
int XLOGShmemSize(void);
|
||||||
|
void XLOGShmemInit(void);
|
||||||
void BootStrapXLOG(void);
|
void BootStrapXLOG(void);
|
||||||
void StartupXLOG(void);
|
void StartupXLOG(void);
|
||||||
|
void ShutdownXLOG(void);
|
||||||
void CreateCheckPoint(bool shutdown);
|
void CreateCheckPoint(bool shutdown);
|
||||||
|
|
||||||
char *XLogDir = NULL;
|
char XLogDir[MAXPGPATH+1];
|
||||||
char *ControlFilePath = NULL;
|
char ControlFilePath[MAXPGPATH+1];
|
||||||
uint32 XLOGbuffers = 0;
|
uint32 XLOGbuffers = 0;
|
||||||
XLogRecPtr MyLastRecPtr = {0, 0};
|
XLogRecPtr MyLastRecPtr = {0, 0};
|
||||||
bool StopIfError = false;
|
bool StopIfError = false;
|
||||||
@ -81,7 +83,8 @@ static XLogCtlData *XLogCtl = NULL;
|
|||||||
|
|
||||||
typedef enum DBState
|
typedef enum DBState
|
||||||
{
|
{
|
||||||
DB_SHUTDOWNED = 1,
|
DB_STARTUP = 0,
|
||||||
|
DB_SHUTDOWNED,
|
||||||
DB_SHUTDOWNING,
|
DB_SHUTDOWNING,
|
||||||
DB_IN_RECOVERY,
|
DB_IN_RECOVERY,
|
||||||
DB_IN_PRODUCTION
|
DB_IN_PRODUCTION
|
||||||
@ -114,9 +117,9 @@ typedef struct CheckPoint
|
|||||||
} 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 XLogLastSeg (0xffffffff / XLogSegSize)
|
||||||
#define XLogFileSize (XLogLastSeg * XLogSegSize)
|
#define XLogFileSize (XLogLastSeg * XLogSegSize)
|
||||||
|
|
||||||
@ -166,6 +169,7 @@ static void XLogWrite(char *buffer);
|
|||||||
static int XLogFileInit(uint32 log, uint32 seg);
|
static int XLogFileInit(uint32 log, uint32 seg);
|
||||||
static int XLogFileOpen(uint32 log, uint32 seg, bool econt);
|
static int XLogFileOpen(uint32 log, uint32 seg, bool econt);
|
||||||
static XLogRecord *ReadRecord(XLogRecPtr *RecPtr, char *buffer);
|
static XLogRecord *ReadRecord(XLogRecPtr *RecPtr, char *buffer);
|
||||||
|
static char *str_time(time_t tnow);
|
||||||
|
|
||||||
static XLgwrResult LgwrResult = {{0, 0}, {0, 0}};
|
static XLgwrResult LgwrResult = {{0, 0}, {0, 0}};
|
||||||
static XLgwrRqst LgwrRqst = {{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 int logFile = -1;
|
||||||
static uint32 logId = 0;
|
static uint32 logId = 0;
|
||||||
static uint32 logSeg = 0;
|
static uint32 logSeg = 0;
|
||||||
static off_t logOff = 0;
|
static uint32 logOff = 0;
|
||||||
|
|
||||||
static XLogRecPtr ReadRecPtr;
|
static XLogRecPtr ReadRecPtr;
|
||||||
static XLogRecPtr EndRecPtr;
|
static XLogRecPtr EndRecPtr;
|
||||||
static int readFile = -1;
|
static int readFile = -1;
|
||||||
static uint32 readId = 0;
|
static uint32 readId = 0;
|
||||||
static uint32 readSeg = 0;
|
static uint32 readSeg = 0;
|
||||||
static off_t readOff = (off_t) -1;
|
static uint32 readOff = 0;
|
||||||
static char readBuf[BLCKSZ];
|
static char readBuf[BLCKSZ];
|
||||||
static XLogRecord *nextRecord = NULL;
|
static XLogRecord *nextRecord = NULL;
|
||||||
|
|
||||||
@ -262,7 +266,13 @@ XLogInsert(RmgrId rmid, char *hdr, uint32 hdrlen, char *buf, uint32 buflen)
|
|||||||
freespace -= SizeOfXLogRecord;
|
freespace -= SizeOfXLogRecord;
|
||||||
record = (XLogRecord*) Insert->currpos;
|
record = (XLogRecord*) Insert->currpos;
|
||||||
record->xl_prev = Insert->PrevRecord;
|
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_xid = GetCurrentTransactionId();
|
||||||
record->xl_len = (len > freespace) ? freespace : len;
|
record->xl_len = (len > freespace) ? freespace : len;
|
||||||
record->xl_info = (len > freespace) ? XLR_TO_BE_CONTINUED : 0;
|
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 =
|
RecPtr.xrecoff =
|
||||||
XLogCtl->xlblocks[curridx].xrecoff - BLCKSZ +
|
XLogCtl->xlblocks[curridx].xrecoff - BLCKSZ +
|
||||||
Insert->currpos - ((char*) Insert->currpage);
|
Insert->currpos - ((char*) Insert->currpage);
|
||||||
if (MyLastRecPtr.xrecoff == 0)
|
if (MyLastRecPtr.xrecoff == 0 && rmid != RM_XLOG_ID)
|
||||||
{
|
{
|
||||||
SpinAcquire(SInvalLock);
|
SpinAcquire(SInvalLock);
|
||||||
MyProc->logRec = RecPtr;
|
MyProc->logRec = RecPtr;
|
||||||
@ -489,7 +499,7 @@ XLogFlush(XLogRecPtr record)
|
|||||||
{
|
{
|
||||||
logId = LgwrResult.Write.xlogid;
|
logId = LgwrResult.Write.xlogid;
|
||||||
logSeg = (LgwrResult.Write.xrecoff - 1) / XLogSegSize;
|
logSeg = (LgwrResult.Write.xrecoff - 1) / XLogSegSize;
|
||||||
logOff = (off_t) 0;
|
logOff = 0;
|
||||||
logFile = XLogFileOpen(logId, logSeg, false);
|
logFile = XLogFileOpen(logId, logSeg, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -612,7 +622,7 @@ XLogWrite(char *buffer)
|
|||||||
}
|
}
|
||||||
logId = LgwrResult.Write.xlogid;
|
logId = LgwrResult.Write.xlogid;
|
||||||
logSeg = (LgwrResult.Write.xrecoff - 1) / XLogSegSize;
|
logSeg = (LgwrResult.Write.xrecoff - 1) / XLogSegSize;
|
||||||
logOff = (off_t) 0;
|
logOff = 0;
|
||||||
logFile = XLogFileInit(logId, logSeg);
|
logFile = XLogFileInit(logId, logSeg);
|
||||||
SpinAcquire(ControlFileLockId);
|
SpinAcquire(ControlFileLockId);
|
||||||
ControlFile->logId = logId;
|
ControlFile->logId = logId;
|
||||||
@ -626,14 +636,14 @@ XLogWrite(char *buffer)
|
|||||||
{
|
{
|
||||||
logId = LgwrResult.Write.xlogid;
|
logId = LgwrResult.Write.xlogid;
|
||||||
logSeg = (LgwrResult.Write.xrecoff - 1) / XLogSegSize;
|
logSeg = (LgwrResult.Write.xrecoff - 1) / XLogSegSize;
|
||||||
logOff = (off_t) 0;
|
logOff = 0;
|
||||||
logFile = XLogFileOpen(logId, logSeg, false);
|
logFile = XLogFileOpen(logId, logSeg, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (logOff != (LgwrResult.Write.xrecoff - BLCKSZ) % XLogSegSize)
|
if (logOff != (LgwrResult.Write.xrecoff - BLCKSZ) % XLogSegSize)
|
||||||
{
|
{
|
||||||
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",
|
elog(STOP, "Lseek(logfile %u seg %u off %u) failed: %d",
|
||||||
logId, logSeg, logOff, errno);
|
logId, logSeg, logOff, errno);
|
||||||
}
|
}
|
||||||
@ -717,6 +727,10 @@ tryAgain:
|
|||||||
elog(STOP, "Fsync(logfile %u seg %u) failed: %d",
|
elog(STOP, "Fsync(logfile %u seg %u) failed: %d",
|
||||||
logId, logSeg, errno);
|
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);
|
return(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -753,327 +767,6 @@ tryAgain:
|
|||||||
return(fd);
|
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()
|
|
||||||
{
|
|
||||||
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: %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))
|
|
||||||
{
|
|
||||||
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 ||
|
|
||||||
ControlFile->checkPoint.xlogid == 0 ||
|
|
||||||
ControlFile->checkPoint.xrecoff == 0)
|
|
||||||
elog(STOP, "Control file context is broken");
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
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)));
|
|
||||||
}
|
|
||||||
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))
|
|
||||||
{
|
|
||||||
if (ControlFile->state == DB_SHUTDOWNED)
|
|
||||||
elog(STOP, "Invalid Redo/Undo record in Shutdowned state");
|
|
||||||
recovery = true;
|
|
||||||
}
|
|
||||||
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*
|
static XLogRecord*
|
||||||
ReadRecord(XLogRecPtr *RecPtr, char *buffer)
|
ReadRecord(XLogRecPtr *RecPtr, char *buffer)
|
||||||
{
|
{
|
||||||
@ -1081,6 +774,7 @@ ReadRecord(XLogRecPtr *RecPtr, char *buffer)
|
|||||||
XLogRecPtr tmpRecPtr = EndRecPtr;
|
XLogRecPtr tmpRecPtr = EndRecPtr;
|
||||||
bool nextmode = (RecPtr == NULL);
|
bool nextmode = (RecPtr == NULL);
|
||||||
int emode = (nextmode) ? LOG : STOP;
|
int emode = (nextmode) ? LOG : STOP;
|
||||||
|
bool noBlck = false;
|
||||||
|
|
||||||
if (nextmode)
|
if (nextmode)
|
||||||
{
|
{
|
||||||
@ -1113,16 +807,16 @@ ReadRecord(XLogRecPtr *RecPtr, char *buffer)
|
|||||||
readSeg = RecPtr->xrecoff / XLogSegSize;
|
readSeg = RecPtr->xrecoff / XLogSegSize;
|
||||||
if (readFile < 0)
|
if (readFile < 0)
|
||||||
{
|
{
|
||||||
readOff = (off_t) -1;
|
noBlck = true;
|
||||||
readFile = XLogFileOpen(readId, readSeg, nextmode);
|
readFile = XLogFileOpen(readId, readSeg, nextmode);
|
||||||
if (readFile < 0)
|
if (readFile < 0)
|
||||||
goto next_record_is_invalid;
|
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;
|
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",
|
elog(STOP, "ReadRecord: lseek(logfile %u seg %u off %u) failed: %d",
|
||||||
readId, readSeg, readOff, errno);
|
readId, readSeg, readOff, errno);
|
||||||
if (read(readFile, readBuf, BLCKSZ) != BLCKSZ)
|
if (read(readFile, readBuf, BLCKSZ) != BLCKSZ)
|
||||||
@ -1186,7 +880,7 @@ got_record:;
|
|||||||
readId++;
|
readId++;
|
||||||
}
|
}
|
||||||
close(readFile);
|
close(readFile);
|
||||||
readOff = (off_t) 0;
|
readOff = 0;
|
||||||
readFile = XLogFileOpen(readId, readSeg, nextmode);
|
readFile = XLogFileOpen(readId, readSeg, nextmode);
|
||||||
if (readFile < 0)
|
if (readFile < 0)
|
||||||
goto next_record_is_invalid;
|
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",
|
elog(LOG, "Formating logfile %u seg %u block %u at offset %u",
|
||||||
readId, readSeg, readOff, EndRecPtr.xrecoff % BLCKSZ);
|
readId, readSeg, readOff, EndRecPtr.xrecoff % BLCKSZ);
|
||||||
readFile = XLogFileOpen(readId, readSeg, false);
|
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",
|
elog(STOP, "ReadRecord: lseek(logfile %u seg %u off %u) failed: %d",
|
||||||
readId, readSeg, readOff, errno);
|
readId, readSeg, readOff, errno);
|
||||||
if (read(readFile, readBuf, BLCKSZ) != BLCKSZ)
|
if (read(readFile, readBuf, BLCKSZ) != BLCKSZ)
|
||||||
@ -1288,7 +982,7 @@ next_record_is_invalid:;
|
|||||||
readId, readSeg, readOff, errno);
|
readId, readSeg, readOff, errno);
|
||||||
memset(readBuf + EndRecPtr.xrecoff % BLCKSZ, 0,
|
memset(readBuf + EndRecPtr.xrecoff % BLCKSZ, 0,
|
||||||
BLCKSZ - EndRecPtr.xrecoff % BLCKSZ);
|
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",
|
elog(STOP, "ReadRecord: lseek(logfile %u seg %u off %u) failed: %d",
|
||||||
readId, readSeg, readOff, errno);
|
readId, readSeg, readOff, errno);
|
||||||
if (write(readFile, readBuf, BLCKSZ) != BLCKSZ)
|
if (write(readFile, readBuf, BLCKSZ) != BLCKSZ)
|
||||||
@ -1303,15 +997,17 @@ next_record_is_invalid:;
|
|||||||
readId = tmpRecPtr.xlogid;
|
readId = tmpRecPtr.xlogid;
|
||||||
readSeg = tmpRecPtr.xrecoff / XLogSegSize;
|
readSeg = tmpRecPtr.xrecoff / XLogSegSize;
|
||||||
readOff = (tmpRecPtr.xrecoff % XLogSegSize) / BLCKSZ;
|
readOff = (tmpRecPtr.xrecoff % XLogSegSize) / BLCKSZ;
|
||||||
|
Assert(readOff > 0);
|
||||||
}
|
}
|
||||||
if (readOff > 0)
|
if (readOff > 0)
|
||||||
{
|
{
|
||||||
elog(LOG, "Formating logfile %u seg %u block %u at offset 0",
|
if (!XLByteEQ(tmpRecPtr, EndRecPtr))
|
||||||
readId, readSeg, readOff);
|
elog(LOG, "Formating logfile %u seg %u block %u at offset 0",
|
||||||
|
readId, readSeg, readOff);
|
||||||
readOff *= BLCKSZ;
|
readOff *= BLCKSZ;
|
||||||
memset(readBuf, 0, BLCKSZ);
|
memset(readBuf, 0, BLCKSZ);
|
||||||
readFile = XLogFileOpen(readId, readSeg, false);
|
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",
|
elog(STOP, "ReadRecord: lseek(logfile %u seg %u off %u) failed: %d",
|
||||||
readId, readSeg, readOff, errno);
|
readId, readSeg, readOff, errno);
|
||||||
while (readOff < XLogSegSize)
|
while (readOff < XLogSegSize)
|
||||||
@ -1357,6 +1053,381 @@ next_record_is_invalid:;
|
|||||||
return(record);
|
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
|
void
|
||||||
CreateCheckPoint(bool shutdown)
|
CreateCheckPoint(bool shutdown)
|
||||||
{
|
{
|
||||||
@ -1375,7 +1446,7 @@ CreateCheckPoint(bool shutdown)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Get REDO record ptr */
|
/* Get REDO record ptr */
|
||||||
while (!TAS(&(XLogCtl->insert_lck)))
|
while (TAS(&(XLogCtl->insert_lck)))
|
||||||
{
|
{
|
||||||
struct timeval delay = {0, 5000};
|
struct timeval delay = {0, 5000};
|
||||||
|
|
||||||
@ -1410,6 +1481,7 @@ CreateCheckPoint(bool shutdown)
|
|||||||
FlushBufferPool();
|
FlushBufferPool();
|
||||||
|
|
||||||
/* Get UNDO record ptr */
|
/* Get UNDO record ptr */
|
||||||
|
checkPoint.undo.xrecoff = 0;
|
||||||
|
|
||||||
if (shutdown && checkPoint.undo.xrecoff != 0)
|
if (shutdown && checkPoint.undo.xrecoff != 0)
|
||||||
elog(STOP, "Active transaction while data base is shutting down");
|
elog(STOP, "Active transaction while data base is shutting down");
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Copyright (c) 1994, Regents of the University of California
|
* Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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 ALLOC(t, c) (t *)calloc((unsigned)(c), sizeof(t))
|
||||||
#define FIRST_TYPE_OID 16 /* OID of the first type */
|
#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);
|
extern int Int_yyparse(void);
|
||||||
static hashnode *AddStr(char *str, int strlength, int mderef);
|
static hashnode *AddStr(char *str, int strlength, int mderef);
|
||||||
static Form_pg_attribute AllocateAttribute(void);
|
static Form_pg_attribute AllocateAttribute(void);
|
||||||
@ -218,22 +226,13 @@ BootstrapMain(int argc, char *argv[])
|
|||||||
*/
|
*/
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
int portFd = -1;
|
|
||||||
char *dbName;
|
char *dbName;
|
||||||
int flag;
|
int flag;
|
||||||
int override = 1; /* use BootstrapProcessing or
|
bool xloginit = false;
|
||||||
* InitProcessing mode */
|
|
||||||
|
|
||||||
extern int optind;
|
extern int optind;
|
||||||
extern char *optarg;
|
extern char *optarg;
|
||||||
|
|
||||||
/* ----------------
|
|
||||||
* initialize signal handlers
|
|
||||||
* ----------------
|
|
||||||
*/
|
|
||||||
pqsignal(SIGINT, (sig_func) die);
|
|
||||||
pqsignal(SIGHUP, (sig_func) die);
|
|
||||||
pqsignal(SIGTERM, (sig_func) die);
|
|
||||||
|
|
||||||
/* --------------------
|
/* --------------------
|
||||||
* initialize globals
|
* initialize globals
|
||||||
@ -252,8 +251,9 @@ BootstrapMain(int argc, char *argv[])
|
|||||||
Noversion = false;
|
Noversion = false;
|
||||||
dbName = NULL;
|
dbName = NULL;
|
||||||
DataDir = getenv("PGDATA"); /* Null if no PGDATA variable */
|
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)
|
switch (flag)
|
||||||
{
|
{
|
||||||
@ -270,14 +270,17 @@ BootstrapMain(int argc, char *argv[])
|
|||||||
case 'F':
|
case 'F':
|
||||||
disableFsync = true;
|
disableFsync = true;
|
||||||
break;
|
break;
|
||||||
case 'O':
|
|
||||||
override = true;
|
|
||||||
break;
|
|
||||||
case 'Q':
|
case 'Q':
|
||||||
Quiet = true;
|
Quiet = true;
|
||||||
break;
|
break;
|
||||||
case 'P': /* specify port */
|
case 'x':
|
||||||
portFd = atoi(optarg);
|
xloginit = true;
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
IsUnderPostmaster = true;
|
||||||
|
break;
|
||||||
|
case 'B':
|
||||||
|
NBuffers = atoi(optarg);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
usage();
|
usage();
|
||||||
@ -290,6 +293,8 @@ BootstrapMain(int argc, char *argv[])
|
|||||||
else if (argc - optind == 1)
|
else if (argc - optind == 1)
|
||||||
dbName = argv[optind];
|
dbName = argv[optind];
|
||||||
|
|
||||||
|
SetProcessingMode(BootstrapProcessing);
|
||||||
|
|
||||||
if (!DataDir)
|
if (!DataDir)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "%s does not know where to find the database system "
|
fprintf(stderr, "%s does not know where to find the database system "
|
||||||
@ -311,24 +316,50 @@ BootstrapMain(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------
|
BaseInit();
|
||||||
* initialize input fd
|
|
||||||
* ----------------
|
if (!IsUnderPostmaster)
|
||||||
*/
|
|
||||||
if (IsUnderPostmaster && portFd < 0)
|
|
||||||
{
|
{
|
||||||
fputs("backend: failed, no -P option with -postmaster opt.\n", stderr);
|
pqsignal(SIGINT, (sig_func) die);
|
||||||
proc_exit(1);
|
pqsignal(SIGHUP, (sig_func) die);
|
||||||
|
pqsignal(SIGTERM, (sig_func) die);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------
|
/*
|
||||||
* backend initialization
|
* Bootstrap under Postmaster means two things:
|
||||||
* ----------------
|
* (xloginit) ? StartupXLOG : ShutdownXLOG
|
||||||
|
*
|
||||||
|
* If !under Postmaster and xloginit then BootStrapXLOG.
|
||||||
|
*/
|
||||||
|
if (IsUnderPostmaster || xloginit)
|
||||||
|
{
|
||||||
|
sprintf(XLogDir, "%s%cpg_xlog", DataDir, SEP_CHAR);
|
||||||
|
sprintf(ControlFilePath, "%s%cpg_control", DataDir, SEP_CHAR);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsUnderPostmaster && xloginit)
|
||||||
|
{
|
||||||
|
StartupXLOG();
|
||||||
|
proc_exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsUnderPostmaster && xloginit)
|
||||||
|
{
|
||||||
|
BootStrapXLOG();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* backend initialization
|
||||||
*/
|
*/
|
||||||
SetProcessingMode((override) ? BootstrapProcessing : InitProcessing);
|
|
||||||
InitPostgres(dbName);
|
InitPostgres(dbName);
|
||||||
LockDisable(true);
|
LockDisable(true);
|
||||||
|
|
||||||
|
if (IsUnderPostmaster && !xloginit)
|
||||||
|
{
|
||||||
|
ShutdownXLOG();
|
||||||
|
proc_exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < MAXATTR; i++)
|
for (i = 0; i < MAXATTR; i++)
|
||||||
{
|
{
|
||||||
attrtypes[i] = (Form_pg_attribute) NULL;
|
attrtypes[i] = (Form_pg_attribute) NULL;
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* NOTES
|
||||||
*
|
*
|
||||||
@ -87,6 +87,7 @@
|
|||||||
#include "storage/fd.h"
|
#include "storage/fd.h"
|
||||||
#include "storage/ipc.h"
|
#include "storage/ipc.h"
|
||||||
#include "storage/proc.h"
|
#include "storage/proc.h"
|
||||||
|
#include "access/xlog.h"
|
||||||
#include "tcop/tcopprot.h"
|
#include "tcop/tcopprot.h"
|
||||||
#include "utils/trace.h"
|
#include "utils/trace.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
@ -98,10 +99,13 @@
|
|||||||
#define INVALID_SOCK (-1)
|
#define INVALID_SOCK (-1)
|
||||||
#define ARGV_SIZE 64
|
#define ARGV_SIZE 64
|
||||||
|
|
||||||
/*
|
#ifdef HAVE_SIGPROCMASK
|
||||||
* Max time in seconds for socket to linger (close() to block) waiting
|
sigset_t UnBlockSig,
|
||||||
* for frontend to retrieve its message from us.
|
BlockSig;
|
||||||
*/
|
#else
|
||||||
|
int UnBlockSig,
|
||||||
|
BlockSig;
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Info for garbage collection. Whenever a process dies, the Postmaster
|
* Info for garbage collection. Whenever a process dies, the Postmaster
|
||||||
@ -124,7 +128,6 @@ static Dllist *BackendList;
|
|||||||
static Dllist *PortList;
|
static Dllist *PortList;
|
||||||
|
|
||||||
static unsigned short PostPortName = 0;
|
static unsigned short PostPortName = 0;
|
||||||
static short ActiveBackends = FALSE;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is a boolean indicating that there is at least one backend that
|
* 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 */
|
* non-local connections */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
static pid_t StartupPID = 0,
|
||||||
* GH: For !HAVE_SIGPROCMASK (NEXTSTEP), TRH implemented an
|
ShutdownPID = 0;
|
||||||
* alternative interface.
|
|
||||||
*/
|
|
||||||
#ifdef HAVE_SIGPROCMASK
|
|
||||||
static sigset_t oldsigmask,
|
|
||||||
newsigmask;
|
|
||||||
|
|
||||||
#else
|
#define NoShutdown 0
|
||||||
static int orgsigmask = sigblock(0);
|
#define SmartShutdown 1
|
||||||
|
#define FastShutdown 2
|
||||||
|
|
||||||
#endif
|
static int Shutdown = NoShutdown;
|
||||||
|
|
||||||
|
static bool FatalError = false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* State for assigning random salts and cancel keys.
|
* State for assigning random salts and cancel keys.
|
||||||
@ -234,30 +235,35 @@ extern char *optarg;
|
|||||||
extern int optind,
|
extern int optind,
|
||||||
opterr;
|
opterr;
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* postmaster.c - function prototypes
|
* postmaster.c - function prototypes
|
||||||
*/
|
*/
|
||||||
static void pmdaemonize(void);
|
static void pmdaemonize(void);
|
||||||
static Port *ConnCreate(int serverFd);
|
static Port *ConnCreate(int serverFd);
|
||||||
static void ConnFree(Port *port);
|
static void ConnFree(Port *port);
|
||||||
static void reset_shared(unsigned short port);
|
static void reset_shared(unsigned short port);
|
||||||
static void pmdie(SIGNAL_ARGS);
|
static void pmdie(SIGNAL_ARGS);
|
||||||
static void reaper(SIGNAL_ARGS);
|
static void reaper(SIGNAL_ARGS);
|
||||||
static void dumpstatus(SIGNAL_ARGS);
|
static void dumpstatus(SIGNAL_ARGS);
|
||||||
static void CleanupProc(int pid, int exitstatus);
|
static void CleanupProc(int pid, int exitstatus);
|
||||||
static int DoBackend(Port *port);
|
static int DoBackend(Port *port);
|
||||||
static void ExitPostmaster(int status);
|
static void ExitPostmaster(int status);
|
||||||
static void usage(const char *);
|
static void usage(const char *);
|
||||||
static int ServerLoop(void);
|
static int ServerLoop(void);
|
||||||
static int BackendStartup(Port *port);
|
static int BackendStartup(Port *port);
|
||||||
static int readStartupPacket(void *arg, PacketLen len, void *pkt);
|
static int readStartupPacket(void *arg, PacketLen len, void *pkt);
|
||||||
static int processCancelRequest(Port *port, PacketLen len, void *pkt);
|
static int processCancelRequest(Port *port, PacketLen len, void *pkt);
|
||||||
static int initMasks(fd_set *rmask, fd_set *wmask);
|
static int initMasks(fd_set *rmask, fd_set *wmask);
|
||||||
static long PostmasterRandom(void);
|
static long PostmasterRandom(void);
|
||||||
static void RandomSalt(char *salt);
|
static void RandomSalt(char *salt);
|
||||||
static void SignalChildren(SIGNAL_ARGS);
|
static void SignalChildren(SIGNAL_ARGS);
|
||||||
static int CountChildren(void);
|
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
|
#ifdef USE_SSL
|
||||||
static void InitSSL(void);
|
static void InitSSL(void);
|
||||||
#endif
|
#endif
|
||||||
@ -613,18 +619,23 @@ PostmasterMain(int argc, char *argv[])
|
|||||||
/*
|
/*
|
||||||
* Set up signal handlers for the postmaster process.
|
* Set up signal handlers for the postmaster process.
|
||||||
*/
|
*/
|
||||||
|
PG_INITMASK();
|
||||||
|
PG_SETMASK(&BlockSig);
|
||||||
|
|
||||||
pqsignal(SIGHUP, pmdie); /* send SIGHUP, don't die */
|
pqsignal(SIGHUP, pmdie); /* send SIGHUP, don't die */
|
||||||
pqsignal(SIGINT, pmdie); /* die */
|
pqsignal(SIGINT, pmdie); /* send SIGTERM and ShutdownDataBase */
|
||||||
pqsignal(SIGQUIT, pmdie); /* send SIGTERM and die */
|
pqsignal(SIGQUIT, pmdie); /* send SIGUSR1 and die */
|
||||||
pqsignal(SIGTERM, pmdie); /* send SIGTERM,SIGKILL and die */
|
pqsignal(SIGTERM, pmdie); /* wait for children and ShutdownDataBase */
|
||||||
pqsignal(SIGPIPE, SIG_IGN); /* ignored */
|
pqsignal(SIGALRM, SIG_IGN); /* ignored */
|
||||||
pqsignal(SIGUSR1, pmdie); /* send SIGUSR1 and die */
|
pqsignal(SIGPIPE, SIG_IGN); /* ignored */
|
||||||
pqsignal(SIGUSR2, pmdie); /* send SIGUSR2, don't die */
|
pqsignal(SIGUSR1, SIG_IGN); /* ignored */
|
||||||
pqsignal(SIGCHLD, reaper); /* handle child termination */
|
pqsignal(SIGUSR2, pmdie); /* send SIGUSR2, don't die */
|
||||||
pqsignal(SIGTTIN, SIG_IGN); /* ignored */
|
pqsignal(SIGCHLD, reaper); /* handle child termination */
|
||||||
pqsignal(SIGTTOU, SIG_IGN); /* ignored */
|
pqsignal(SIGTTIN, SIG_IGN); /* ignored */
|
||||||
pqsignal(SIGWINCH, dumpstatus); /* dump port status */
|
pqsignal(SIGTTOU, SIG_IGN); /* ignored */
|
||||||
|
pqsignal(SIGWINCH, dumpstatus); /* dump port status */
|
||||||
|
|
||||||
|
StartupPID = StartupDataBase();
|
||||||
|
|
||||||
status = ServerLoop();
|
status = ServerLoop();
|
||||||
|
|
||||||
@ -702,12 +713,6 @@ ServerLoop(void)
|
|||||||
|
|
||||||
nSockets = initMasks(&readmask, &writemask);
|
nSockets = initMasks(&readmask, &writemask);
|
||||||
|
|
||||||
#ifdef HAVE_SIGPROCMASK
|
|
||||||
sigprocmask(0, NULL, &oldsigmask);
|
|
||||||
sigemptyset(&newsigmask);
|
|
||||||
sigaddset(&newsigmask, SIGCHLD);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
Port *port;
|
Port *port;
|
||||||
@ -717,25 +722,25 @@ ServerLoop(void)
|
|||||||
int no_select = 0;
|
int no_select = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_SIGPROCMASK
|
|
||||||
sigprocmask(SIG_SETMASK, &oldsigmask, 0);
|
|
||||||
#else
|
|
||||||
sigsetmask(orgsigmask);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
memmove((char *) &rmask, (char *) &readmask, sizeof(fd_set));
|
memmove((char *) &rmask, (char *) &readmask, sizeof(fd_set));
|
||||||
memmove((char *) &wmask, (char *) &writemask, sizeof(fd_set));
|
memmove((char *) &wmask, (char *) &writemask, sizeof(fd_set));
|
||||||
|
|
||||||
#ifdef USE_SSL
|
#ifdef USE_SSL
|
||||||
for (curr = DLGetHead(PortList); curr; curr = DLGetSucc(curr))
|
for (curr = DLGetHead(PortList); curr; curr = DLGetSucc(curr))
|
||||||
if (((Port *)DLE_VAL(curr))->ssl &&
|
{
|
||||||
SSL_pending(((Port *)DLE_VAL(curr))->ssl) > 0) {
|
if (((Port *)DLE_VAL(curr))->ssl &&
|
||||||
no_select = 1;
|
SSL_pending(((Port *)DLE_VAL(curr))->ssl) > 0)
|
||||||
break;
|
{
|
||||||
}
|
no_select = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PG_SETMASK(&UnBlockSig);
|
||||||
if (no_select)
|
if (no_select)
|
||||||
FD_ZERO(&rmask); /* So we don't accept() anything below */
|
FD_ZERO(&rmask); /* So we don't accept() anything below */
|
||||||
else
|
else
|
||||||
|
#else
|
||||||
|
PG_SETMASK(&UnBlockSig);
|
||||||
#endif
|
#endif
|
||||||
if (select(nSockets, &rmask, &wmask, (fd_set *) NULL,
|
if (select(nSockets, &rmask, &wmask, (fd_set *) NULL,
|
||||||
(struct timeval *) NULL) < 0)
|
(struct timeval *) NULL) < 0)
|
||||||
@ -765,16 +770,9 @@ ServerLoop(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* [TRH] To avoid race conditions, block SIGCHLD signals while we
|
* Block all signals
|
||||||
* are handling the request. (both reaper() and ConnCreate()
|
|
||||||
* manipulate the BackEnd list, and reaper() calls free() which is
|
|
||||||
* usually non-reentrant.)
|
|
||||||
*/
|
*/
|
||||||
#ifdef HAVE_SIGPROCMASK
|
PG_SETMASK(&BlockSig);
|
||||||
sigprocmask(SIG_BLOCK, &newsigmask, &oldsigmask);
|
|
||||||
#else
|
|
||||||
sigblock(sigmask(SIGCHLD)); /* XXX[TRH] portability */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* new connection pending on our well-known port's socket */
|
/* new connection pending on our well-known port's socket */
|
||||||
|
|
||||||
@ -817,8 +815,8 @@ ServerLoop(void)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
if (FD_ISSET(port->sock, &rmask))
|
if (FD_ISSET(port->sock, &rmask))
|
||||||
readyread = 1;
|
readyread = 1;
|
||||||
|
|
||||||
if (readyread)
|
if (readyread)
|
||||||
{
|
{
|
||||||
@ -852,13 +850,25 @@ ServerLoop(void)
|
|||||||
|
|
||||||
if (status == STATUS_OK && port->pktInfo.state == Idle)
|
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,
|
PacketSendError(&port->pktInfo,
|
||||||
"Sorry, too many clients already");
|
"Sorry, too many clients already");
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the backend start fails then keep the connection
|
* If the backend start fails then keep the connection
|
||||||
* open to report it. Otherwise, pretend there is an
|
* open to report it. Otherwise, pretend there is an
|
||||||
@ -1113,6 +1123,7 @@ ConnCreate(int serverFd)
|
|||||||
{
|
{
|
||||||
fprintf(stderr, "%s: ConnCreate: malloc failed\n",
|
fprintf(stderr, "%s: ConnCreate: malloc failed\n",
|
||||||
progname);
|
progname);
|
||||||
|
SignalChildren(SIGUSR1);
|
||||||
ExitPostmaster(1);
|
ExitPostmaster(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1154,7 +1165,6 @@ reset_shared(unsigned short port)
|
|||||||
{
|
{
|
||||||
ipc_key = port * 1000 + shmem_seq * 100;
|
ipc_key = port * 1000 + shmem_seq * 100;
|
||||||
CreateSharedMemoryAndSemaphores(ipc_key, MaxBackends);
|
CreateSharedMemoryAndSemaphores(ipc_key, MaxBackends);
|
||||||
ActiveBackends = FALSE;
|
|
||||||
shmem_seq += 1;
|
shmem_seq += 1;
|
||||||
if (shmem_seq >= 10)
|
if (shmem_seq >= 10)
|
||||||
shmem_seq -= 10;
|
shmem_seq -= 10;
|
||||||
@ -1166,49 +1176,94 @@ reset_shared(unsigned short port)
|
|||||||
static void
|
static void
|
||||||
pmdie(SIGNAL_ARGS)
|
pmdie(SIGNAL_ARGS)
|
||||||
{
|
{
|
||||||
int i;
|
PG_SETMASK(&BlockSig);
|
||||||
|
|
||||||
TPRINTF(TRACE_VERBOSE, "pmdie %d", postgres_signal_arg);
|
TPRINTF(TRACE_VERBOSE, "pmdie %d", postgres_signal_arg);
|
||||||
|
|
||||||
/*
|
|
||||||
* Kill self and/or children processes depending on signal number.
|
|
||||||
*/
|
|
||||||
switch (postgres_signal_arg)
|
switch (postgres_signal_arg)
|
||||||
{
|
{
|
||||||
case SIGHUP:
|
case SIGHUP:
|
||||||
/* Send SIGHUP to all children (update options flags) */
|
/*
|
||||||
|
* Send SIGHUP to all children (update options flags)
|
||||||
|
*/
|
||||||
|
if (Shutdown > SmartShutdown)
|
||||||
|
return;
|
||||||
SignalChildren(SIGHUP);
|
SignalChildren(SIGHUP);
|
||||||
/* Don't die */
|
|
||||||
return;
|
return;
|
||||||
case SIGINT:
|
|
||||||
/* Die without killing children */
|
|
||||||
break;
|
|
||||||
case SIGQUIT:
|
|
||||||
/* Shutdown all children with SIGTERM */
|
|
||||||
SignalChildren(SIGTERM);
|
|
||||||
/* Don't die */
|
|
||||||
return;
|
|
||||||
case SIGTERM:
|
|
||||||
/* Shutdown all children with SIGTERM and SIGKILL, then die */
|
|
||||||
SignalChildren(SIGTERM);
|
|
||||||
for (i = 0; i < 10; i++)
|
|
||||||
{
|
|
||||||
if (!DLGetHead(BackendList))
|
|
||||||
break;
|
|
||||||
sleep(1);
|
|
||||||
}
|
|
||||||
if (DLGetHead(BackendList))
|
|
||||||
SignalChildren(SIGKILL);
|
|
||||||
break;
|
|
||||||
case SIGUSR1:
|
|
||||||
/* Quick die all children with SIGUSR1 and die */
|
|
||||||
SignalChildren(SIGUSR1);
|
|
||||||
break;
|
|
||||||
case SIGUSR2:
|
case SIGUSR2:
|
||||||
/* Send SIGUSR2 to all children (AsyncNotifyHandler) */
|
/*
|
||||||
|
* Send SIGUSR2 to all children (AsyncNotifyHandler)
|
||||||
|
*/
|
||||||
|
if (Shutdown > SmartShutdown)
|
||||||
|
return;
|
||||||
SignalChildren(SIGUSR2);
|
SignalChildren(SIGUSR2);
|
||||||
/* Don't die */
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
case SIGTERM:
|
||||||
|
/*
|
||||||
|
* 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 (!FatalError)
|
||||||
|
SignalChildren(SIGTERM);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
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 */
|
/* exit postmaster */
|
||||||
@ -1224,29 +1279,82 @@ reaper(SIGNAL_ARGS)
|
|||||||
/* GH: replace waitpid for !HAVE_WAITPID. Does this work ? */
|
/* GH: replace waitpid for !HAVE_WAITPID. Does this work ? */
|
||||||
#ifdef HAVE_WAITPID
|
#ifdef HAVE_WAITPID
|
||||||
int status; /* backend exit status */
|
int status; /* backend exit status */
|
||||||
|
|
||||||
#else
|
#else
|
||||||
union wait statusp; /* backend exit status */
|
union wait status; /* backend exit status */
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
int exitstatus;
|
||||||
int pid; /* process id of dead backend */
|
int pid; /* process id of dead backend */
|
||||||
|
|
||||||
|
PG_SETMASK(&BlockSig);
|
||||||
|
|
||||||
if (DebugLvl)
|
if (DebugLvl)
|
||||||
fprintf(stderr, "%s: reaping dead processes...\n",
|
fprintf(stderr, "%s: reaping dead processes...\n",
|
||||||
progname);
|
progname);
|
||||||
#ifdef HAVE_WAITPID
|
#ifdef HAVE_WAITPID
|
||||||
while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
|
while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
|
||||||
{
|
{
|
||||||
CleanupProc(pid, status);
|
exitstatus = status;
|
||||||
pqsignal(SIGCHLD, reaper);
|
|
||||||
}
|
|
||||||
#else
|
#else
|
||||||
while ((pid = wait3(&statusp, WNOHANG, NULL)) > 0)
|
while ((pid = wait3(&status, WNOHANG, NULL)) > 0)
|
||||||
{
|
{
|
||||||
CleanupProc(pid, statusp.w_status);
|
exitstatus = status.w_status;
|
||||||
pqsignal(SIGCHLD, reaper);
|
|
||||||
}
|
|
||||||
#endif
|
#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,
|
CleanupProc(int pid,
|
||||||
int exitstatus) /* child's exit status. */
|
int exitstatus) /* child's exit status. */
|
||||||
{
|
{
|
||||||
Dlelem *prev,
|
Dlelem *curr,
|
||||||
*curr;
|
*next;
|
||||||
Backend *bp;
|
Backend *bp;
|
||||||
int sig;
|
int sig;
|
||||||
|
|
||||||
@ -1298,18 +1406,19 @@ CleanupProc(int pid,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FatalError = true;
|
||||||
curr = DLGetHead(BackendList);
|
curr = DLGetHead(BackendList);
|
||||||
while (curr)
|
while (curr)
|
||||||
{
|
{
|
||||||
|
next = DLGetSucc(curr);
|
||||||
bp = (Backend *) DLE_VAL(curr);
|
bp = (Backend *) DLE_VAL(curr);
|
||||||
|
|
||||||
/* -----------------
|
/*
|
||||||
* SIGUSR1 is the special signal that says exit
|
* SIGUSR1 is the special signal that says exit
|
||||||
* without proc_exit and let the user know what's going on.
|
* without proc_exit and let the user know what's going on.
|
||||||
* ProcSemaphoreKill() cleans up the backends semaphore. If
|
* ProcSemaphoreKill() cleans up the backends semaphore. If
|
||||||
* SendStop is set (-s on command line), then we send a SIGSTOP so
|
* SendStop is set (-s on command line), then we send a SIGSTOP so
|
||||||
* that we can core dumps from all backends by hand.
|
* that we can core dumps from all backends by hand.
|
||||||
* -----------------
|
|
||||||
*/
|
*/
|
||||||
sig = (SendStop) ? SIGSTOP : SIGUSR1;
|
sig = (SendStop) ? SIGSTOP : SIGUSR1;
|
||||||
if (bp->pid != pid)
|
if (bp->pid != pid)
|
||||||
@ -1322,36 +1431,25 @@ CleanupProc(int pid,
|
|||||||
bp->pid);
|
bp->pid);
|
||||||
kill(bp->pid, sig);
|
kill(bp->pid, sig);
|
||||||
}
|
}
|
||||||
ProcRemove(bp->pid);
|
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);
|
||||||
|
|
||||||
prev = DLGetPred(curr);
|
DLRemove(curr);
|
||||||
DLRemove(curr);
|
free(bp);
|
||||||
free(bp);
|
DLFreeElem(curr);
|
||||||
DLFreeElem(curr);
|
|
||||||
if (!prev)
|
|
||||||
{ /* removed head */
|
|
||||||
curr = DLGetHead(BackendList);
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
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;
|
bn->cancel_key = MyCancelKey;
|
||||||
DLAddHead(BackendList, DLNewElem(bn));
|
DLAddHead(BackendList, DLNewElem(bn));
|
||||||
|
|
||||||
ActiveBackends = TRUE;
|
|
||||||
|
|
||||||
return STATUS_OK;
|
return STATUS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1586,27 +1682,9 @@ DoBackend(Port *port)
|
|||||||
/* We don't want the postmaster's proc_exit() handlers */
|
/* We don't want the postmaster's proc_exit() handlers */
|
||||||
on_exit_reset();
|
on_exit_reset();
|
||||||
|
|
||||||
/* ----------------
|
/*
|
||||||
* register signal handlers.
|
* Signal handlers setting is moved to tcop/postgres...
|
||||||
* Thanks to the postmaster, these are currently blocked.
|
|
||||||
* ----------------
|
|
||||||
*/
|
*/
|
||||||
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 */
|
/* Close the postmaster sockets */
|
||||||
if (NetServer)
|
if (NetServer)
|
||||||
@ -1739,6 +1817,8 @@ ExitPostmaster(int status)
|
|||||||
/*
|
/*
|
||||||
* Not sure of the semantics here. When the Postmaster dies, should
|
* Not sure of the semantics here. When the Postmaster dies, should
|
||||||
* the backends all be killed? probably not.
|
* the backends all be killed? probably not.
|
||||||
|
*
|
||||||
|
* MUST -- vadim 05-10-1999
|
||||||
*/
|
*/
|
||||||
if (ServerSock_INET != INVALID_SOCK)
|
if (ServerSock_INET != INVALID_SOCK)
|
||||||
StreamClose(ServerSock_INET);
|
StreamClose(ServerSock_INET);
|
||||||
@ -1752,8 +1832,11 @@ ExitPostmaster(int status)
|
|||||||
static void
|
static void
|
||||||
dumpstatus(SIGNAL_ARGS)
|
dumpstatus(SIGNAL_ARGS)
|
||||||
{
|
{
|
||||||
Dlelem *curr = DLGetHead(PortList);
|
Dlelem *curr;
|
||||||
|
|
||||||
|
PG_SETMASK(&BlockSig);
|
||||||
|
|
||||||
|
curr = DLGetHead(PortList);
|
||||||
while (curr)
|
while (curr)
|
||||||
{
|
{
|
||||||
Port *port = DLE_VAL(curr);
|
Port *port = DLE_VAL(curr);
|
||||||
@ -1837,7 +1920,6 @@ CountChildren(void)
|
|||||||
return cnt;
|
return cnt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifdef USE_SSL
|
#ifdef USE_SSL
|
||||||
/*
|
/*
|
||||||
* Initialize SSL library and structures
|
* Initialize SSL library and structures
|
||||||
@ -1868,3 +1950,88 @@ static void InitSSL(void) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#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);
|
||||||
|
}
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
#ifndef HAS_TEST_AND_SET
|
||||||
{
|
{
|
||||||
int status;
|
|
||||||
extern IpcSemaphoreId WaitIOSemId;
|
extern IpcSemaphoreId WaitIOSemId;
|
||||||
extern IpcSemaphoreId WaitCLSemId;
|
extern IpcSemaphoreId WaitCLSemId;
|
||||||
|
|
||||||
WaitIOSemId = IpcSemaphoreCreate(IPCKeyGetWaitIOSemaphoreKey(key),
|
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),
|
WaitCLSemId = IpcSemaphoreCreate(IPCKeyGetWaitCLSemaphoreKey(key),
|
||||||
1, IPCProtection,
|
1, IPCProtection,
|
||||||
IpcSemaphoreDefaultStartValue,
|
IpcSemaphoreDefaultStartValue, 1);
|
||||||
1, &status);
|
if (WaitCLSemId < 0)
|
||||||
|
elog(FATAL, "InitBufferPool: IpcSemaphoreCreate(WaitCLSemId) failed");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
PrivateRefCount = (long *) calloc(NBuffers, sizeof(long));
|
PrivateRefCount = (long *) calloc(NBuffers, sizeof(long));
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* 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:
|
* Note:
|
||||||
* XXX This should be split into two different calls. One should
|
* XXX This should be split into two different calls. One should
|
||||||
@ -312,8 +300,7 @@ IpcSemaphoreCreate(IpcSemaphoreKey semKey,
|
|||||||
int semNum,
|
int semNum,
|
||||||
int permission,
|
int permission,
|
||||||
int semStartValue,
|
int semStartValue,
|
||||||
int removeOnExit,
|
int removeOnExit)
|
||||||
int *status)
|
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
int errStatus;
|
int errStatus;
|
||||||
@ -321,20 +308,14 @@ IpcSemaphoreCreate(IpcSemaphoreKey semKey,
|
|||||||
u_short array[IPC_NMAXSEM];
|
u_short array[IPC_NMAXSEM];
|
||||||
union semun semun;
|
union semun semun;
|
||||||
|
|
||||||
/* get a semaphore if non-existent */
|
|
||||||
/* check arguments */
|
/* check arguments */
|
||||||
if (semNum > IPC_NMAXSEM || semNum <= 0)
|
if (semNum > IPC_NMAXSEM || semNum <= 0)
|
||||||
{
|
return(-1);
|
||||||
*status = IpcInvalidArgument;
|
|
||||||
return 2; /* returns the number of the invalid
|
|
||||||
* argument */
|
|
||||||
}
|
|
||||||
|
|
||||||
semId = semget(semKey, 0, 0);
|
semId = semget(semKey, 0, 0);
|
||||||
|
|
||||||
if (semId == -1)
|
if (semId == -1)
|
||||||
{
|
{
|
||||||
*status = IpcSemIdNotExist; /* there doesn't exist a semaphore */
|
|
||||||
#ifdef DEBUG_IPC
|
#ifdef DEBUG_IPC
|
||||||
EPRINTF("calling semget with %d, %d , %d\n",
|
EPRINTF("calling semget with %d, %d , %d\n",
|
||||||
semKey,
|
semKey,
|
||||||
@ -348,7 +329,7 @@ IpcSemaphoreCreate(IpcSemaphoreKey semKey,
|
|||||||
EPRINTF("IpcSemaphoreCreate: semget failed (%s) "
|
EPRINTF("IpcSemaphoreCreate: semget failed (%s) "
|
||||||
"key=%d, num=%d, permission=%o",
|
"key=%d, num=%d, permission=%o",
|
||||||
strerror(errno), semKey, semNum, permission);
|
strerror(errno), semKey, semNum, permission);
|
||||||
proc_exit(3);
|
return(-1);
|
||||||
}
|
}
|
||||||
for (i = 0; i < semNum; i++)
|
for (i = 0; i < semNum; i++)
|
||||||
array[i] = semStartValue;
|
array[i] = semStartValue;
|
||||||
@ -358,22 +339,16 @@ IpcSemaphoreCreate(IpcSemaphoreKey semKey,
|
|||||||
{
|
{
|
||||||
EPRINTF("IpcSemaphoreCreate: semctl failed (%s) id=%d",
|
EPRINTF("IpcSemaphoreCreate: semctl failed (%s) id=%d",
|
||||||
strerror(errno), semId);
|
strerror(errno), semId);
|
||||||
|
semctl(semId, 0, IPC_RMID, semun);
|
||||||
|
return(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (removeOnExit)
|
if (removeOnExit)
|
||||||
on_shmem_exit(IPCPrivateSemaphoreKill, (caddr_t) semId);
|
on_shmem_exit(IPCPrivateSemaphoreKill, (caddr_t) semId);
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* there is a semaphore id for this key */
|
|
||||||
*status = IpcSemIdExist;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_IPC
|
#ifdef DEBUG_IPC
|
||||||
EPRINTF("\nIpcSemaphoreCreate, status %d, returns %d\n",
|
EPRINTF("\nIpcSemaphoreCreate, returns %d\n", semId);
|
||||||
*status,
|
|
||||||
semId);
|
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
fflush(stderr);
|
fflush(stderr);
|
||||||
#endif
|
#endif
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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)
|
CreateSharedMemoryAndSemaphores(IPCKey key, int maxBackends)
|
||||||
{
|
{
|
||||||
int size;
|
int size;
|
||||||
|
extern int XLOGShmemSize(void);
|
||||||
|
extern void XLOGShmemInit(void);
|
||||||
|
|
||||||
#ifdef HAS_TEST_AND_SET
|
#ifdef HAS_TEST_AND_SET
|
||||||
/* ---------------
|
/*
|
||||||
* create shared memory for slocks
|
* Create shared memory for slocks
|
||||||
* --------------
|
|
||||||
*/
|
*/
|
||||||
CreateAndInitSLockMemory(IPCKeyGetSLockSharedMemoryKey(key));
|
CreateAndInitSLockMemory(IPCKeyGetSLockSharedMemoryKey(key));
|
||||||
#endif
|
#endif
|
||||||
/* ----------------
|
/*
|
||||||
* kill and create the buffer manager buffer pool (and semaphore)
|
* Kill and create the buffer manager buffer pool (and semaphore)
|
||||||
* ----------------
|
|
||||||
*/
|
*/
|
||||||
CreateSpinlocks(IPCKeyGetSpinLockSemaphoreKey(key));
|
CreateSpinlocks(IPCKeyGetSpinLockSemaphoreKey(key));
|
||||||
|
|
||||||
@ -73,7 +73,7 @@ CreateSharedMemoryAndSemaphores(IPCKey key, int maxBackends)
|
|||||||
* moderately-accurate estimates for the big hogs, plus 100K for the
|
* moderately-accurate estimates for the big hogs, plus 100K for the
|
||||||
* stuff that's too small to bother with estimating.
|
* stuff that's too small to bother with estimating.
|
||||||
*/
|
*/
|
||||||
size = BufferShmemSize() + LockShmemSize(maxBackends);
|
size = BufferShmemSize() + LockShmemSize(maxBackends) + XLOGShmemSize();
|
||||||
#ifdef STABLE_MEMORY_STORAGE
|
#ifdef STABLE_MEMORY_STORAGE
|
||||||
size += MMShmemSize();
|
size += MMShmemSize();
|
||||||
#endif
|
#endif
|
||||||
@ -89,6 +89,7 @@ CreateSharedMemoryAndSemaphores(IPCKey key, int maxBackends)
|
|||||||
ShmemCreate(IPCKeyGetBufferMemoryKey(key), size);
|
ShmemCreate(IPCKeyGetBufferMemoryKey(key), size);
|
||||||
ShmemIndexReset();
|
ShmemIndexReset();
|
||||||
InitShmem(key, size);
|
InitShmem(key, size);
|
||||||
|
XLOGShmemInit();
|
||||||
InitBufferPool(key);
|
InitBufferPool(key);
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* bootstrap initialize spin locks so we can start to use the
|
||||||
* allocator and shmem index.
|
* allocator and shmem index.
|
||||||
*/
|
*/
|
||||||
if (!InitSpinLocks(ShmemBootstrap, IPCKeyGetSpinLockSemaphoreKey(key)))
|
InitSpinLocks();
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We have just allocated additional space for two spinlocks. Now
|
* We have just allocated additional space for two spinlocks. Now
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
#ifdef HAS_TEST_AND_SET
|
||||||
/* real spin lock implementations */
|
/* real spin lock implementations */
|
||||||
|
|
||||||
bool
|
void
|
||||||
CreateSpinlocks(IPCKey key)
|
CreateSpinlocks(IPCKey key)
|
||||||
{
|
{
|
||||||
/* the spin lock shared memory must have been created by now */
|
/* the spin lock shared memory must have been created by now */
|
||||||
return TRUE;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
void
|
||||||
InitSpinLocks(int init, IPCKey key)
|
InitSpinLocks(void)
|
||||||
{
|
{
|
||||||
extern SPINLOCK ShmemLock;
|
extern SPINLOCK ShmemLock;
|
||||||
extern SPINLOCK ShmemIndexLock;
|
extern SPINLOCK ShmemIndexLock;
|
||||||
@ -57,7 +57,8 @@ InitSpinLocks(int init, IPCKey key)
|
|||||||
extern SPINLOCK ProcStructLock;
|
extern SPINLOCK ProcStructLock;
|
||||||
extern SPINLOCK SInvalLock;
|
extern SPINLOCK SInvalLock;
|
||||||
extern SPINLOCK OidGenLockId;
|
extern SPINLOCK OidGenLockId;
|
||||||
|
extern SPINLOCK XidGenLockId;
|
||||||
|
extern SPINLOCK ControlFileLockId;
|
||||||
#ifdef STABLE_MEMORY_STORAGE
|
#ifdef STABLE_MEMORY_STORAGE
|
||||||
extern SPINLOCK MMCacheLock;
|
extern SPINLOCK MMCacheLock;
|
||||||
|
|
||||||
@ -71,12 +72,14 @@ InitSpinLocks(int init, IPCKey key)
|
|||||||
ProcStructLock = (SPINLOCK) PROCSTRUCTLOCKID;
|
ProcStructLock = (SPINLOCK) PROCSTRUCTLOCKID;
|
||||||
SInvalLock = (SPINLOCK) SINVALLOCKID;
|
SInvalLock = (SPINLOCK) SINVALLOCKID;
|
||||||
OidGenLockId = (SPINLOCK) OIDGENLOCKID;
|
OidGenLockId = (SPINLOCK) OIDGENLOCKID;
|
||||||
|
XidGenLockId = (SPINLOCK) XIDGENLOCKID;
|
||||||
|
ControlFileLockId = (SPINLOCK) CNTLFILELOCKID;
|
||||||
|
|
||||||
#ifdef STABLE_MEMORY_STORAGE
|
#ifdef STABLE_MEMORY_STORAGE
|
||||||
MMCacheLock = (SPINLOCK) MMCACHELOCKID;
|
MMCacheLock = (SPINLOCK) MMCACHELOCKID;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return TRUE;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef LOCKDEBUG
|
#ifdef LOCKDEBUG
|
||||||
@ -224,55 +227,17 @@ SpinIsLocked(SPINLOCK lock)
|
|||||||
* the spinlocks
|
* the spinlocks
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
bool
|
void
|
||||||
CreateSpinlocks(IPCKey key)
|
CreateSpinlocks(IPCKey key)
|
||||||
{
|
{
|
||||||
|
|
||||||
int status;
|
SpinLockId = IpcSemaphoreCreate(key, MAX_SPINS, IPCProtection,
|
||||||
IpcSemaphoreId semid;
|
IpcSemaphoreDefaultStartValue, 1);
|
||||||
|
|
||||||
semid = IpcSemaphoreCreate(key, MAX_SPINS, IPCProtection,
|
if (SpinLockId <= 0)
|
||||||
IpcSemaphoreDefaultStartValue, 1, &status);
|
elog(STOP, "CreateSpinlocks: cannot create spin locks");
|
||||||
if (status == IpcSemIdExist)
|
|
||||||
{
|
|
||||||
IpcSemaphoreKill(key);
|
|
||||||
elog(NOTICE, "Destroying old spinlock semaphore");
|
|
||||||
semid = IpcSemaphoreCreate(key, MAX_SPINS, IPCProtection,
|
|
||||||
IpcSemaphoreDefaultStartValue, 1, &status);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (semid >= 0)
|
return;
|
||||||
{
|
|
||||||
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;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -287,8 +252,8 @@ AttachSpinLocks(IPCKey key)
|
|||||||
* (SJCacheLock) for it. Same story for the main memory storage mgr.
|
* (SJCacheLock) for it. Same story for the main memory storage mgr.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
bool
|
void
|
||||||
InitSpinLocks(int init, IPCKey key)
|
InitSpinLocks(void)
|
||||||
{
|
{
|
||||||
extern SPINLOCK ShmemLock;
|
extern SPINLOCK ShmemLock;
|
||||||
extern SPINLOCK ShmemIndexLock;
|
extern SPINLOCK ShmemIndexLock;
|
||||||
@ -297,26 +262,14 @@ InitSpinLocks(int init, IPCKey key)
|
|||||||
extern SPINLOCK ProcStructLock;
|
extern SPINLOCK ProcStructLock;
|
||||||
extern SPINLOCK SInvalLock;
|
extern SPINLOCK SInvalLock;
|
||||||
extern SPINLOCK OidGenLockId;
|
extern SPINLOCK OidGenLockId;
|
||||||
|
extern SPINLOCK XidGenLockId;
|
||||||
|
extern SPINLOCK ControlFileLockId;
|
||||||
|
|
||||||
#ifdef STABLE_MEMORY_STORAGE
|
#ifdef STABLE_MEMORY_STORAGE
|
||||||
extern SPINLOCK MMCacheLock;
|
extern SPINLOCK MMCacheLock;
|
||||||
|
|
||||||
#endif
|
#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 */
|
/* These five (or six) spinlocks have fixed location is shmem */
|
||||||
ShmemLock = (SPINLOCK) SHMEMLOCKID;
|
ShmemLock = (SPINLOCK) SHMEMLOCKID;
|
||||||
ShmemIndexLock = (SPINLOCK) SHMEMINDEXLOCKID;
|
ShmemIndexLock = (SPINLOCK) SHMEMINDEXLOCKID;
|
||||||
@ -325,12 +278,14 @@ InitSpinLocks(int init, IPCKey key)
|
|||||||
ProcStructLock = (SPINLOCK) PROCSTRUCTLOCKID;
|
ProcStructLock = (SPINLOCK) PROCSTRUCTLOCKID;
|
||||||
SInvalLock = (SPINLOCK) SINVALLOCKID;
|
SInvalLock = (SPINLOCK) SINVALLOCKID;
|
||||||
OidGenLockId = (SPINLOCK) OIDGENLOCKID;
|
OidGenLockId = (SPINLOCK) OIDGENLOCKID;
|
||||||
|
XidGenLockId = (SPINLOCK) XIDGENLOCKID;
|
||||||
|
ControlFileLockId = (SPINLOCK) CNTLFILELOCKID;
|
||||||
|
|
||||||
#ifdef STABLE_MEMORY_STORAGE
|
#ifdef STABLE_MEMORY_STORAGE
|
||||||
MMCacheLock = (SPINLOCK) MMCACHELOCKID;
|
MMCacheLock = (SPINLOCK) MMCACHELOCKID;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return TRUE;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* HAS_TEST_AND_SET */
|
#endif /* HAS_TEST_AND_SET */
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* This is so that we can support more backends. (system-wide semaphore
|
||||||
* sets run out pretty fast.) -ay 4/95
|
* 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 <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
@ -70,7 +70,7 @@
|
|||||||
#include "storage/proc.h"
|
#include "storage/proc.h"
|
||||||
#include "utils/trace.h"
|
#include "utils/trace.h"
|
||||||
|
|
||||||
static void HandleDeadLock(int sig);
|
void HandleDeadLock(SIGNAL_ARGS);
|
||||||
static void ProcFreeAllSemaphores(void);
|
static void ProcFreeAllSemaphores(void);
|
||||||
|
|
||||||
#define DeadlockCheckTimer pg_options[OPT_DEADLOCKTIMEOUT]
|
#define DeadlockCheckTimer pg_options[OPT_DEADLOCKTIMEOUT]
|
||||||
@ -84,12 +84,6 @@ static void ProcFreeAllSemaphores(void);
|
|||||||
*/
|
*/
|
||||||
SPINLOCK ProcStructLock;
|
SPINLOCK ProcStructLock;
|
||||||
|
|
||||||
/*
|
|
||||||
* For cleanup routines. Don't cleanup if the initialization
|
|
||||||
* has not happened.
|
|
||||||
*/
|
|
||||||
static bool ProcInitialized = FALSE;
|
|
||||||
|
|
||||||
static PROC_HDR *ProcGlobal = NULL;
|
static PROC_HDR *ProcGlobal = NULL;
|
||||||
|
|
||||||
PROC *MyProc = NULL;
|
PROC *MyProc = NULL;
|
||||||
@ -167,8 +161,9 @@ InitProcGlobal(IPCKey key, int maxBackends)
|
|||||||
PROC_NSEMS_PER_SET,
|
PROC_NSEMS_PER_SET,
|
||||||
IPCProtection,
|
IPCProtection,
|
||||||
IpcSemaphoreDefaultStartValue,
|
IpcSemaphoreDefaultStartValue,
|
||||||
0,
|
0);
|
||||||
&semstat);
|
if (semId < 0)
|
||||||
|
elog(FATAL, "InitProcGlobal: IpcSemaphoreCreate failed");
|
||||||
/* mark this sema set allocated */
|
/* mark this sema set allocated */
|
||||||
ProcGlobal->freeSemMap[i] = (1 << PROC_NSEMS_PER_SET);
|
ProcGlobal->freeSemMap[i] = (1 << PROC_NSEMS_PER_SET);
|
||||||
}
|
}
|
||||||
@ -189,12 +184,6 @@ InitProcess(IPCKey key)
|
|||||||
unsigned long location,
|
unsigned long location,
|
||||||
myOffset;
|
myOffset;
|
||||||
|
|
||||||
/* ------------------
|
|
||||||
* Routine called if deadlock timer goes off. See ProcSleep()
|
|
||||||
* ------------------
|
|
||||||
*/
|
|
||||||
pqsignal(SIGALRM, HandleDeadLock);
|
|
||||||
|
|
||||||
SpinAcquire(ProcStructLock);
|
SpinAcquire(ProcStructLock);
|
||||||
|
|
||||||
/* attach to the free list */
|
/* attach to the free list */
|
||||||
@ -203,7 +192,7 @@ InitProcess(IPCKey key)
|
|||||||
if (!found)
|
if (!found)
|
||||||
{
|
{
|
||||||
/* this should not happen. InitProcGlobal() is called before this. */
|
/* this should not happen. InitProcGlobal() is called before this. */
|
||||||
elog(ERROR, "InitProcess: Proc Header uninitialized");
|
elog(STOP, "InitProcess: Proc Header uninitialized");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MyProc != NULL)
|
if (MyProc != NULL)
|
||||||
@ -271,8 +260,7 @@ InitProcess(IPCKey key)
|
|||||||
PROC_NSEMS_PER_SET,
|
PROC_NSEMS_PER_SET,
|
||||||
IPCProtection,
|
IPCProtection,
|
||||||
IpcSemaphoreDefaultStartValue,
|
IpcSemaphoreDefaultStartValue,
|
||||||
0,
|
0);
|
||||||
&semstat);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* we might be reusing a semaphore that belongs to a dead backend.
|
* we might be reusing a semaphore that belongs to a dead backend.
|
||||||
@ -316,14 +304,12 @@ InitProcess(IPCKey key)
|
|||||||
*/
|
*/
|
||||||
location = MAKE_OFFSET(MyProc);
|
location = MAKE_OFFSET(MyProc);
|
||||||
if ((!ShmemPIDLookup(MyProcPid, &location)) || (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;
|
MyProc->errType = NO_ERROR;
|
||||||
SHMQueueElemInit(&(MyProc->links));
|
SHMQueueElemInit(&(MyProc->links));
|
||||||
|
|
||||||
on_shmem_exit(ProcKill, (caddr_t) MyProcPid);
|
on_shmem_exit(ProcKill, (caddr_t) MyProcPid);
|
||||||
|
|
||||||
ProcInitialized = TRUE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -755,8 +741,8 @@ ProcAddLock(SHM_QUEUE *elem)
|
|||||||
* up my semaphore.
|
* up my semaphore.
|
||||||
* --------------------
|
* --------------------
|
||||||
*/
|
*/
|
||||||
static void
|
void
|
||||||
HandleDeadLock(int sig)
|
HandleDeadLock(SIGNAL_ARGS)
|
||||||
{
|
{
|
||||||
LOCK *mywaitlock;
|
LOCK *mywaitlock;
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* NOTES
|
||||||
* this is the "main" module of the postgres backend and
|
* this is the "main" module of the postgres backend and
|
||||||
@ -58,6 +58,7 @@
|
|||||||
#include "tcop/pquery.h"
|
#include "tcop/pquery.h"
|
||||||
#include "tcop/tcopprot.h"
|
#include "tcop/tcopprot.h"
|
||||||
#include "tcop/utility.h"
|
#include "tcop/utility.h"
|
||||||
|
#include "storage/proc.h"
|
||||||
#include "utils/ps_status.h"
|
#include "utils/ps_status.h"
|
||||||
#include "utils/temprel.h"
|
#include "utils/temprel.h"
|
||||||
#include "utils/trace.h"
|
#include "utils/trace.h"
|
||||||
@ -98,11 +99,20 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/*static bool EnableRewrite = true; , never changes why have it*/
|
/*static bool EnableRewrite = true; , never changes why have it*/
|
||||||
CommandDest whereToSendOutput;
|
CommandDest whereToSendOutput = Debug;
|
||||||
|
|
||||||
/* Define status buffer needed by PS_SET_STATUS */
|
/* Define status buffer needed by PS_SET_STATUS */
|
||||||
PS_DEFINE_BUFFER;
|
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 lockingOff;
|
||||||
extern int NBuffers;
|
extern int NBuffers;
|
||||||
|
|
||||||
@ -115,21 +125,8 @@ char relname[80]; /* current relation name */
|
|||||||
/* note: these declarations had better match tcopprot.h */
|
/* note: these declarations had better match tcopprot.h */
|
||||||
DLLIMPORT sigjmp_buf Warn_restart;
|
DLLIMPORT sigjmp_buf Warn_restart;
|
||||||
|
|
||||||
bool InError = true;
|
bool InError = false;
|
||||||
|
bool ExitAfterAbort = false;
|
||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
extern int NBuffers;
|
extern int NBuffers;
|
||||||
|
|
||||||
@ -773,6 +770,7 @@ handle_warn(SIGNAL_ARGS)
|
|||||||
void
|
void
|
||||||
quickdie(SIGNAL_ARGS)
|
quickdie(SIGNAL_ARGS)
|
||||||
{
|
{
|
||||||
|
PG_SETMASK(&BlockSig);
|
||||||
elog(NOTICE, "Message from PostgreSQL backend:"
|
elog(NOTICE, "Message from PostgreSQL backend:"
|
||||||
"\n\tThe Postmaster has informed me that some other backend"
|
"\n\tThe Postmaster has informed me that some other backend"
|
||||||
" died abnormally and possibly corrupted shared memory."
|
" 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.
|
* storage. Just nail the windows shut and get out of town.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
exit(0);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Abort transaction and exit
|
||||||
|
*/
|
||||||
void
|
void
|
||||||
die(SIGNAL_ARGS)
|
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 */
|
/* signal handler for floating point exception */
|
||||||
@ -907,6 +917,8 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[])
|
|||||||
#endif
|
#endif
|
||||||
DataDir = getenv("PGDATA");
|
DataDir = getenv("PGDATA");
|
||||||
|
|
||||||
|
SetProcessingMode(InitProcessing);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Try to get initial values for date styles and formats. Does not do
|
* 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
|
* a complete job, but should be good enough for backend. Cannot call
|
||||||
@ -1265,41 +1277,6 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[])
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------
|
|
||||||
* get user name (needed now in case it is the default database name)
|
|
||||||
* and check command line validity
|
|
||||||
* ----------------
|
|
||||||
*/
|
|
||||||
SetPgUserName();
|
|
||||||
userName = GetPgUserName();
|
|
||||||
|
|
||||||
if (IsUnderPostmaster)
|
|
||||||
{
|
|
||||||
/* noninteractive case: nothing should be left after switches */
|
|
||||||
if (errs || argc != optind || DBName == NULL)
|
|
||||||
{
|
|
||||||
usage(argv[0]);
|
|
||||||
proc_exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* interactive case: database name can be last arg on command line */
|
|
||||||
if (errs || argc - optind > 1)
|
|
||||||
{
|
|
||||||
usage(argv[0]);
|
|
||||||
proc_exit(1);
|
|
||||||
}
|
|
||||||
else if (argc - optind == 1)
|
|
||||||
DBName = argv[optind];
|
|
||||||
else if ((DBName = userName) == NULL)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "%s: USER undefined and no database specified\n",
|
|
||||||
argv[0]);
|
|
||||||
proc_exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ShowStats &&
|
if (ShowStats &&
|
||||||
(ShowParserStats || ShowPlannerStats || ShowExecutorStats))
|
(ShowParserStats || ShowPlannerStats || ShowExecutorStats))
|
||||||
{
|
{
|
||||||
@ -1317,6 +1294,90 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[])
|
|||||||
proc_exit(1);
|
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();
|
||||||
|
|
||||||
|
if (IsUnderPostmaster)
|
||||||
|
{
|
||||||
|
/* noninteractive case: nothing should be left after switches */
|
||||||
|
if (errs || argc != optind || DBName == NULL)
|
||||||
|
{
|
||||||
|
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]);
|
||||||
|
proc_exit(1);
|
||||||
|
}
|
||||||
|
else if (argc - optind == 1)
|
||||||
|
DBName = argv[optind];
|
||||||
|
else if ((DBName = userName) == NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: USER undefined and no database specified\n",
|
||||||
|
argv[0]);
|
||||||
|
proc_exit(1);
|
||||||
|
}
|
||||||
|
BaseInit();
|
||||||
|
sprintf(XLogDir, "%s%cpg_xlog", DataDir, SEP_CHAR);
|
||||||
|
sprintf(ControlFilePath, "%s%cpg_control", DataDir, SEP_CHAR);
|
||||||
|
StartupXLOG();
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set up additional info.
|
* Set up additional info.
|
||||||
*/
|
*/
|
||||||
@ -1367,19 +1428,15 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[])
|
|||||||
remote_info = remote_host = "unknown";
|
remote_info = remote_host = "unknown";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
/*
|
||||||
|
* Set process params for ps
|
||||||
/* ----------------
|
*/
|
||||||
* set process params for ps
|
|
||||||
* ----------------
|
|
||||||
*/
|
|
||||||
if (IsUnderPostmaster)
|
|
||||||
{
|
|
||||||
PS_INIT_STATUS(real_argc, real_argv, argv[0],
|
PS_INIT_STATUS(real_argc, real_argv, argv[0],
|
||||||
remote_info, userName, DBName);
|
remote_info, userName, DBName);
|
||||||
PS_SET_STATUS("startup");
|
PS_SET_STATUS("startup");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* print flags
|
* 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)
|
if (Verbose)
|
||||||
TPRINTF(TRACE_VERBOSE, "InitPostgres");
|
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 */
|
parser_input = makeStringInfo(); /* initialize input buffer */
|
||||||
|
|
||||||
/* ----------------
|
/*
|
||||||
* Set up handler for cancel-request signal, and
|
* Send this backend's cancellation info to the frontend.
|
||||||
* 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...
|
|
||||||
*/
|
|
||||||
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 &&
|
if (whereToSendOutput == Remote &&
|
||||||
PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
|
PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
|
||||||
{
|
{
|
||||||
@ -1485,40 +1508,41 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[])
|
|||||||
if (!IsUnderPostmaster)
|
if (!IsUnderPostmaster)
|
||||||
{
|
{
|
||||||
puts("\nPOSTGRES backend interactive interface ");
|
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
|
* Initialize the deferred trigger manager
|
||||||
* ----------------
|
|
||||||
*/
|
*/
|
||||||
if (DeferredTriggerInit() != 0)
|
if (DeferredTriggerInit() != 0)
|
||||||
ExitPostgres(1);
|
ExitPostgres(1);
|
||||||
|
|
||||||
/* ----------------
|
/*
|
||||||
* POSTGRES main processing loop begins here
|
* POSTGRES main processing loop begins here
|
||||||
*
|
*
|
||||||
* if an exception is encountered, processing resumes here
|
* If an exception is encountered, processing resumes here
|
||||||
* so we abort the current transaction and start a new one.
|
* so we abort the current transaction and start a new one.
|
||||||
*
|
|
||||||
* Note: elog(ERROR) does a siglongjmp() to transfer control here.
|
|
||||||
* See comments with the declaration of InError, above.
|
|
||||||
* ----------------
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
SetProcessingMode(NormalProcessing);
|
||||||
|
|
||||||
if (sigsetjmp(Warn_restart, 1) != 0)
|
if (sigsetjmp(Warn_restart, 1) != 0)
|
||||||
{
|
{
|
||||||
InError = true;
|
|
||||||
|
|
||||||
time(&tim);
|
time(&tim);
|
||||||
|
|
||||||
if (Verbose)
|
if (Verbose)
|
||||||
TPRINTF(TRACE_VERBOSE, "AbortCurrentTransaction");
|
TPRINTF(TRACE_VERBOSE, "AbortCurrentTransaction");
|
||||||
|
|
||||||
AbortCurrentTransaction();
|
AbortCurrentTransaction();
|
||||||
|
InError = false;
|
||||||
|
if (ExitAfterAbort)
|
||||||
|
{
|
||||||
|
ProcReleaseLocks(); /* Just to be sure... */
|
||||||
|
ExitPostgres(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
InError = false;
|
PG_SETMASK(&UnBlockSig);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Non-error queries loop here.
|
* Non-error queries loop here.
|
||||||
@ -1636,6 +1660,8 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[])
|
|||||||
*/
|
*/
|
||||||
case 'X':
|
case 'X':
|
||||||
case EOF:
|
case EOF:
|
||||||
|
if (!IsUnderPostmaster)
|
||||||
|
ShutdownXLOG();
|
||||||
pq_close();
|
pq_close();
|
||||||
proc_exit(0);
|
proc_exit(0);
|
||||||
break;
|
break;
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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 errno;
|
||||||
extern int sys_nerr;
|
extern int sys_nerr;
|
||||||
|
|
||||||
|
extern CommandDest whereToSendOutput;
|
||||||
|
|
||||||
#ifdef USE_SYSLOG
|
#ifdef USE_SYSLOG
|
||||||
/*
|
/*
|
||||||
@ -107,6 +108,19 @@ elog(int lev, const char *fmt, ...)
|
|||||||
if (lev <= DEBUG && Debugfile < 0)
|
if (lev <= DEBUG && Debugfile < 0)
|
||||||
return; /* ignore debug msgs if noplace to send */
|
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 */
|
/* choose message prefix and indent level */
|
||||||
switch (lev)
|
switch (lev)
|
||||||
{
|
{
|
||||||
@ -304,7 +318,7 @@ elog(int lev, const char *fmt, ...)
|
|||||||
|
|
||||||
#ifndef PG_STANDALONE
|
#ifndef PG_STANDALONE
|
||||||
|
|
||||||
if (lev > DEBUG && IsUnderPostmaster)
|
if (lev > DEBUG && whereToSendOutput == Remote)
|
||||||
{
|
{
|
||||||
/* Send IPC message to the front-end program */
|
/* Send IPC message to the front-end program */
|
||||||
char msgtype;
|
char msgtype;
|
||||||
@ -336,7 +350,7 @@ elog(int lev, const char *fmt, ...)
|
|||||||
pq_flush();
|
pq_flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lev > DEBUG && ! IsUnderPostmaster)
|
if (lev > DEBUG && whereToSendOutput != Remote)
|
||||||
{
|
{
|
||||||
/* We are running as an interactive backend, so just send
|
/* We are running as an interactive backend, so just send
|
||||||
* the message to stderr.
|
* the message to stderr.
|
||||||
@ -355,36 +369,29 @@ elog(int lev, const char *fmt, ...)
|
|||||||
/*
|
/*
|
||||||
* Perform error recovery action as specified by lev.
|
* Perform error recovery action as specified by lev.
|
||||||
*/
|
*/
|
||||||
if (lev == ERROR)
|
if (lev == ERROR || lev == FATAL)
|
||||||
{
|
{
|
||||||
if (InError)
|
if (InError)
|
||||||
{
|
{
|
||||||
/* error reported during error recovery; don't loop forever */
|
/* error reported during error recovery; don't loop forever */
|
||||||
elog(REALLYFATAL, "elog: error during error recovery, giving up!");
|
elog(REALLYFATAL, "elog: error during error recovery, giving up!");
|
||||||
}
|
}
|
||||||
|
InError = true;
|
||||||
|
ProcReleaseSpins(NULL); /* get rid of spinlocks we hold */
|
||||||
|
if (lev == FATAL)
|
||||||
|
{
|
||||||
|
if (IsInitProcessingMode())
|
||||||
|
ExitPostgres(0);
|
||||||
|
ExitAfterAbort = true;
|
||||||
|
}
|
||||||
/* exit to main loop */
|
/* exit to main loop */
|
||||||
ProcReleaseSpins(NULL); /* get rid of spinlocks we hold */
|
|
||||||
siglongjmp(Warn_restart, 1);
|
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)
|
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.
|
* process exit status and kill the other backends too.
|
||||||
*/
|
*/
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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)
|
* processing mode support stuff (used to be in pmod.c)
|
||||||
* ----------------------------------------------------------------
|
* ----------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
static ProcessingMode Mode = NoProcessing;
|
static ProcessingMode Mode = InitProcessing;
|
||||||
|
|
||||||
#ifdef NOT_USED
|
|
||||||
/*
|
|
||||||
* IsNoProcessingMode
|
|
||||||
* True iff processing mode is NoProcessing.
|
|
||||||
*/
|
|
||||||
bool
|
|
||||||
IsNoProcessingMode()
|
|
||||||
{
|
|
||||||
return (bool) (Mode == NoProcessing);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* IsBootstrapProcessingMode
|
* IsBootstrapProcessingMode
|
||||||
@ -186,13 +173,13 @@ IsNormalProcessingMode()
|
|||||||
* BadArg if called with invalid mode.
|
* BadArg if called with invalid mode.
|
||||||
*
|
*
|
||||||
* Note:
|
* Note:
|
||||||
* Mode is NoProcessing before the first time this is called.
|
* Mode is InitProcessing before the first time this is called.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
SetProcessingMode(ProcessingMode mode)
|
SetProcessingMode(ProcessingMode mode)
|
||||||
{
|
{
|
||||||
AssertArg(mode == NoProcessing || mode == BootstrapProcessing ||
|
AssertArg(mode == BootstrapProcessing || mode == InitProcessing ||
|
||||||
mode == InitProcessing || mode == NormalProcessing);
|
mode == NormalProcessing);
|
||||||
|
|
||||||
Mode = mode;
|
Mode = mode;
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* NOTES
|
||||||
* InitPostgres() is the function called from PostgresMain
|
* InitPostgres() is the function called from PostgresMain
|
||||||
@ -53,12 +53,13 @@
|
|||||||
#include "mb/pg_wchar.h"
|
#include "mb/pg_wchar.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void BaseInit(void);
|
||||||
|
|
||||||
static void VerifySystemDatabase(void);
|
static void VerifySystemDatabase(void);
|
||||||
static void VerifyMyDatabase(void);
|
static void VerifyMyDatabase(void);
|
||||||
static void ReverifyMyDatabase(char *name);
|
static void ReverifyMyDatabase(char *name);
|
||||||
static void InitCommunication(void);
|
static void InitCommunication(void);
|
||||||
static void InitMyDatabaseInfo(char *name);
|
static void InitMyDatabaseInfo(char *name);
|
||||||
static void InitStdio(void);
|
|
||||||
static void InitUserid(void);
|
static void InitUserid(void);
|
||||||
|
|
||||||
|
|
||||||
@ -385,37 +386,6 @@ InitCommunication()
|
|||||||
{
|
{
|
||||||
if (MyBackendTag == -1)
|
if (MyBackendTag == -1)
|
||||||
elog(FATAL, "InitCommunication: missing POSTID");
|
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)
|
else if (IsUnderPostmaster)
|
||||||
{
|
{
|
||||||
@ -439,12 +409,6 @@ InitCommunication()
|
|||||||
* initialize shared memory and semaphores appropriately.
|
* initialize shared memory and semaphores appropriately.
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
#ifdef EMULATE_UNDER_POSTMASTER
|
|
||||||
|
|
||||||
forcesharedmemory:
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!IsUnderPostmaster) /* postmaster already did this */
|
if (!IsUnderPostmaster) /* postmaster already did this */
|
||||||
{
|
{
|
||||||
PostgresIpcKey = key;
|
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
|
* InitPostgres
|
||||||
* Initialize POSTGRES.
|
* Initialize POSTGRES.
|
||||||
@ -477,8 +426,6 @@ InitStdio()
|
|||||||
*/
|
*/
|
||||||
extern int NBuffers;
|
extern int NBuffers;
|
||||||
|
|
||||||
bool PostgresIsInitialized = false;
|
|
||||||
|
|
||||||
int lockingOff = 0; /* backend -L switch */
|
int lockingOff = 0; /* backend -L switch */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -488,37 +435,11 @@ InitPostgres(char *name) /* database name */
|
|||||||
{
|
{
|
||||||
bool bootstrap; /* true if BootstrapProcessing */
|
bool bootstrap; /* true if BootstrapProcessing */
|
||||||
|
|
||||||
/* ----------------
|
/*
|
||||||
* see if we're running in BootstrapProcessing mode
|
* See if we're running in BootstrapProcessing mode
|
||||||
* ----------------
|
|
||||||
*/
|
*/
|
||||||
bootstrap = IsBootstrapProcessingMode();
|
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
|
* initialize the backend local portal stack used by
|
||||||
* internal PQ function calls. see src/lib/libpq/be-dumpdata.c
|
* internal PQ function calls. see src/lib/libpq/be-dumpdata.c
|
||||||
@ -528,14 +449,6 @@ InitPostgres(char *name) /* database name */
|
|||||||
*/
|
*/
|
||||||
be_portalinit();
|
be_portalinit();
|
||||||
|
|
||||||
/* ----------------
|
|
||||||
* attach to shared memory and semaphores, and initialize our
|
|
||||||
* input/output/debugging file descriptors.
|
|
||||||
* ----------------
|
|
||||||
*/
|
|
||||||
InitCommunication();
|
|
||||||
InitStdio();
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* initialize the local buffer manager
|
* 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
|
* 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());
|
AmiTransactionOverride(IsBootstrapProcessingMode());
|
||||||
LockDisable(true);
|
LockDisable(true);
|
||||||
@ -598,20 +507,18 @@ InitPostgres(char *name) /* database name */
|
|||||||
|
|
||||||
LockDisable(false);
|
LockDisable(false);
|
||||||
|
|
||||||
/* ----------------
|
/*
|
||||||
* Set up my per-backend PROC struct in shared memory.
|
* Set up my per-backend PROC struct in shared memory.
|
||||||
* ----------------
|
|
||||||
*/
|
*/
|
||||||
InitProcess(PostgresIpcKey);
|
InitProcess(PostgresIpcKey);
|
||||||
|
|
||||||
/* ----------------
|
/*
|
||||||
* Initialize my entry in the shared-invalidation manager's
|
* Initialize my entry in the shared-invalidation manager's
|
||||||
* array of per-backend data. (Formerly this came before
|
* array of per-backend data. (Formerly this came before
|
||||||
* InitProcess, but now it must happen after, because it uses
|
* InitProcess, but now it must happen after, because it uses
|
||||||
* MyProc.) Once I have done this, I am visible to other backends!
|
* 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();
|
InitSharedInvalidationState();
|
||||||
|
|
||||||
@ -622,16 +529,14 @@ InitPostgres(char *name) /* database name */
|
|||||||
MyBackendId);
|
MyBackendId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------
|
/*
|
||||||
* initialize the access methods.
|
* Initialize the access methods.
|
||||||
* Does not touch files (?) - thomas 1997-11-01
|
* Does not touch files (?) - thomas 1997-11-01
|
||||||
* ----------------
|
|
||||||
*/
|
*/
|
||||||
initam();
|
initam();
|
||||||
|
|
||||||
/* ----------------
|
/*
|
||||||
* initialize all the system catalog caches.
|
* Initialize all the system catalog caches.
|
||||||
* ----------------
|
|
||||||
*/
|
*/
|
||||||
zerocaches();
|
zerocaches();
|
||||||
|
|
||||||
@ -641,34 +546,19 @@ InitPostgres(char *name) /* database name */
|
|||||||
*/
|
*/
|
||||||
InitCatalogCache();
|
InitCatalogCache();
|
||||||
|
|
||||||
/* ----------------
|
/*
|
||||||
* set ourselves to the proper user id and figure out our postgres
|
* 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
|
* user id. If we ever add security so that we check for valid
|
||||||
* postgres users, we might do it here.
|
* postgres users, we might do it here.
|
||||||
* ----------------
|
|
||||||
*/
|
*/
|
||||||
InitUserid();
|
InitUserid();
|
||||||
|
|
||||||
/* ----------------
|
/*
|
||||||
* initialize local data in cache invalidation stuff
|
* Initialize local data in cache invalidation stuff
|
||||||
* ----------------
|
|
||||||
*/
|
*/
|
||||||
if (!bootstrap)
|
if (!bootstrap)
|
||||||
InitLocalInvalidateData();
|
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)
|
if (lockingOff)
|
||||||
LockDisable(true);
|
LockDisable(true);
|
||||||
|
|
||||||
@ -680,3 +570,30 @@ InitPostgres(char *name) /* database name */
|
|||||||
if (!bootstrap)
|
if (!bootstrap)
|
||||||
ReverifyMyDatabase(name);
|
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();
|
||||||
|
}
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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"
|
#include "utils/tqual.h"
|
||||||
|
|
||||||
extern bool PostgresIsInitialized;
|
|
||||||
|
|
||||||
SnapshotData SnapshotDirtyData;
|
SnapshotData SnapshotDirtyData;
|
||||||
Snapshot SnapshotDirty = &SnapshotDirtyData;
|
Snapshot SnapshotDirty = &SnapshotDirtyData;
|
||||||
|
|
||||||
@ -194,17 +192,6 @@ HeapTupleSatisfiesNow(HeapTupleHeader tuple)
|
|||||||
if (AMI_OVERRIDE)
|
if (AMI_OVERRIDE)
|
||||||
return true;
|
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_COMMITTED))
|
||||||
{
|
{
|
||||||
if (tuple->t_infomask & HEAP_XMIN_INVALID)
|
if (tuple->t_infomask & HEAP_XMIN_INVALID)
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
#
|
#
|
||||||
#
|
#
|
||||||
# IDENTIFICATION
|
# 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
|
mkdir $PGDATA/base
|
||||||
if [ $? -ne 0 ]; then exit 5; fi
|
if [ $? -ne 0 ]; then exit 5; fi
|
||||||
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
|
fi
|
||||||
|
|
||||||
#----------------------------------------------------------------------------
|
#----------------------------------------------------------------------------
|
||||||
@ -316,6 +322,7 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
BACKENDARGS="-boot -C -F -D$PGDATA $BACKEND_TALK_ARG"
|
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"
|
echo "Creating template database in $PGDATA/base/template1"
|
||||||
[ "$debug" -ne 0 ] && echo "Running: postgres $BACKENDARGS template1"
|
[ "$debug" -ne 0 ] && echo "Running: postgres $BACKENDARGS template1"
|
||||||
@ -323,7 +330,7 @@ echo "Creating template database in $PGDATA/base/template1"
|
|||||||
cat $TEMPLATE \
|
cat $TEMPLATE \
|
||||||
| sed -e "s/postgres PGUID/$POSTGRES_SUPERUSERNAME $POSTGRES_SUPERUID/" \
|
| sed -e "s/postgres PGUID/$POSTGRES_SUPERUSERNAME $POSTGRES_SUPERUID/" \
|
||||||
-e "s/PGUID/$POSTGRES_SUPERUID/" \
|
-e "s/PGUID/$POSTGRES_SUPERUID/" \
|
||||||
| postgres $BACKENDARGS template1
|
| postgres $FIRSTRUN template1
|
||||||
|
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
echo "$CMDNAME: could not create template database"
|
echo "$CMDNAME: could not create template database"
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
*
|
*
|
||||||
* Copyright (c) 1994, Regents of the University of California
|
* 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
|
* NOTES
|
||||||
* This shouldn't be in libpq, but the monitor and some other
|
* This shouldn't be in libpq, but the monitor and some other
|
||||||
@ -17,6 +17,28 @@
|
|||||||
#ifndef PQSIGNAL_H
|
#ifndef PQSIGNAL_H
|
||||||
#define 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);
|
typedef void (*pqsigfunc) (int);
|
||||||
|
|
||||||
extern pqsigfunc pqsignal(int signo, pqsigfunc func);
|
extern pqsigfunc pqsignal(int signo, pqsigfunc func);
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
*
|
*
|
||||||
* Copyright (c) 1994, Regents of the University of California
|
* 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
|
* NOTES
|
||||||
* some of the information in this file will be moved to
|
* 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:
|
* Description:
|
||||||
* There are four processing modes in POSTGRES. They are NoProcessing
|
* There are three processing modes in POSTGRES. They are
|
||||||
* or "none," BootstrapProcessing or "bootstrap," InitProcessing or
|
* "BootstrapProcessing or "bootstrap," InitProcessing or
|
||||||
* "initialization," and NormalProcessing or "normal."
|
* "initialization," and NormalProcessing or "normal."
|
||||||
*
|
*
|
||||||
* If a POSTGRES binary is in normal mode, then all code may be executed
|
* The first two processing modes are used during special times. When the
|
||||||
* 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
|
|
||||||
* system state indicates bootstrap processing, transactions are all given
|
* 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.
|
* is used during the initial generation of template databases.
|
||||||
*
|
*
|
||||||
* Finally, the execution state is in initialization mode until all normal
|
* Initialization mode until all normal initialization is complete.
|
||||||
* initialization is complete. Some code behaves differently when executed in
|
* Some code behaves differently when executed in this mode to enable
|
||||||
* this mode to enable system bootstrapping.
|
* system bootstrapping.
|
||||||
|
*
|
||||||
|
* If a POSTGRES binary is in normal mode, then all code may be executed
|
||||||
|
* normally.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
typedef enum ProcessingMode
|
typedef enum ProcessingMode
|
||||||
{
|
{
|
||||||
NoProcessing, /* "nothing" can be done */
|
|
||||||
BootstrapProcessing, /* bootstrap creation of template database */
|
BootstrapProcessing, /* bootstrap creation of template database */
|
||||||
InitProcessing, /* initializing system */
|
InitProcessing, /* initializing system */
|
||||||
NormalProcessing /* normal processing */
|
NormalProcessing /* normal processing */
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
*
|
*
|
||||||
* Copyright (c) 1994, Regents of the University of California
|
* 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
|
* NOTES
|
||||||
* This file is very architecture-specific. This stuff should actually
|
* 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,
|
extern IpcSemaphoreId IpcSemaphoreCreate(IpcSemaphoreKey semKey,
|
||||||
int semNum, int permission, int semStartValue,
|
int semNum, int permission, int semStartValue,
|
||||||
int removeOnExit, int *status);
|
int removeOnExit);
|
||||||
extern void IpcSemaphoreKill(IpcSemaphoreKey key);
|
extern void IpcSemaphoreKill(IpcSemaphoreKey key);
|
||||||
extern void IpcSemaphoreLock(IpcSemaphoreId semId, int sem, int lock);
|
extern void IpcSemaphoreLock(IpcSemaphoreId semId, int sem, int lock);
|
||||||
extern void IpcSemaphoreUnlock(IpcSemaphoreId semId, int sem, int lock);
|
extern void IpcSemaphoreUnlock(IpcSemaphoreId semId, int sem, int lock);
|
||||||
@ -105,6 +105,8 @@ typedef enum _LockId_
|
|||||||
BUFMGRLOCKID,
|
BUFMGRLOCKID,
|
||||||
LOCKLOCKID,
|
LOCKLOCKID,
|
||||||
OIDGENLOCKID,
|
OIDGENLOCKID,
|
||||||
|
XIDGENLOCKID,
|
||||||
|
CNTLFILELOCKID,
|
||||||
SHMEMLOCKID,
|
SHMEMLOCKID,
|
||||||
SHMEMINDEXLOCKID,
|
SHMEMINDEXLOCKID,
|
||||||
LOCKMGRLOCKID,
|
LOCKMGRLOCKID,
|
||||||
@ -147,6 +149,8 @@ typedef enum _LockId_
|
|||||||
|
|
||||||
PROCSTRUCTLOCKID,
|
PROCSTRUCTLOCKID,
|
||||||
OIDGENLOCKID,
|
OIDGENLOCKID,
|
||||||
|
XIDGENLOCKID,
|
||||||
|
CNTLFILELOCKID,
|
||||||
FIRSTFREELOCKID
|
FIRSTFREELOCKID
|
||||||
} _LockId_;
|
} _LockId_;
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
*
|
*
|
||||||
* Copyright (c) 1994, Regents of the University of California
|
* 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;
|
typedef int SPINLOCK;
|
||||||
|
|
||||||
extern bool CreateSpinlocks(IPCKey key);
|
extern void CreateSpinlocks(IPCKey key);
|
||||||
extern bool InitSpinLocks(int init, IPCKey key);
|
extern void InitSpinLocks(void);
|
||||||
extern void SpinAcquire(SPINLOCK lockid);
|
extern void SpinAcquire(SPINLOCK lockid);
|
||||||
extern void SpinRelease(SPINLOCK lockid);
|
extern void SpinRelease(SPINLOCK lockid);
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
*
|
*
|
||||||
* Copyright (c) 1994, Regents of the University of California
|
* 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
|
* OLD COMMENTS
|
||||||
* This file was created so that other c files could get the two
|
* This file was created so that other c files could get the two
|
||||||
@ -38,6 +38,7 @@
|
|||||||
#endif
|
#endif
|
||||||
extern DLLIMPORT sigjmp_buf Warn_restart;
|
extern DLLIMPORT sigjmp_buf Warn_restart;
|
||||||
extern bool InError;
|
extern bool InError;
|
||||||
|
extern bool ExitAfterAbort;
|
||||||
|
|
||||||
#ifndef BOOTSTRAP_INCLUDE
|
#ifndef BOOTSTRAP_INCLUDE
|
||||||
extern List *pg_parse_and_plan(char *query_string, Oid *typev, int nargs,
|
extern List *pg_parse_and_plan(char *query_string, Oid *typev, int nargs,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user