Skip to content
Snippets Groups Projects
Commit a30b9bf8 authored by Michael Paquier's avatar Michael Paquier
Browse files

wal_utils: Add support for segment list on same timeline

This can be satisfied only if caller has not provided a history file.
Regression tests are added as well.
parent 3e2e6ba4
No related branches found
No related tags found
No related merge requests found
-- Install extension for tests
CREATE EXTENSION wal_utils;
ERROR: extension "wal_utils" already exists
-- NULL checks
SELECT build_wal_segment_list(NULL, '0/0'::pg_lsn, 1, '0/0'::pg_lsn, NULL);
ERROR: origin or target data cannot be NULL
SELECT build_wal_segment_list(1, NULL, 1, '0/0'::pg_lsn, NULL);
ERROR: origin or target data cannot be NULL
SELECT build_wal_segment_list(1, '0/0'::pg_lsn, NULL, '0/0'::pg_lsn, NULL);
ERROR: origin or target data cannot be NULL
SELECT build_wal_segment_list(1, '0/0'::pg_lsn, 1, NULL, NULL);
ERROR: origin or target data cannot be NULL
CREATE TABLE history_data (data text);
-- Load history file to use as a base with multiple timelines
INSERT INTO history_data VALUES (
Loading
Loading
@@ -83,6 +92,36 @@ SELECT build_wal_segment_list(3, '0/177BEB38'::pg_lsn, 8, '0/259BEB38'::pg_lsn,
000000080000000000000025
(15 rows)
 
-- List of segments with same timeline for origin and target
SELECT build_wal_segment_list(1, '0/1D4F390', 1, '0/189BEB38'::pg_lsn, NULL);
build_wal_segment_list
--------------------------
000000010000000000000001
000000010000000000000002
000000010000000000000003
000000010000000000000004
000000010000000000000005
000000010000000000000006
000000010000000000000007
000000010000000000000008
000000010000000000000009
00000001000000000000000A
00000001000000000000000B
00000001000000000000000C
00000001000000000000000D
00000001000000000000000E
00000001000000000000000F
000000010000000000000010
000000010000000000000011
000000010000000000000012
000000010000000000000013
000000010000000000000014
000000010000000000000015
000000010000000000000016
000000010000000000000017
000000010000000000000018
(24 rows)
-- error, target TLI older than origin TLI
SELECT build_wal_segment_list(2, '0/09D4F389'::pg_lsn, 1, '0/259BEB38'::pg_lsn, data)
FROM history_data;
Loading
Loading
@@ -99,4 +138,7 @@ ERROR: timeline of last history entry 7 newer than or equal to target timeline
SELECT build_wal_segment_list(6, '0/09D4F389'::pg_lsn, 9, '0/259BEB38'::pg_lsn, data)
FROM history_data;
ERROR: origin data not a direct parent of target
-- error, target and origin timelines have to match without history file
SELECT build_wal_segment_list(1, '0/1D4F390', 2, '0/189BEB38'::pg_lsn, NULL);
ERROR: origin and target timelines not matching without history file
DROP TABLE history_data;
-- Install extension for tests
CREATE EXTENSION wal_utils;
 
-- NULL checks
SELECT build_wal_segment_list(NULL, '0/0'::pg_lsn, 1, '0/0'::pg_lsn, NULL);
SELECT build_wal_segment_list(1, NULL, 1, '0/0'::pg_lsn, NULL);
SELECT build_wal_segment_list(1, '0/0'::pg_lsn, NULL, '0/0'::pg_lsn, NULL);
SELECT build_wal_segment_list(1, '0/0'::pg_lsn, 1, NULL, NULL);
CREATE TABLE history_data (data text);
 
-- Load history file to use as a base with multiple timelines
Loading
Loading
@@ -20,6 +26,8 @@ SELECT build_wal_segment_list(1, '0/06D4F389'::pg_lsn, 8, '0/259BEB38'::pg_lsn,
-- Partial list of segments
SELECT build_wal_segment_list(3, '0/177BEB38'::pg_lsn, 8, '0/259BEB38'::pg_lsn, data)
FROM history_data;
-- List of segments with same timeline for origin and target
SELECT build_wal_segment_list(1, '0/1D4F390', 1, '0/189BEB38'::pg_lsn, NULL);
-- error, target TLI older than origin TLI
SELECT build_wal_segment_list(2, '0/09D4F389'::pg_lsn, 1, '0/259BEB38'::pg_lsn, data)
FROM history_data;
Loading
Loading
@@ -32,5 +40,7 @@ SELECT build_wal_segment_list(1, '0/09D4F389'::pg_lsn, 2, '0/10D4F389'::pg_lsn,
-- error, timelines are not direct parents
SELECT build_wal_segment_list(6, '0/09D4F389'::pg_lsn, 9, '0/259BEB38'::pg_lsn, data)
FROM history_data;
-- error, target and origin timelines have to match without history file
SELECT build_wal_segment_list(1, '0/1D4F390', 2, '0/189BEB38'::pg_lsn, NULL);
 
DROP TABLE history_data;
Loading
Loading
@@ -27,4 +27,4 @@ CREATE FUNCTION build_wal_segment_list(
OUT wal_segs text)
RETURNS SETOF text
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;
LANGUAGE C;
Loading
Loading
@@ -223,17 +223,17 @@ parse_wal_history(PG_FUNCTION_ARGS)
Datum
build_wal_segment_list(PG_FUNCTION_ARGS)
{
TimeLineID origin_tli = PG_GETARG_INT32(0);
XLogRecPtr origin_lsn = PG_GETARG_LSN(1);
TimeLineID target_tli = PG_GETARG_INT32(2);
XLogRecPtr target_lsn = PG_GETARG_LSN(3);
char *history_buf = TextDatumGetCString(PG_GETARG_DATUM(4));
TimeLineID origin_tli;
XLogRecPtr origin_lsn;
TimeLineID target_tli;
XLogRecPtr target_lsn;
char *history_buf;
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
MemoryContext per_query_ctx;
MemoryContext oldcontext;
TupleDesc tupdesc;
Tuplestorestate *tupstore;
List *entries;
List *entries = NIL;
ListCell *entry;
TimeLineHistoryEntry *history;
bool history_match = false;
Loading
Loading
@@ -244,6 +244,20 @@ build_wal_segment_list(PG_FUNCTION_ARGS)
bool nulls[1];
XLogSegNo logSegNo;
 
/* Sanity checks for arguments */
if (PG_ARGISNULL(0) || PG_ARGISNULL(1) ||
PG_ARGISNULL(2) || PG_ARGISNULL(3))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("origin or target data cannot be NULL")));
origin_tli = PG_GETARG_INT32(0);
origin_lsn = PG_GETARG_LSN(1);
target_tli = PG_GETARG_INT32(2);
target_lsn = PG_GETARG_LSN(3);
history_buf = PG_ARGISNULL(4) ? NULL :
TextDatumGetCString(PG_GETARG_DATUM(4));
/* check to see if caller supports us returning a tuplestore */
if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
ereport(ERROR,
Loading
Loading
@@ -283,57 +297,83 @@ build_wal_segment_list(PG_FUNCTION_ARGS)
errmsg("origin timeline %u newer than target timeline %u",
origin_tli, target_tli)));
 
/* parse the history file */
entries = parseTimeLineHistory(history_buf);
/*
* Check that the target data is newer than the last entry in the history
* file. Better safe than sorry.
*/
history = (TimeLineHistoryEntry *) llast(entries);
if (history->tli >= target_tli)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("timeline of last history entry %u newer than or "
"equal to target timeline %u",
history->tli, target_tli)));
if (history->end > target_lsn)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("LSN %X/%X of last history entry newer than target LSN %X/%X",
(uint32) (history->end >> 32),
(uint32) history->end,
(uint32) (target_lsn >> 32),
(uint32) target_lsn)));
/*
* Check that origin and target are direct parents, we already know that
* the target fits with the history file.
* Check parentage of the target and origin timelines if a history file
* has been given by caller.
*/
foreach(entry, entries)
if (history_buf)
{
history = (TimeLineHistoryEntry *) lfirst(entry);
/* parse the history file */
entries = parseTimeLineHistory(history_buf);
 
if (history->begin <= origin_lsn &&
history->end >= origin_lsn &&
history->tli == origin_tli)
if (entries == NIL)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("timeline history found empty after parsing")));
/*
* Check that the target data is newer than the last entry in the history
* file. Better safe than sorry.
*/
history = (TimeLineHistoryEntry *) llast(entries);
if (history->tli >= target_tli)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("timeline of last history entry %u newer than or "
"equal to target timeline %u",
history->tli, target_tli)));
if (history->end > target_lsn)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("LSN %X/%X of last history entry newer than target LSN %X/%X",
(uint32) (history->end >> 32),
(uint32) history->end,
(uint32) (target_lsn >> 32),
(uint32) target_lsn)));
/*
* Check that origin and target are direct parents, we already know that
* the target fits with the history file.
*/
foreach(entry, entries)
{
history_match = true;
break;
history = (TimeLineHistoryEntry *) lfirst(entry);
if (history->begin <= origin_lsn &&
history->end >= origin_lsn &&
history->tli == origin_tli)
{
history_match = true;
break;
}
}
if (!history_match)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("origin data not a direct parent of target")));
/*
* Abuse this variable as temporary storage, we want the beginning
* of the last, target timeline to match the end of the last timeline
* tracked in the history file.
*/
current_seg_lsn = history->end;
}
else
{
/* Here the origin and target timeline match */
if (origin_tli != target_tli)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("origin and target timelines not matching without history file")));
 
if (!history_match)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("origin data not a direct parent of target")));
current_seg_lsn = origin_lsn;
}
 
/*
* Before listing the list of files, add a last history entry using the
* target data, this simplifies the logic below to build the segment list.
*/
current_seg_lsn = history->end; /* abuse this variable as temporary
* storage */
history = (TimeLineHistoryEntry *) palloc(sizeof(TimeLineHistoryEntry));
history->tli = target_tli;
history->begin = current_seg_lsn;
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment