* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Header: /cvsroot/pgsql/src/backend/access/transam/xlog.c,v 1.23 2000/11/03 11:39:35 vadim Exp $
+ * $Header: /cvsroot/pgsql/src/backend/access/transam/xlog.c,v 1.24 2000/11/05 22:50:19 vadim Exp $
*
*-------------------------------------------------------------------------
*/
#include <errno.h>
#include <sys/stat.h>
#include <sys/time.h>
+#include <sys/types.h>
+#include <dirent.h>
#include "postgres.h"
int XLOG_DEBUG = 1;
+/* To read/update control file and create new log file */
SPINLOCK ControlFileLockId;
+
+/* To generate new xid */
SPINLOCK XidGenLockId;
extern VariableCache ShmemVariableCache;
typedef struct XLogCtlData
{
- XLogCtlInsert Insert;
- XLgwrRqst LgwrRqst;
- XLgwrResult LgwrResult;
- XLogCtlWrite Write;
- char *pages;
- XLogRecPtr *xlblocks; /* 1st byte ptr-s + BLCKSZ */
- uint32 XLogCacheByte;
- uint32 XLogCacheBlck;
- StartUpID ThisStartUpID;
+ XLogCtlInsert Insert;
+ XLgwrRqst LgwrRqst;
+ XLgwrResult LgwrResult;
+ XLogCtlWrite Write;
+ char *pages;
+ XLogRecPtr *xlblocks; /* 1st byte ptr-s + BLCKSZ */
+ uint32 XLogCacheByte;
+ uint32 XLogCacheBlck;
+ StartUpID ThisStartUpID;
#ifdef HAS_TEST_AND_SET
- slock_t insert_lck;
- slock_t info_lck;
- slock_t lgwr_lck;
+ slock_t insert_lck;
+ slock_t info_lck;
+ slock_t lgwr_lck;
+ slock_t chkp_lck; /* checkpoint lock */
#endif
} XLogCtlData;
uint32 blcksz; /* block size for this DB */
uint32 relseg_size; /* blocks per segment of large relation */
uint32 catalog_version_no; /* internal version number */
+ char archdir[MAXPGPATH]; /* where to move offline log files */
/*
* MORE DATA FOLLOWS AT THE END OF THIS STRUCTURE - locations of data
snprintf(path, MAXPGPATH, "%s%c%08X%08X", \
XLogDir, SEP_CHAR, log, seg)
+#define XLogTempFileName(path, log, seg) \
+ snprintf(path, MAXPGPATH, "%s%cT%08X%08X", \
+ XLogDir, SEP_CHAR, log, seg)
+
#define PrevBufIdx(curridx) \
((curridx == 0) ? XLogCtl->XLogCacheBlck : (curridx - 1))
static void GetFreeXLBuffer(void);
static void XLogWrite(char *buffer);
-static int XLogFileInit(uint32 log, uint32 seg);
+static int XLogFileInit(uint32 log, uint32 seg, bool *usexistent);
static int XLogFileOpen(uint32 log, uint32 seg, bool econt);
static XLogRecord *ReadRecord(XLogRecPtr *RecPtr, char *buffer);
static char *str_time(time_t tnow);
char *from;
uint32 wcnt = 0;
int i = 0;
+ bool usexistent;
for (; XLByteLT(LgwrResult.Write, LgwrRqst.Write);)
{
logId = LgwrResult.Write.xlogid;
logSeg = (LgwrResult.Write.xrecoff - 1) / XLogSegSize;
logOff = 0;
- logFile = XLogFileInit(logId, logSeg);
SpinAcquire(ControlFileLockId);
+ /* create/use new log file */
+ usexistent = true;
+ logFile = XLogFileInit(logId, logSeg, &usexistent);
ControlFile->logId = logId;
ControlFile->logSeg = logSeg + 1;
ControlFile->time = time(NULL);
UpdateControlFile();
SpinRelease(ControlFileLockId);
+ if (!usexistent) /* there was no file */
+ elog(LOG, "XLogWrite: had to create new log file - "
+ "you probably should do checkpoints more often");
}
if (logFile < 0)
}
static int
-XLogFileInit(uint32 log, uint32 seg)
+XLogFileInit(uint32 log, uint32 seg, bool *usexistent)
{
char path[MAXPGPATH];
+ char tpath[MAXPGPATH];
int fd;
XLogFileName(path, log, seg);
+
+ /*
+ * Try to use existent file (checkpoint maker
+ * creates it sometime).
+ */
+ if (*usexistent)
+ {
+ fd = BasicOpenFile(path, O_RDWR | PG_BINARY, S_IRUSR | S_IWUSR);
+ if (fd < 0)
+ {
+ if (errno != ENOENT)
+ elog(STOP, "InitOpen(logfile %u seg %u) failed: %d",
+ logId, logSeg, errno);
+ }
+ else
+ return(fd);
+ *usexistent = false;
+ }
+
+ XLogTempFileName(tpath, log, seg);
+ unlink(tpath);
unlink(path);
- fd = BasicOpenFile(path, O_RDWR | O_CREAT | O_EXCL | PG_BINARY, S_IRUSR | S_IWUSR);
+ fd = BasicOpenFile(tpath, O_RDWR | O_CREAT | O_EXCL | PG_BINARY, S_IRUSR | S_IWUSR);
if (fd < 0)
- elog(STOP, "Init(logfile %u seg %u) failed: %d",
+ elog(STOP, "InitCreate(logfile %u seg %u) failed: %d",
logId, logSeg, errno);
if (lseek(fd, XLogSegSize - 1, SEEK_SET) != (off_t) (XLogSegSize - 1))
elog(STOP, "Lseek(logfile %u seg %u off %u) failed: %d",
log, seg, 0, errno);
+ close(fd);
+ link(tpath, path);
+ unlink(tpath);
+
+ fd = BasicOpenFile(path, O_RDWR | PG_BINARY, S_IRUSR | S_IWUSR);
+ if (fd < 0)
+ elog(STOP, "InitReopen(logfile %u seg %u) failed: %d",
+ logId, logSeg, errno);
+
return (fd);
}
return (fd);
}
+/*
+ * (Re)move offline log files older or equal to passwd one
+ */
+static void
+MoveOfflineLogs(char *archdir, uint32 _logId, uint32 _logSeg)
+{
+ DIR *xldir;
+ struct dirent *xlde;
+ char lastoff[32];
+ char path[MAXPGPATH];
+
+ Assert(archdir[0] == 0); /* ! implemented yet */
+
+ xldir = opendir(XLogDir);
+ if (xldir == NULL)
+ elog(STOP, "MoveOfflineLogs: cannot open xlog dir: %d", errno);
+
+ sprintf(lastoff, "%08X%08X", _logId, _logSeg);
+
+ errno = 0;
+ while ((xlde = readdir(xldir)) != NULL)
+ {
+ if (strlen(xlde->d_name) != 16 ||
+ strspn(xlde->d_name, "0123456789ABCDEF") != 16)
+ continue;
+ if (strcmp(xlde->d_name, lastoff) > 0)
+ {
+ elog(LOG, "MoveOfflineLogs: skip %s", xlde->d_name);
+ errno = 0;
+ continue;
+ }
+ elog(LOG, "MoveOfflineLogs: %s %s", (archdir[0]) ?
+ "archive" : "remove", xlde->d_name);
+ sprintf(path, "%s%c%s", XLogDir, SEP_CHAR, xlde->d_name);
+ if (archdir[0] != 0)
+ unlink(path);
+ errno = 0;
+ }
+ if (errno)
+ elog(STOP, "MoveOfflineLogs: cannot read xlog dir: %d", errno);
+ closedir(xldir);
+}
+
static XLogRecord *
ReadRecord(XLogRecPtr *RecPtr, char *buffer)
{
int fd;
char buffer[BLCKSZ];
CheckPoint checkPoint;
+ bool usexistent = false;
#ifdef XLOG
XLogPageHeader page = (XLogPageHeader) buffer;
record->xl_rmid = RM_XLOG_ID;
memcpy((char *) record + SizeOfXLogRecord, &checkPoint, sizeof(checkPoint));
- logFile = XLogFileInit(0, 0);
+ logFile = XLogFileInit(0, 0, &usexistent);
if (write(logFile, buffer, BLCKSZ) != BLCKSZ)
elog(STOP, "BootStrapXLOG failed to write logfile: %d", errno);
S_INIT_LOCK(&(XLogCtl->insert_lck));
S_INIT_LOCK(&(XLogCtl->info_lck));
S_INIT_LOCK(&(XLogCtl->lgwr_lck));
+ S_INIT_LOCK(&(XLogCtl->chkp_lck));
/*
* Open/read Control file
elog(LOG, "Data Base System shut down at %s", str_time(time(NULL)));
}
+extern XLogRecPtr GetUndoRecPtr(void);
+
void
CreateCheckPoint(bool shutdown)
{
XLogCtlInsert *Insert = &XLogCtl->Insert;
uint32 freespace;
uint16 curridx;
+ uint32 _logId;
+ uint32 _logSeg;
+ char archdir[MAXPGPATH];
+
+ if (MyLastRecPtr.xrecoff != 0)
+ elog(ERROR, "CreateCheckPoint: cannot be called inside transaction block");
+
+ while (TAS(&(XLogCtl->chkp_lck)))
+ {
+ struct timeval delay = {2, 0};
+
+ if (shutdown)
+ elog(STOP, "Checkpoint lock is busy while data base is shutting down");
+ (void) select(0, NULL, NULL, NULL, &delay);
+ }
memset(&checkPoint, 0, sizeof(checkPoint));
if (shutdown)
/* Get REDO record ptr */
while (TAS(&(XLogCtl->insert_lck)))
{
- struct timeval delay = {0, 5000};
+ struct timeval delay = {1, 0};
if (shutdown)
elog(STOP, "XLog insert lock is busy while data base is shutting down");
FlushBufferPool();
/* Get UNDO record ptr - should use oldest of PROC->logRec */
- checkPoint.undo.xrecoff = 0;
+ checkPoint.undo = GetUndoRecPtr();
if (shutdown && checkPoint.undo.xrecoff != 0)
elog(STOP, "Active transaction while data base is shutting down");
SpinAcquire(ControlFileLockId);
if (shutdown)
ControlFile->state = DB_SHUTDOWNED;
-
#ifdef XLOG
+ else /* create new log file */
+ {
+ if (recptr.xrecoff % XLogSegSize >=
+ (uint32) (0.75 * XLogSegSize))
+ {
+ int lf;
+ bool usexistent = true;
+
+ _logId = recptr.xlogid;
+ _logSeg = recptr.xrecoff / XLogSegSize;
+ if (_logSeg >= XLogLastSeg)
+ {
+ _logId++;
+ _logSeg = 0;
+ }
+ else
+ _logSeg++;
+ lf = XLogFileInit(_logId, _logSeg, &usexistent);
+ close(lf);
+ }
+ }
+
ControlFile->checkPoint = MyLastRecPtr;
+
+ _logId = ControlFile->logId;
+ _logSeg = ControlFile->logSeg - 1;
+ strcpy(archdir, ControlFile->archdir);
+
#else
ControlFile->checkPoint.xlogid = 0;
ControlFile->checkPoint.xrecoff = SizeOfXLogPHD;
UpdateControlFile();
SpinRelease(ControlFileLockId);
+#ifdef XLOG
+ /*
+ * Delete offline log files. Get oldest online
+ * log file from undo rec if it's valid.
+ */
+ if (checkPoint.undo.xrecoff != 0)
+ {
+ _logId = checkPoint.undo.xlogid;
+ _logSeg = checkPoint.undo.xrecoff / XLogSegSize;
+ }
+ if (_logId || _logSeg)
+ {
+ if (_logSeg)
+ _logSeg--;
+ else
+ {
+ _logId--;
+ _logSeg = 0;
+ }
+ MoveOfflineLogs(archdir, _logId, _logSeg);
+ }
+
+ S_UNLOCK(&(XLogCtl->chkp_lck));
+
+ MyLastRecPtr.xrecoff = 0; /* to avoid commit record */
+#endif
+
return;
}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.204 2000/11/05 00:15:54 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.205 2000/11/05 22:50:20 vadim Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
RenameStmt, RevokeStmt, RuleActionStmt, RuleActionStmtOrEmpty,
RuleStmt, SelectStmt, SetSessionStmt, TransactionStmt, TruncateStmt,
UnlistenStmt, UpdateStmt, VacuumStmt, VariableResetStmt,
- VariableSetStmt, VariableShowStmt, ViewStmt
+ VariableSetStmt, VariableShowStmt, ViewStmt, CheckPointStmt
%type <node> select_no_parens, select_clause, simple_select
/* Keywords (in SQL92 reserved words) */
%token ABSOLUTE, ACTION, ADD, ALL, ALTER, AND, ANY, AS, ASC,
BEGIN_TRANS, BETWEEN, BOTH, BY,
- CASCADE, CASE, CAST, CHAR, CHARACTER, CHECK, CLOSE,
+ CASCADE, CASE, CAST, CHAR, CHARACTER, CHECK, CLOSE,
COALESCE, COLLATE, COLUMN, COMMIT,
CONSTRAINT, CONSTRAINTS, CREATE, CROSS, CURRENT_DATE,
CURRENT_TIME, CURRENT_TIMESTAMP, CURRENT_USER, CURSOR,
*/
%token ABORT_TRANS, ACCESS, AFTER, AGGREGATE, ANALYZE,
BACKWARD, BEFORE, BINARY, BIT,
- CACHE, CLUSTER, COMMENT, COPY, CREATEDB, CREATEUSER, CYCLE,
+ CACHE, CHECKPOINT, CLUSTER, COMMENT, COPY, CREATEDB, CREATEUSER, CYCLE,
DATABASE, DELIMITERS, DO,
EACH, ENCODING, EXCLUSIVE, EXPLAIN, EXTEND,
FORCE, FORWARD, FUNCTION, HANDLER,
| VariableShowStmt
| VariableResetStmt
| ConstraintsSetStmt
+ | CheckPointStmt
| /*EMPTY*/
{ $$ = (Node *)NULL; }
;
;
+/*
+ * Checkpoint statement
+ */
+CheckPointStmt: CHECKPOINT
+ {
+ CheckPointStmt *n = makeNode(CheckPointStmt);
+ $$ = (Node *)n;
+ }
+ ;
+
/*****************************************************************************
*
* ALTER TABLE variations
| CACHE { $$ = "cache"; }
| CASCADE { $$ = "cascade"; }
| CHAIN { $$ = "chain"; }
+ | CHECKPOINT { $$ = "checkpoint"; }
| CLOSE { $$ = "close"; }
| COMMENT { $$ = "comment"; }
| COMMIT { $$ = "commit"; }