Dimitri Fontaine <[email protected]> writes:
> Also, should I try to send a patch implementing my proposal (internal
> command exposed as a function at the SQL level, and while at it, maybe
> the internal command "pg_archive_bypass" to mimic /usr/bin/true as an
> archive_command)?
I had to have a try at it, even if quick and dirty. I've not tried to
code the pg_archive_bypass internal command for lack of discussion, but
I still think it would be great to have it.
So here's a "see my idea in code" patch, that put the previous code by
Simon into a backend function. As the goal was not to adapt the existing
code intended as external to use the internal APIs, you'll find it quite
ugly I'm sure.
For example, this #define XLOG_DATA_FNAME_LEN has to go away, but that
won't help having the idea accepted or not, and as I'm only warming up,
I didn't tackle the problem. If you want me to do it, I'd appreciate
some guidance as how to, though.
It goes like this:
dim=# select pg_switch_xlog();
pg_switch_xlog
----------------
0/1000098
(1 row)
dim=# select pg_archive_cleanup('0/1000098');
DEBUG: removing "pg_xlog/000000010000000000000000"
DEBUG: removing "pg_xlog/000000010000000000000001"
pg_archive_cleanup
--------------------
t
(1 row)
I hope you too will find this way of interfacing is easier to deal with
for everybody (from code maintenance to user settings).
Regards,
--
dim
*** a/src/backend/access/transam/xlog.c
--- b/src/backend/access/transam/xlog.c
***************
*** 599,605 **** static bool read_backup_label(XLogRecPtr *checkPointLoc);
static void rm_redo_error_callback(void *arg);
static int get_sync_bit(int method);
-
/*
* Insert an XLOG record having the specified RMID and info bytes,
* with the body of the record being the data chunk(s) described by
--- 599,604 ----
***************
*** 3056,3062 **** not_available:
}
/*
! * Attempt to execute an external shell command during recovery.
*
* 'command' is the shell command to be executed, 'commandName' is a
* human-readable name describing the command emitted in the logs. If
--- 3055,3065 ----
}
/*
! * Attempt to execute an external shell command during recovery, or an
! * internal one if given one.
! *
! * There's only one supported internal commands today, which is named
! * "pg_archive_cleanup".
*
* 'command' is the shell command to be executed, 'commandName' is a
* human-readable name describing the command emitted in the logs. If
***************
*** 3094,3145 **** ExecuteRecoveryCommand(char *command, char *commandName, bool failOnSignal)
LWLockRelease(ControlFileLock);
/*
! * construct the command to be executed
*/
! dp = xlogRecoveryCmd;
! endp = xlogRecoveryCmd + MAXPGPATH - 1;
! *endp = '\0';
! for (sp = command; *sp; sp++)
{
! if (*sp == '%')
{
! switch (sp[1])
{
! case 'r':
! /* %r: filename of last restartpoint */
! sp++;
! StrNCpy(dp, lastRestartPointFname, endp - dp);
! dp += strlen(dp);
! break;
! case '%':
! /* convert %% to a single % */
! sp++;
! if (dp < endp)
! *dp++ = *sp;
! break;
! default:
! /* otherwise treat the % as not special */
! if (dp < endp)
! *dp++ = *sp;
! break;
}
}
! else
! {
! if (dp < endp)
! *dp++ = *sp;
! }
! }
! *dp = '\0';
! ereport(DEBUG3,
! (errmsg_internal("executing %s \"%s\"", commandName, command)));
- /*
- * execute the constructed command
- */
- rc = system(xlogRecoveryCmd);
if (rc != 0)
{
/*
--- 3097,3164 ----
LWLockRelease(ControlFileLock);
/*
! * if the command is internal, call the function
*/
! if( strcmp(command, "pg_archive_cleanup") == 0 )
! {
! text *restart_filename = cstring_to_text(lastRestartPointFname);
! rc = DatumGetBool(DirectFunctionCall1(pg_archive_cleanup,
! restart_filename)) ? 0 : 1;
! ereport(DEBUG3,
! (errmsg_internal("calling %s \"%s\"", commandName, command)));
! }
! else
{
! /*
! * construct the command to be executed
! */
! dp = xlogRecoveryCmd;
! endp = xlogRecoveryCmd + MAXPGPATH - 1;
! *endp = '\0';
!
! for (sp = command; *sp; sp++)
{
! if (*sp == '%')
{
! switch (sp[1])
! {
! case 'r':
! /* %r: filename of last restartpoint */
! sp++;
! StrNCpy(dp, lastRestartPointFname, endp - dp);
! dp += strlen(dp);
! break;
! case '%':
! /* convert %% to a single % */
! sp++;
! if (dp < endp)
! *dp++ = *sp;
! break;
! default:
! /* otherwise treat the % as not special */
! if (dp < endp)
! *dp++ = *sp;
! break;
! }
! }
! else
! {
! if (dp < endp)
! *dp++ = *sp;
}
}
! *dp = '\0';
! ereport(DEBUG3,
! (errmsg_internal("executing %s \"%s\"", commandName, command)));
!
! /*
! * execute the constructed command
! */
! rc = system(xlogRecoveryCmd);
! }
if (rc != 0)
{
/*
***************
*** 8916,8921 **** pg_xlogfile_name(PG_FUNCTION_ARGS)
--- 8935,9008 ----
}
/*
+ * Cleanup XLOGDIR given a specific WAL file name, which we keep. Typically
+ * used as an archive_cleanup_command.
+ */
+ Datum
+ pg_archive_cleanup(PG_FUNCTION_ARGS)
+ {
+ text *location = PG_GETARG_TEXT_P(0);
+ char *exclusiveCleanupFileName = text_to_cstring(location);
+ char WALFilePath[MAXPGPATH]; /* the file path including archive */
+ int rc = 0; /* nothing to do ain't an error */
+ DIR *xldir;
+ struct dirent *xlde;
+
+ if ((xldir = opendir(XLOGDIR)) != NULL)
+ {
+ while ((xlde = readdir(xldir)) != NULL)
+ {
+ /*
+ * We ignore the timeline part of the XLOG segment identifiers
+ * in deciding whether a segment is still needed. This
+ * ensures that we won't prematurely remove a segment from a
+ * parent timeline. We could probably be a little more
+ * proactive about removing segments of non-parent timelines,
+ * but that would be a whole lot more complicated.
+ *
+ * We use the alphanumeric sorting property of the filenames
+ * to decide which ones are earlier than the
+ * exclusiveCleanupFileName file. Note that this means files
+ * are not removed in the order they were originally written,
+ * in case this worries you.
+ */
+ #define XLOG_DATA_FNAME_LEN 24
+ if (strlen(xlde->d_name) == XLOG_DATA_FNAME_LEN &&
+ strspn(xlde->d_name, "0123456789ABCDEF") == XLOG_DATA_FNAME_LEN &&
+ strcmp(xlde->d_name + 8, exclusiveCleanupFileName + 8) < 0)
+ {
+ #ifdef WIN32
+ snprintf(WALFilePath, MAXPGPATH, "%s\\%s", XLOGDIR, xlde->d_name);
+ #else
+ snprintf(WALFilePath, MAXPGPATH, "%s/%s", XLOGDIR, xlde->d_name);
+ #endif
+
+ elog(DEBUG1, "removing \"%s\"", WALFilePath);
+
+ rc = unlink(WALFilePath);
+ if (rc != 0)
+ {
+ elog(ERROR,
+ "failed to remove \"%s\": %s",
+ WALFilePath, strerror(errno));
+ break;
+ }
+ }
+ }
+ }
+ else
+ /*
+ * How much do we want to manage that? ereport?
+ */
+ elog(ERROR, "Can't open \"%s\"", XLOGDIR);
+
+ closedir(xldir);
+ fflush(stderr);
+
+ PG_RETURN_BOOL(rc == 0);
+ }
+
+ /*
* read_backup_label: check to see if a backup_label file is present
*
* If we see a backup_label during recovery, we assume that we are recovering
*** a/src/include/access/xlog_internal.h
--- b/src/include/access/xlog_internal.h
***************
*** 273,278 **** extern Datum pg_last_xlog_receive_location(PG_FUNCTION_ARGS);
--- 273,279 ----
extern Datum pg_last_xlog_replay_location(PG_FUNCTION_ARGS);
extern Datum pg_xlogfile_name_offset(PG_FUNCTION_ARGS);
extern Datum pg_xlogfile_name(PG_FUNCTION_ARGS);
+ extern Datum pg_archive_cleanup(PG_FUNCTION_ARGS);
extern Datum pg_is_in_recovery(PG_FUNCTION_ARGS);
#endif /* XLOG_INTERNAL_H */
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 3314,3319 **** DATA(insert OID = 2850 ( pg_xlogfile_name_offset PGNSP PGUID 12 1 0 0 f f f t f
--- 3314,3321 ----
DESCR("xlog filename and byte offset, given an xlog location");
DATA(insert OID = 2851 ( pg_xlogfile_name PGNSP PGUID 12 1 0 0 f f f t f i 1 0 25 "25" _null_ _null_ _null_ _null_ pg_xlogfile_name _null_ _null_ _null_ ));
DESCR("xlog filename, given an xlog location");
+ DATA(insert OID = 3822 ( pg_archive_cleanup PGNSP PGUID 12 1 0 0 f f f t f i 1 0 16 "25" _null_ _null_ _null_ _null_ pg_archive_cleanup _null_ _null_ _null_ ));
+ DESCR("cleanup archive up to given xlog location");
DATA(insert OID = 3810 ( pg_is_in_recovery PGNSP PGUID 12 1 0 0 f f f t f v 0 0 16 "" _null_ _null_ _null_ _null_ pg_is_in_recovery _null_ _null_ _null_ ));
DESCR("true if server is in recovery");
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers