From 25fb2394060f66c06da48f0810297a138e4485f8 Mon Sep 17 00:00:00 2001 From: skimo Date: Sat, 2 Mar 2002 23:36:22 +0000 Subject: [PATCH] Initial db4 logging. Still incomplete: no line undo yet; no mark logging. Has the flushing db-workaround. --- common/common.h | 4 +- common/exf.c | 20 ++- common/exf.h | 7 +- common/log4.c | 460 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 486 insertions(+), 5 deletions(-) create mode 100644 common/log4.c diff --git a/common/common.h b/common/common.h index 0201a062..87cf553b 100644 --- a/common/common.h +++ b/common/common.h @@ -6,7 +6,7 @@ * * See the LICENSE file for redistribution information. * - * $Id: common.h,v 10.19 2001/11/02 11:52:06 skimo Exp $ (Berkeley) $Date: 2001/11/02 11:52:06 $ + * $Id: common.h,v 10.20 2002/03/02 23:36:22 skimo Exp $ (Berkeley) $Date: 2002/03/02 23:36:22 $ */ /* @@ -98,9 +98,9 @@ typedef enum { SEQ_ABBREV, SEQ_COMMAND, SEQ_INPUT } seq_t; #include "../ex/ex.h" /* Required by gs.h. */ #include "gs.h" /* Required by screen.h. */ #include "conv.h" +#include "log.h" /* Required by screen.h */ #include "screen.h" /* Required by exf.h. */ #include "exf.h" -#include "log.h" #include "mem.h" #include "vi_auto.h" diff --git a/common/exf.c b/common/exf.c index 6d602ccf..a30501cc 100644 --- a/common/exf.c +++ b/common/exf.c @@ -10,7 +10,7 @@ #include "config.h" #ifndef lint -static const char sccsid[] = "$Id: exf.c,v 10.69 2001/11/01 15:24:43 skimo Exp $ (Berkeley) $Date: 2001/11/01 15:24:43 $"; +static const char sccsid[] = "$Id: exf.c,v 10.70 2002/03/02 23:36:22 skimo Exp $ (Berkeley) $Date: 2002/03/02 23:36:22 $"; #endif /* not lint */ #include @@ -1580,6 +1580,12 @@ file_lock(SCR *sp, char *name, int *fdp, int fd, int iswrite) #endif } +#ifdef USE_DB4_LOGGING +#define VI_DB_INIT_LOG DB_INIT_LOG +#else +#define VI_DB_INIT_LOG 0 +#endif + static int db_setup(SCR *sp, EXF *ep) { @@ -1602,9 +1608,19 @@ db_setup(SCR *sp, EXF *ep) msgq(sp, M_ERR, "env_create"); goto err; } +#ifdef USE_DB4_LOGGING + if ((sp->db_error = vi_db_init_recover(env))) { + msgq(sp, M_DBERR, "init_recover"); + goto err; + } + if ((sp->db_error = __vi_init_recover(env))) { + msgq(sp, M_DBERR, "init_recover"); + goto err; + } +#endif if ((sp->db_error = env->open(env, path, DB_PRIVATE | DB_CREATE | DB_INIT_MPOOL | VI_DB_THREAD - /* | DB_INIT_LOG */, 0)) != 0) { + | VI_DB_INIT_LOG, 0)) != 0) { msgq(sp, M_DBERR, "env->open"); goto err; } diff --git a/common/exf.h b/common/exf.h index 5bd42f2e..ae8f88f2 100644 --- a/common/exf.h +++ b/common/exf.h @@ -6,7 +6,7 @@ * * See the LICENSE file for redistribution information. * - * $Id: exf.h,v 10.18 2001/11/01 13:15:21 skimo Exp $ (Berkeley) $Date: 2001/11/01 13:15:21 $ + * $Id: exf.h,v 10.19 2002/03/02 23:36:23 skimo Exp $ (Berkeley) $Date: 2002/03/02 23:36:23 $ */ /* Undo direction. */ /* @@ -27,6 +27,11 @@ struct _exf { DB *log; /* Log db structure. */ db_recno_t l_high; /* Log last + 1 record number. */ db_recno_t l_cur; /* Log current record number. */ +#ifdef USE_DB4_LOGGING + DB_LSN lsn_first; + DB_LSN lsn_high; /* LSN of last record. */ + DB_LSN lsn_cur; /* LSN of first record to undo. */ +#endif MARK l_cursor; /* Log cursor position. */ dir_t lundo; /* Last undo direction. */ WIN *l_win; /* Window owning transaction. */ diff --git a/common/log4.c b/common/log4.c new file mode 100644 index 00000000..3229bdb1 --- /dev/null +++ b/common/log4.c @@ -0,0 +1,460 @@ +/*- + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1992, 1993, 1994, 1995, 1996 + * Keith Bostic. All rights reserved. + * + * See the LICENSE file for redistribution information. + */ + +#include "config.h" + +#ifndef lint +static const char sccsid[] = "$Id: log4.c,v 10.1 2002/03/02 23:36:23 skimo Exp $"; +#endif /* not lint */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" + +/* + * The log consists of records, each containing a type byte and a variable + * length byte string, as follows: + * + * LOG_CURSOR_INIT MARK + * LOG_CURSOR_END MARK + * LOG_LINE_APPEND_F db_recno_t char * + * LOG_LINE_APPEND_B db_recno_t char * + * LOG_LINE_DELETE_F db_recno_t char * + * LOG_LINE_DELETE_B db_recno_t char * + * LOG_LINE_RESET_F db_recno_t char * + * LOG_LINE_RESET_B db_recno_t char * + * LOG_MARK LMARK + * + * We do before image physical logging. This means that the editor layer + * MAY NOT modify records in place, even if simply deleting or overwriting + * characters. Since the smallest unit of logging is a line, we're using + * up lots of space. This may eventually have to be reduced, probably by + * doing logical logging, which is a much cooler database phrase. + * + * The implementation of the historic vi 'u' command, using roll-forward and + * roll-back, is simple. Each set of changes has a LOG_CURSOR_INIT record, + * followed by a number of other records, followed by a LOG_CURSOR_END record. + * LOG_LINE_RESET records come in pairs. The first is a LOG_LINE_RESET_B + * record, and is the line before the change. The second is LOG_LINE_RESET_F, + * and is the line after the change. Roll-back is done by backing up to the + * first LOG_CURSOR_INIT record before a change. Roll-forward is done in a + * similar fashion. + * + * The 'U' command is implemented by rolling backward to a LOG_CURSOR_END + * record for a line different from the current one. It should be noted that + * this means that a subsequent 'u' command will make a change based on the + * new position of the log's cursor. This is okay, and, in fact, historic vi + * behaved that way. + */ + +static int log_cursor1 __P((SCR *, int)); + +/* + * log_init -- + * Initialize the logging subsystem. + * + * PUBLIC: int log_init __P((SCR *, EXF *)); + */ +int +log_init(SCR *sp, EXF *ep) +{ + DB_LOGC *logc; + DBT data; + size_t nlen; + + /* + * !!! + * ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER. + * + * Initialize the buffer. The logging subsystem has its own + * buffers because the global ones are almost by definition + * going to be in use when the log runs. + */ + sp->wp->l_lp = NULL; + sp->wp->l_len = 0; + ep->l_cursor.lno = 1; /* XXX Any valid recno. */ + ep->l_cursor.cno = 0; + ep->l_high = ep->l_cur = 1; + + if ((sp->db_error = ep->env->log_cursor(ep->env, &logc, 0)) + != 0) { + msgq(sp, M_DBERR, "env->log_cursor"); + F_SET(ep, F_NOLOG); + return (1); + } + nlen = 1024; +retry: + BINC_GOTO(sp, sp->wp->l_lp, sp->wp->l_len, nlen); + memset(&data, 0, sizeof(data)); + data.data = sp->wp->l_lp; + data.ulen = sp->wp->l_len; + data.flags = DB_DBT_USERMEM; + switch ((sp->db_error = + logc->get(logc, &ep->lsn_first, &data, DB_LAST))) { + case ENOMEM: + nlen = data.size; + goto retry; + default: +alloc_err: + msgq(sp, M_DBERR, "logc->get"); + F_SET(ep, F_NOLOG); + return (1); + case 0: + ; + } + MEMCPY(&ep->lsn_cur, &ep->lsn_first, 1); + MEMCPY(&ep->lsn_high, &ep->lsn_first, 1); + logc->close(logc, 0); + + ep->l_win = NULL; + /*LOCK_INIT(sp->wp, ep);*/ + + return (0); +} + +/* + * log_end -- + * Close the logging subsystem. + * + * PUBLIC: int log_end __P((SCR *, EXF *)); + */ +int +log_end(SCR *sp, EXF *ep) +{ + /* + * !!! + * ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER. + */ + /*LOCK_END(sp->wp, ep);*/ + if (sp->wp->l_lp != NULL) { + free(sp->wp->l_lp); + sp->wp->l_lp = NULL; + } + sp->wp->l_len = 0; + ep->l_cursor.lno = 1; /* XXX Any valid recno. */ + ep->l_cursor.cno = 0; + ep->l_high = ep->l_cur = 1; + return (0); +} + +/* + * log_cursor -- + * Log the current cursor position, starting an event. + * + * PUBLIC: int log_cursor __P((SCR *)); + */ +int +log_cursor(SCR *sp) +{ + EXF *ep; + + ep = sp->ep; + if (F_ISSET(ep, F_NOLOG)) + return (0); + + /* + * If any changes were made since the last cursor init, + * put out the ending cursor record. + */ + if (ep->l_cursor.lno == OOBLNO) { + if (ep->l_win && ep->l_win != sp->wp) + return 0; + ep->l_cursor.lno = sp->lno; + ep->l_cursor.cno = sp->cno; + ep->l_win = NULL; + return (log_cursor1(sp, LOG_CURSOR_END)); + } + ep->l_cursor.lno = sp->lno; + ep->l_cursor.cno = sp->cno; + return (0); +} + +/* + * log_cursor1 -- + * Actually push a cursor record out. + */ +static int +log_cursor1(SCR *sp, int type) +{ + DBT data, key; + EXF *ep; + + ep = sp->ep; + + /* + if (type == LOG_CURSOR_INIT && + LOCK_TRY(sp->wp, ep)) + return 1; + */ + + if (type == LOG_CURSOR_INIT && + (sp->db_error = __vi_log_truncate(ep)) != 0) { + msgq(sp, M_DBERR, "truncate"); + return 1; + } + if ((sp->db_error = + __vi_cursor_log(ep->env, NULL, &ep->lsn_cur, 0, type, + ep->l_cursor.lno, ep->l_cursor.cno)) != 0) { + msgq(sp, M_DBERR, "cursor_log"); + return 1; + } + if (type == LOG_CURSOR_END) { + MEMCPY(&ep->lsn_high, &ep->lsn_cur, 1); + /* XXXX should not be needed */ + ep->env->log_flush(ep->env, NULL); + } + +#if defined(DEBUG) && 0 + vtrace(sp, "%lu: %s: %u/%u\n", ep->l_cur, + type == LOG_CURSOR_INIT ? "log_cursor_init" : "log_cursor_end", + sp->lno, sp->cno); +#endif + /* Reset high water mark. */ + ep->l_high = ++ep->l_cur; + + /* + if (type == LOG_CURSOR_END) + LOCK_UNLOCK(sp->wp, ep); + */ + return (0); +} + +/* + * log_line -- + * Log a line change. + * + * PUBLIC: int log_line __P((SCR *, db_recno_t, u_int)); + */ +int +log_line(SCR *sp, db_recno_t lno, u_int action) +{ + DBT data, key; + EXF *ep; + size_t len; + CHAR_T *lp; + db_recno_t lcur; + + ep = sp->ep; + if (F_ISSET(ep, F_NOLOG)) + return (0); + + /* + * XXX + * + * Kluge for vi. Clear the EXF undo flag so that the + * next 'u' command does a roll-back, regardless. + */ + F_CLR(ep, F_UNDO); + + /* Put out one initial cursor record per set of changes. */ + if (ep->l_cursor.lno != OOBLNO) { + if (log_cursor1(sp, LOG_CURSOR_INIT)) + return (1); + ep->l_cursor.lno = OOBLNO; + ep->l_win = sp->wp; + } /*else if (ep->l_win != sp->wp) { + printf("log_line own: %p, this: %p\n", ep->l_win, sp->wp); + return 1; + }*/ + + if ((sp->db_error = + __vi_change_log(ep->env, NULL, &ep->lsn_cur, 0, action, + lno)) != 0) { + msgq(sp, M_DBERR, "change_log"); + return 1; + } + +#if defined(DEBUG) && 0 + switch (action) { + case LOG_LINE_APPEND_F: + vtrace(sp, "%u: log_line: append_f: %lu {%u}\n", + ep->l_cur, lno, len); + break; + case LOG_LINE_APPEND_B: + vtrace(sp, "%u: log_line: append_b: %lu {%u}\n", + ep->l_cur, lno, len); + break; + case LOG_LINE_DELETE_F: + vtrace(sp, "%lu: log_line: delete_f: %lu {%u}\n", + ep->l_cur, lno, len); + break; + case LOG_LINE_DELETE_B: + vtrace(sp, "%lu: log_line: delete_b: %lu {%u}\n", + ep->l_cur, lno, len); + break; + case LOG_LINE_RESET_F: + vtrace(sp, "%lu: log_line: reset_f: %lu {%u}\n", + ep->l_cur, lno, len); + break; + case LOG_LINE_RESET_B: + vtrace(sp, "%lu: log_line: reset_b: %lu {%u}\n", + ep->l_cur, lno, len); + break; + } +#endif + /* Reset high water mark. */ + ep->l_high = ++ep->l_cur; + + return (0); +} + +/* + * log_mark -- + * Log a mark position. For the log to work, we assume that there + * aren't any operations that just put out a log record -- this + * would mean that undo operations would only reset marks, and not + * cause any other change. + * + * PUBLIC: int log_mark __P((SCR *, LMARK *)); + */ +int +log_mark(SCR *sp, LMARK *lmp) +{ + DBT data, key; + EXF *ep; + + ep = sp->ep; + if (F_ISSET(ep, F_NOLOG)) + return (0); + + /* Put out one initial cursor record per set of changes. */ + if (ep->l_cursor.lno != OOBLNO) { + if (log_cursor1(sp, LOG_CURSOR_INIT)) + return (1); + ep->l_cursor.lno = OOBLNO; + ep->l_win = sp->wp; + } + + /* XXXX DB4 specific code */ + +#if defined(DEBUG) && 0 + vtrace(sp, "%lu: mark %c: %lu/%u\n", + ep->l_cur, lmp->name, lmp->lno, lmp->cno); +#endif + /* Reset high water mark. */ + ep->l_high = ++ep->l_cur; + return (0); +} + +/* + * Log_backward -- + * Roll the log backward one operation. + * + * PUBLIC: int log_backward __P((SCR *, MARK *)); + */ +int +log_backward(SCR *sp, MARK *rp) +{ + EXF *ep; + LMARK lm; + MARK m; + db_recno_t lno; + int didop; + u_char *p; + size_t size; + + ep = sp->ep; + if (F_ISSET(ep, F_NOLOG)) { + msgq(sp, M_ERR, + "010|Logging not being performed, undo not possible"); + return (1); + } + + if (log_compare(&ep->lsn_cur, &ep->lsn_first) <= 0) { + msgq(sp, M_BERR, "011|No changes to undo"); + return (1); + } + return __vi_log_traverse(sp, DB_TXN_BACKWARD_ROLL, rp); +} + +/* + * Log_setline -- + * Reset the line to its original appearance. + * + * XXX + * There's a bug in this code due to our not logging cursor movements + * unless a change was made. If you do a change, move off the line, + * then move back on and do a 'U', the line will be restored to the way + * it was before the original change. + * + * PUBLIC: int log_setline __P((SCR *)); + */ +int +log_setline(SCR *sp) +{ + EXF *ep; + LMARK lm; + MARK m; + db_recno_t lno; + u_char *p; + size_t size; + + ep = sp->ep; + if (F_ISSET(ep, F_NOLOG)) { + msgq(sp, M_ERR, + "012|Logging not being performed, undo not possible"); + return (1); + } + + if (ep->l_cur == 1) + return (1); + + if (ep->l_win && ep->l_win != sp->wp) { + ex_emsg(sp, NULL, EXM_LOCKED); + return 1; + } + ep->l_win = sp->wp; + + F_SET(ep, F_NOLOG); /* Turn off logging. */ + + /* XXXX DB4 code */ + + return 0; +} + +/* + * Log_forward -- + * Roll the log forward one operation. + * + * PUBLIC: int log_forward __P((SCR *, MARK *)); + */ +int +log_forward(SCR *sp, MARK *rp) +{ + EXF *ep; + LMARK lm; + MARK m; + db_recno_t lno; + int didop; + u_char *p; + size_t size; + + ep = sp->ep; + if (F_ISSET(ep, F_NOLOG)) { + msgq(sp, M_ERR, + "013|Logging not being performed, roll-forward not possible"); + return (1); + } + + if (log_compare(&ep->lsn_cur, &ep->lsn_high) >= 0) { + msgq(sp, M_BERR, "014|No changes to re-do"); + return (1); + } + return __vi_log_traverse(sp, DB_TXN_FORWARD_ROLL, rp); +} -- 2.11.4.GIT