1 /***********************************************************************************************************************************
2 Test Database
3 ***********************************************************************************************************************************/
4 #include "common/io/fdRead.h"
5 #include "common/io/fdWrite.h"
6 #include "common/type/json.h"
7
8 #include "common/harnessConfig.h"
9 #include "common/harnessFork.h"
10 #include "common/harnessLog.h"
11 #include "common/harnessPq.h"
12
13 /***********************************************************************************************************************************
14 Macro to check that replay is making progress -- this does not seem useful enough to be included in the pq harness header
15 ***********************************************************************************************************************************/
16 #define HRNPQ_MACRO_REPLAY_TARGET_REACHED_PROGRESS( \
17 sessionParam, walNameParam, lsnNameParam, targetLsnParam, targetReachedParam, replayLsnParam, replayLastLsnParam, \
18 replayProgressParam, sleepParam) \
19 {.session = sessionParam, \
20 .function = HRNPQ_SENDQUERY, \
21 .param = \
22 "[\"select replayLsn::text,\\n" \
23 " (replayLsn > '" targetLsnParam "')::bool as targetReached,\\n" \
24 " (replayLsn > '" replayLastLsnParam "')::bool as replayProgress\\n" \
25 " from pg_catalog.pg_last_" walNameParam "_replay_" lsnNameParam "() as replayLsn\"]", \
26 .resultInt = 1, .sleep = sleepParam}, \
27 {.session = sessionParam, .function = HRNPQ_CONSUMEINPUT}, \
28 {.session = sessionParam, .function = HRNPQ_ISBUSY}, \
29 {.session = sessionParam, .function = HRNPQ_GETRESULT}, \
30 {.session = sessionParam, .function = HRNPQ_RESULTSTATUS, .resultInt = PGRES_TUPLES_OK}, \
31 {.session = sessionParam, .function = HRNPQ_NTUPLES, .resultInt = 1}, \
32 {.session = sessionParam, .function = HRNPQ_NFIELDS, .resultInt = 3}, \
33 {.session = sessionParam, .function = HRNPQ_FTYPE, .param = "[0]", .resultInt = HRNPQ_TYPE_TEXT}, \
34 {.session = sessionParam, .function = HRNPQ_FTYPE, .param = "[1]", .resultInt = HRNPQ_TYPE_BOOL}, \
35 {.session = sessionParam, .function = HRNPQ_FTYPE, .param = "[2]", .resultInt = HRNPQ_TYPE_BOOL}, \
36 {.session = sessionParam, .function = HRNPQ_GETVALUE, .param = "[0,0]", .resultZ = replayLsnParam}, \
37 {.session = sessionParam, .function = HRNPQ_GETVALUE, .param = "[0,1]", .resultZ = cvtBoolToConstZ(targetReachedParam)}, \
38 {.session = sessionParam, .function = HRNPQ_GETVALUE, .param = "[0,2]", .resultZ = cvtBoolToConstZ(replayProgressParam)}, \
39 {.session = sessionParam, .function = HRNPQ_CLEAR}, \
40 {.session = sessionParam, .function = HRNPQ_GETRESULT, .resultNull = true}
41
42 #define HRNPQ_MACRO_REPLAY_TARGET_REACHED_PROGRESS_GE_10( \
43 sessionParam, targetLsnParam, targetReachedParam, replayLsnParam, replayLastLsnParam, replayProgressParam, sleepParam) \
44 HRNPQ_MACRO_REPLAY_TARGET_REACHED_PROGRESS( \
45 sessionParam, "wal", "lsn", targetLsnParam, targetReachedParam, replayLsnParam, replayLastLsnParam, replayProgressParam, \
46 sleepParam)
47
48 /***********************************************************************************************************************************
49 Test Run
50 ***********************************************************************************************************************************/
51 void
testRun(void)52 testRun(void)
53 {
54 FUNCTION_HARNESS_VOID();
55
56 // PQfinish() is strictly checked
57 harnessPqScriptStrictSet(true);
58
59 // *****************************************************************************************************************************
60 if (testBegin("Db and dbProtocol()"))
61 {
62 HRN_FORK_BEGIN()
63 {
64 HRN_FORK_CHILD_BEGIN()
65 {
66 // Set options
67 StringList *argList = strLstNew();
68 hrnCfgArgRawZ(argList, cfgOptStanza, "test1");
69 hrnCfgArgKeyRawZ(argList, cfgOptPgPath, 1, TEST_PATH "/pg");
70 hrnCfgArgKeyRawZ(argList, cfgOptPgDatabase, 1, "testdb");
71 hrnCfgArgRawStrId(argList, cfgOptRemoteType, protocolStorageTypePg);
72 hrnCfgArgRawZ(argList, cfgOptProcess, "0");
73 HRN_CFG_LOAD(cfgCmdBackup, argList, .role = cfgCmdRoleRemote);
74
75 // Set script
76 harnessPqScriptSet((HarnessPq [])
77 {
78 HRNPQ_MACRO_OPEN(1, "dbname='testdb' port=5432"),
79 HRNPQ_MACRO_SET_SEARCH_PATH(1),
80 HRNPQ_MACRO_SET_CLIENT_ENCODING(1),
81 HRNPQ_MACRO_VALIDATE_QUERY(1, PG_VERSION_84, TEST_PATH "/pg", NULL, NULL),
82 HRNPQ_MACRO_CLOSE(1),
83
84 HRNPQ_MACRO_OPEN(1, "dbname='testdb' port=5432"),
85 HRNPQ_MACRO_SET_SEARCH_PATH(1),
86 HRNPQ_MACRO_SET_CLIENT_ENCODING(1),
87 HRNPQ_MACRO_VALIDATE_QUERY(1, PG_VERSION_84, TEST_PATH "/pg", NULL, NULL),
88 HRNPQ_MACRO_WAL_SWITCH(1, "xlog", "000000030000000200000003"),
89 HRNPQ_MACRO_CLOSE(1),
90
91 HRNPQ_MACRO_DONE()
92 });
93
94 // Create server
95 ProtocolServer *server = NULL;
96
97 TEST_ASSIGN(
98 server,
99 protocolServerNew(STRDEF("db test server"), STRDEF("test"), HRN_FORK_CHILD_READ(), HRN_FORK_CHILD_WRITE()),
100 "create server");
101
102 static const ProtocolServerHandler commandHandler[] = {PROTOCOL_SERVER_HANDLER_DB_LIST};
103
104 TEST_RESULT_VOID(
105 protocolServerProcess(server, NULL, commandHandler, PROTOCOL_SERVER_HANDLER_LIST_SIZE(commandHandler)),
106 "run process loop");
107 TEST_RESULT_VOID(protocolServerFree(server), "free server");
108 }
109 HRN_FORK_CHILD_END();
110
111 HRN_FORK_PARENT_BEGIN()
112 {
113 // Create client
114 ProtocolClient *client = NULL;
115 Db *db = NULL;
116
117 TEST_ASSIGN(
118 client,
119 protocolClientNew(STRDEF("db test client"), STRDEF("test"), HRN_FORK_PARENT_READ(0), HRN_FORK_PARENT_WRITE(0)),
120 "create client");
121
122 TRY_BEGIN()
123 {
124 // -------------------------------------------------------------------------------------------------------------
125 TEST_TITLE("open and free database");
126
127 TEST_ASSIGN(db, dbNew(NULL, client, STRDEF("test")), "create db");
128
129 TRY_BEGIN()
130 {
131 TEST_RESULT_VOID(dbOpen(db), "open db");
132 TEST_RESULT_UINT(db->remoteIdx, 0, "check remote idx");
133 TEST_RESULT_VOID(dbFree(db), "free db");
134 db = NULL;
135 }
136 CATCH_ANY()
137 {
138 // Free on error
139 dbFree(db);
140 }
141 TRY_END();
142
143 // -------------------------------------------------------------------------------------------------------------
144 TEST_TITLE("remote commands");
145
146 TEST_ASSIGN(db, dbNew(NULL, client, STRDEF("test")), "create db");
147
148 TRY_BEGIN()
149 {
150 TEST_RESULT_VOID(dbOpen(db), "open db");
151 TEST_RESULT_UINT(db->remoteIdx, 1, "check idx");
152 TEST_RESULT_STR_Z(dbWalSwitch(db), "000000030000000200000003", "wal switch");
153 TEST_RESULT_VOID(memContextCallbackClear(db->pub.memContext), "clear context so close is not called");
154 }
155 FINALLY()
156 {
157 // Clear the context callback so the server frees the db on exit
158 memContextCallbackClear(db->pub.memContext);
159 }
160 TRY_END();
161 }
162 FINALLY()
163 {
164 // Free on error
165 protocolClientFree(client);
166 }
167 TRY_END();
168 }
169 HRN_FORK_PARENT_END();
170 }
171 HRN_FORK_END();
172 }
173
174 // *****************************************************************************************************************************
175 if (testBegin("dbBackupStart(), dbBackupStop(), dbTime(), dbList(), dbTablespaceList(), and dbReplayWait()"))
176 {
177 StringList *argList = strLstNew();
178 hrnCfgArgRawZ(argList, cfgOptStanza, "test1");
179 hrnCfgArgKeyRawZ(argList, cfgOptRepoRetentionFull, 1, "1");
180 hrnCfgArgKeyRawZ(argList, cfgOptPgPath, 1, TEST_PATH "/pg1");
181 hrnCfgArgKeyRawZ(argList, cfgOptPgDatabase, 1, "backupdb");
182 HRN_CFG_LOAD(cfgCmdBackup, argList);
183
184 // -------------------------------------------------------------------------------------------------------------------------
185 TEST_TITLE("error when unable to select any pg_settings");
186
187 harnessPqScriptSet((HarnessPq [])
188 {
189 // Connect to primary
190 HRNPQ_MACRO_OPEN(1, "dbname='backupdb' port=5432"),
191 HRNPQ_MACRO_SET_SEARCH_PATH(1),
192 HRNPQ_MACRO_SET_CLIENT_ENCODING(1),
193
194 // Return NULL for a row in pg_settings
195 {.session = 1, .function = HRNPQ_SENDQUERY, .param =
196 "[\"select (select setting from pg_catalog.pg_settings where name = 'server_version_num')::int4,"
197 " (select setting from pg_catalog.pg_settings where name = 'data_directory')::text,"
198 " (select setting from pg_catalog.pg_settings where name = 'archive_mode')::text,"
199 " (select setting from pg_catalog.pg_settings where name = 'archive_command')::text\"]",
200 .resultInt = 1},
201 {.session = 1, .function = HRNPQ_CONSUMEINPUT},
202 {.session = 1, .function = HRNPQ_ISBUSY},
203 {.session = 1, .function = HRNPQ_GETRESULT},
204 {.session = 1, .function = HRNPQ_RESULTSTATUS, .resultInt = PGRES_TUPLES_OK},
205 {.session = 1, .function = HRNPQ_NTUPLES, .resultInt = 1},
206 {.session = 1, .function = HRNPQ_NFIELDS, .resultInt = 4},
207 {.session = 1, .function = HRNPQ_FTYPE, .param = "[0]", .resultInt = HRNPQ_TYPE_INT},
208 {.session = 1, .function = HRNPQ_FTYPE, .param = "[1]", .resultInt = HRNPQ_TYPE_TEXT},
209 {.session = 1, .function = HRNPQ_FTYPE, .param = "[2]", .resultInt = HRNPQ_TYPE_TEXT},
210 {.session = 1, .function = HRNPQ_FTYPE, .param = "[3]", .resultInt = HRNPQ_TYPE_TEXT},
211 {.session = 1, .function = HRNPQ_GETVALUE, .param = "[0,0]", .resultZ = "0"},
212 {.session = 1, .function = HRNPQ_GETVALUE, .param = "[0,1]", .resultZ = "value"},
213 {.session = 1, .function = HRNPQ_GETVALUE, .param = "[0,2]", .resultZ = "value"},
214 {.session = 1, .function = HRNPQ_GETVALUE, .param = "[0,3]", .resultZ = ""},
215 {.session = 1, .function = HRNPQ_GETISNULL, .param = "[0,3]", .resultInt = 1},
216 {.session = 1, .function = HRNPQ_CLEAR},
217 {.session = 1, .function = HRNPQ_GETRESULT, .resultNull = true},
218
219 // Close primary
220 HRNPQ_MACRO_CLOSE(1),
221
222 HRNPQ_MACRO_DONE()
223 });
224
225 TEST_ERROR(dbGet(true, true, false), DbConnectError, "unable to find primary cluster - cannot proceed");
226
227 TEST_RESULT_LOG(
228 "P00 WARN: unable to check pg-1: [DbQueryError] unable to select some rows from pg_settings\n"
229 " HINT: is the backup running as the postgres user?\n"
230 " HINT: is the pg_read_all_settings role assigned for PostgreSQL >= 10?");
231
232 // -------------------------------------------------------------------------------------------------------------------------
233 TEST_TITLE("PostgreSQL 8.3 start backup with no start fast");
234
235 harnessPqScriptSet((HarnessPq [])
236 {
237 // Connect to primary
238 HRNPQ_MACRO_OPEN_LE_91(1, "dbname='backupdb' port=5432", PG_VERSION_83, TEST_PATH "/pg1", NULL, NULL),
239
240 // Get advisory lock
241 HRNPQ_MACRO_ADVISORY_LOCK(1, true),
242
243 // Start backup with no start fast
244 HRNPQ_MACRO_START_BACKUP_83(1, "1/1", "000000010000000100000001"),
245
246 // Close primary
247 HRNPQ_MACRO_CLOSE(1),
248
249 HRNPQ_MACRO_DONE()
250 });
251
252 DbGetResult db = {0};
253 TEST_ASSIGN(db, dbGet(true, true, false), "get primary");
254
255 TEST_RESULT_STR_Z(dbBackupStart(db.primary, false, false).lsn, "1/1", "start backup");
256
257 TEST_RESULT_VOID(dbFree(db.primary), "free primary");
258
259 // -------------------------------------------------------------------------------------------------------------------------
260 TEST_TITLE("PostgreSQL 9.5 start/stop backup");
261
262 harnessPqScriptSet((HarnessPq [])
263 {
264 // Connect to primary
265 HRNPQ_MACRO_OPEN_GE_92(1, "dbname='backupdb' port=5432", PG_VERSION_95, TEST_PATH "/pg1", false, NULL, NULL),
266
267 // Get start time
268 HRNPQ_MACRO_TIME_QUERY(1, 1000),
269
270 // Start backup errors on advisory lock
271 HRNPQ_MACRO_ADVISORY_LOCK(1, false),
272
273 // Start backup
274 HRNPQ_MACRO_ADVISORY_LOCK(1, true),
275 HRNPQ_MACRO_IS_IN_BACKUP(1, false),
276 HRNPQ_MACRO_START_BACKUP_84_95(1, false, "2/3", "000000010000000200000003"),
277 HRNPQ_MACRO_DATABASE_LIST_1(1, "test1"),
278 HRNPQ_MACRO_TABLESPACE_LIST_0(1),
279
280 // Stop backup
281 HRNPQ_MACRO_STOP_BACKUP_LE_95(1, "2/4", "000000010000000200000004"),
282
283 // Close primary
284 HRNPQ_MACRO_CLOSE(1),
285
286 HRNPQ_MACRO_DONE()
287 });
288
289 TEST_ASSIGN(db, dbGet(true, true, false), "get primary");
290
291 TEST_RESULT_UINT(dbTimeMSec(db.primary), 1000, "check time");
292
293 TEST_ERROR(
294 dbBackupStart(db.primary, false, false), LockAcquireError,
295 "unable to acquire pgBackRest advisory lock\n"
296 "HINT: is another pgBackRest backup already running on this cluster?");
297
298 DbBackupStartResult backupStartResult = {.lsn = NULL};
299 TEST_ASSIGN(backupStartResult, dbBackupStart(db.primary, false, true), "start backup");
300 TEST_RESULT_STR_Z(backupStartResult.lsn, "2/3", "check lsn");
301 TEST_RESULT_STR_Z(backupStartResult.walSegmentName, "000000010000000200000003", "check wal segment name");
302
303 TEST_RESULT_STR_Z(jsonFromVar(varNewVarLst(dbList(db.primary))), "[[16384,\"test1\",13777]]", "check db list");
304 TEST_RESULT_STR_Z(jsonFromVar(varNewVarLst(dbTablespaceList(db.primary))), "[]", "check tablespace list");
305
306 DbBackupStopResult backupStopResult = {.lsn = NULL};
307 TEST_ASSIGN(backupStopResult, dbBackupStop(db.primary), "stop backup");
308 TEST_RESULT_STR_Z(backupStopResult.lsn, "2/4", "check lsn");
309 TEST_RESULT_STR_Z(backupStopResult.walSegmentName, "000000010000000200000004", "check wal segment name");
310 TEST_RESULT_STR_Z(backupStopResult.backupLabel, NULL, "check backup label is not set");
311 TEST_RESULT_STR_Z(backupStopResult.tablespaceMap, NULL, "check tablespace map is not set");
312
313 TEST_RESULT_VOID(dbFree(db.primary), "free primary");
314
315 // -------------------------------------------------------------------------------------------------------------------------
316 TEST_TITLE("PostgreSQL 9.5 start/stop backup where backup is in progress");
317
318 harnessPqScriptSet((HarnessPq [])
319 {
320 // Connect to primary
321 HRNPQ_MACRO_OPEN_GE_92(1, "dbname='backupdb' port=5432", PG_VERSION_95, TEST_PATH "/pg1", false, NULL, NULL),
322
323 // Start backup when backup is in progress
324 HRNPQ_MACRO_ADVISORY_LOCK(1, true),
325 HRNPQ_MACRO_IS_IN_BACKUP(1, true),
326
327 // Stop old backup
328 HRNPQ_MACRO_STOP_BACKUP_LE_95(1, "1/1", "000000010000000100000001"),
329
330 // Start backup
331 HRNPQ_MACRO_START_BACKUP_84_95(1, true, "2/5", "000000010000000200000005"),
332
333 // Stop backup
334 HRNPQ_MACRO_STOP_BACKUP_LE_95(1, "2/6", "000000010000000200000006"),
335
336 // Close primary
337 HRNPQ_MACRO_CLOSE(1),
338
339 HRNPQ_MACRO_DONE()
340 });
341
342 TEST_ASSIGN(db, dbGet(true, true, false), "get primary");
343
344 TEST_RESULT_STR_Z(dbBackupStart(db.primary, true, true).lsn, "2/5", "start backup");
345
346 TEST_RESULT_LOG(
347 "P00 WARN: the cluster is already in backup mode but no pgBackRest backup process is running."
348 " pg_stop_backup() will be called so a new backup can be started.");
349
350 TEST_RESULT_STR_Z(dbBackupStop(db.primary).lsn, "2/6", "stop backup");
351
352 TEST_RESULT_VOID(dbFree(db.primary), "free primary");
353
354 // -------------------------------------------------------------------------------------------------------------------------
355 TEST_TITLE("PostgreSQL 9.6 start/stop backup");
356
357 harnessPqScriptSet((HarnessPq [])
358 {
359 // Connect to primary
360 HRNPQ_MACRO_OPEN_GE_96(1, "dbname='backupdb' port=5432", PG_VERSION_96, TEST_PATH "/pg1", false, NULL, NULL),
361
362 // Start backup
363 HRNPQ_MACRO_ADVISORY_LOCK(1, true),
364 HRNPQ_MACRO_START_BACKUP_96(1, false, "3/3", "000000010000000300000003"),
365
366 // Stop backup
367 HRNPQ_MACRO_STOP_BACKUP_96(1, "3/4", "000000010000000300000004", false),
368
369 // Close primary
370 HRNPQ_MACRO_CLOSE(1),
371
372 HRNPQ_MACRO_DONE()
373 });
374
375 TEST_ASSIGN(db, dbGet(true, true, false), "get primary");
376
377 TEST_ASSIGN(backupStartResult, dbBackupStart(db.primary, false, true), "start backup");
378 TEST_RESULT_STR_Z(backupStartResult.lsn, "3/3", "check lsn");
379 TEST_RESULT_STR_Z(backupStartResult.walSegmentName, "000000010000000300000003", "check wal segment name");
380
381 TEST_ASSIGN(backupStopResult, dbBackupStop(db.primary), "stop backup");
382 TEST_RESULT_STR_Z(backupStopResult.lsn, "3/4", "check lsn");
383 TEST_RESULT_STR_Z(backupStopResult.walSegmentName, "000000010000000300000004", "check wal segment name");
384 TEST_RESULT_STR_Z(backupStopResult.backupLabel, "BACKUP_LABEL_DATA", "check backup label");
385 TEST_RESULT_STR_Z(backupStopResult.tablespaceMap, NULL, "check tablespace map is not set");
386
387 TEST_RESULT_VOID(dbFree(db.primary), "free primary");
388
389 // -------------------------------------------------------------------------------------------------------------------------
390 TEST_TITLE("PostgreSQL 9.5 start backup from standby");
391
392 argList = strLstNew();
393 hrnCfgArgRawZ(argList, cfgOptStanza, "test1");
394 hrnCfgArgKeyRawZ(argList, cfgOptRepoRetentionFull, 1, "1");
395 hrnCfgArgKeyRawZ(argList, cfgOptPgPath, 1, TEST_PATH "/pg1");
396 hrnCfgArgKeyRawZ(argList, cfgOptPgPath, 2, TEST_PATH "/pg2");
397 hrnCfgArgKeyRawZ(argList, cfgOptPgPort, 2, "5433");
398 HRN_CFG_LOAD(cfgCmdBackup, argList);
399
400 harnessPqScriptSet((HarnessPq [])
401 {
402 // Connect to primary
403 HRNPQ_MACRO_OPEN_GE_92(1, "dbname='postgres' port=5432", PG_VERSION_95, TEST_PATH "/pg1", false, NULL, NULL),
404
405 // Connect to standby
406 HRNPQ_MACRO_OPEN_GE_92(2, "dbname='postgres' port=5433", PG_VERSION_95, TEST_PATH "/pg2", true, NULL, NULL),
407
408 // Start backup
409 HRNPQ_MACRO_ADVISORY_LOCK(1, true),
410 HRNPQ_MACRO_START_BACKUP_84_95(1, false, "5/4", "000000050000000500000004"),
411
412 // Wait for standby to sync
413 HRNPQ_MACRO_REPLAY_WAIT_LE_95(2, "5/4"),
414
415 // Close standby
416 HRNPQ_MACRO_CLOSE(2),
417
418 // Close primary
419 HRNPQ_MACRO_CLOSE(1),
420
421 HRNPQ_MACRO_DONE()
422 });
423
424 TEST_ASSIGN(db, dbGet(false, true, true), "get primary and standby");
425
426 TEST_RESULT_STR_Z(dbBackupStart(db.primary, false, false).lsn, "5/4", "start backup");
427 TEST_RESULT_VOID(dbReplayWait(db.standby, STRDEF("5/4"), 1000), "sync standby");
428
429 TEST_RESULT_VOID(dbFree(db.standby), "free standby");
430 TEST_RESULT_VOID(dbFree(db.primary), "free primary");
431
432 // -------------------------------------------------------------------------------------------------------------------------
433 TEST_TITLE("PostgreSQL 10 start/stop backup from standby");
434
435 harnessPqScriptSet((HarnessPq [])
436 {
437 // Connect to primary
438 HRNPQ_MACRO_OPEN_GE_96(1, "dbname='postgres' port=5432", PG_VERSION_10, TEST_PATH "/pg1", false, NULL, NULL),
439
440 // Connect to standby
441 HRNPQ_MACRO_OPEN_GE_96(2, "dbname='postgres' port=5433", PG_VERSION_10, TEST_PATH "/pg2", true, NULL, NULL),
442
443 // Start backup
444 HRNPQ_MACRO_ADVISORY_LOCK(1, true),
445 HRNPQ_MACRO_START_BACKUP_GE_10(1, false, "5/5", "000000050000000500000005"),
446
447 // Standby returns NULL lsn
448 {.session = 2,
449 .function = HRNPQ_SENDQUERY,
450 .param =
451 "[\"select replayLsn::text,\\n"
452 " (replayLsn > '5/5')::bool as targetReached\\n"
453 " from pg_catalog.pg_last_wal_replay_lsn() as replayLsn\"]",
454 .resultInt = 1},
455 {.session = 2, .function = HRNPQ_CONSUMEINPUT},
456 {.session = 2, .function = HRNPQ_ISBUSY},
457 {.session = 2, .function = HRNPQ_GETRESULT},
458 {.session = 2, .function = HRNPQ_RESULTSTATUS, .resultInt = PGRES_TUPLES_OK},
459 {.session = 2, .function = HRNPQ_NTUPLES, .resultInt = 1},
460 {.session = 2, .function = HRNPQ_NFIELDS, .resultInt = 2},
461 {.session = 2, .function = HRNPQ_FTYPE, .param = "[0]", .resultInt = HRNPQ_TYPE_TEXT},
462 {.session = 2, .function = HRNPQ_FTYPE, .param = "[1]", .resultInt = HRNPQ_TYPE_BOOL},
463 {.session = 2, .function = HRNPQ_GETVALUE, .param = "[0,0]", .resultZ = ""},
464 {.session = 2, .function = HRNPQ_GETISNULL, .param = "[0,0]", .resultInt = 1},
465 {.session = 2, .function = HRNPQ_GETVALUE, .param = "[0,1]", .resultZ = "false"},
466 {.session = 2, .function = HRNPQ_CLEAR},
467 {.session = 2, .function = HRNPQ_GETRESULT, .resultNull = true},
468
469 // Timeout waiting for sync
470 HRNPQ_MACRO_REPLAY_TARGET_REACHED_GE_10(2, "5/5", false, "5/3"),
471 HRNPQ_MACRO_REPLAY_TARGET_REACHED_PROGRESS_GE_10(2, "5/5", false, "5/3", "5/3", false, 250),
472 HRNPQ_MACRO_REPLAY_TARGET_REACHED_PROGRESS_GE_10(2, "5/5", false, "5/3", "5/3", false, 0),
473
474 // Checkpoint target timeout waiting for sync
475 HRNPQ_MACRO_REPLAY_TARGET_REACHED_GE_10(2, "5/5", true, "5/5"),
476 HRNPQ_MACRO_CHECKPOINT(2),
477 HRNPQ_MACRO_CHECKPOINT_TARGET_REACHED_GE_10(2, "5/5", false, "5/4", 250),
478 HRNPQ_MACRO_CHECKPOINT_TARGET_REACHED_GE_10(2, "5/5", false, "5/4", 0),
479
480 // Wait for standby to sync
481 HRNPQ_MACRO_REPLAY_TARGET_REACHED_GE_10(2, "5/5", false, "5/3"),
482 HRNPQ_MACRO_REPLAY_TARGET_REACHED_PROGRESS_GE_10(2, "5/5", false, "5/3", "5/3", false, 0),
483 HRNPQ_MACRO_REPLAY_TARGET_REACHED_PROGRESS_GE_10(2, "5/5", false, "5/4", "5/3", true, 0),
484 HRNPQ_MACRO_REPLAY_TARGET_REACHED_PROGRESS_GE_10(2, "5/5", true, "5/5", "5/4", true, 0),
485 HRNPQ_MACRO_CHECKPOINT(2),
486 HRNPQ_MACRO_CHECKPOINT_TARGET_REACHED_GE_10(2, "5/5", true, "X/X", 0),
487
488 // Close standby
489 HRNPQ_MACRO_CLOSE(2),
490
491 // Stop backup
492 HRNPQ_MACRO_STOP_BACKUP_GE_10(1, "5/6", "000000050000000500000006", true),
493
494 // Close primary
495 HRNPQ_MACRO_CLOSE(1),
496
497 HRNPQ_MACRO_DONE()
498 });
499
500 TEST_ASSIGN(db, dbGet(false, true, true), "get primary and standby");
501
502 TEST_RESULT_STR_Z(dbBackupStart(db.primary, false, false).lsn, "5/5", "start backup");
503
504 TEST_ERROR(
505 dbReplayWait(db.standby, STRDEF("5/5"), 1000), ArchiveTimeoutError,
506 "unable to query replay lsn on the standby using 'pg_catalog.pg_last_wal_replay_lsn()'\n"
507 "HINT: Is this a standby?");
508
509 TEST_ERROR(
510 dbReplayWait(db.standby, STRDEF("5/5"), 200), ArchiveTimeoutError,
511 "timeout before standby replayed to 5/5 - only reached 5/3");
512
513 TEST_ERROR(
514 dbReplayWait(db.standby, STRDEF("5/5"), 200), ArchiveTimeoutError,
515 "timeout before standby checkpoint lsn reached 5/5 - only reached 5/4");
516
517 TEST_RESULT_VOID(dbReplayWait(db.standby, STRDEF("5/5"), 1000), "sync standby");
518
519 TEST_RESULT_VOID(dbFree(db.standby), "free standby");
520
521 TEST_RESULT_STR_Z(dbBackupStop(db.primary).tablespaceMap, "TABLESPACE_MAP_DATA", "stop backup");
522
523 TEST_RESULT_VOID(dbFree(db.primary), "free primary");
524 }
525
526 // *****************************************************************************************************************************
527 if (testBegin("dbGet()"))
528 {
529 DbGetResult result = {0};
530
531 StringList *argList = strLstNew();
532 hrnCfgArgRawZ(argList, cfgOptStanza, "test1");
533 hrnCfgArgKeyRawZ(argList, cfgOptRepoRetentionFull, 1, "1");
534 hrnCfgArgKeyRawZ(argList, cfgOptPgPath, 1, TEST_PATH "/pg1");
535 hrnCfgArgKeyRawZ(argList, cfgOptPgUser, 1, "bob");
536 HRN_CFG_LOAD(cfgCmdBackup, argList);
537
538 // -------------------------------------------------------------------------------------------------------------------------
539 TEST_TITLE("error connecting to primary");
540
541 harnessPqScriptSet((HarnessPq [])
542 {
543 {.function = HRNPQ_CONNECTDB, .param = "[\"dbname='postgres' port=5432 user='bob'\"]"},
544 {.function = HRNPQ_STATUS, .resultInt = CONNECTION_BAD},
545 {.function = HRNPQ_ERRORMESSAGE, .resultZ = "error"},
546 {.function = HRNPQ_FINISH},
547 {.function = NULL}
548 });
549
550 TEST_ERROR(dbGet(true, true, false), DbConnectError, "unable to find primary cluster - cannot proceed");
551 TEST_RESULT_LOG(
552 "P00 WARN: unable to check pg-1: [DbConnectError] unable to connect to 'dbname='postgres' port=5432 user='bob'':"
553 " error");
554
555 // -------------------------------------------------------------------------------------------------------------------------
556 TEST_TITLE("only available cluster is a standby");
557
558 harnessPqScriptSet((HarnessPq [])
559 {
560 HRNPQ_MACRO_OPEN(1, "dbname='postgres' port=5432 user='bob'"),
561 HRNPQ_MACRO_SET_SEARCH_PATH(1),
562 HRNPQ_MACRO_SET_CLIENT_ENCODING(1),
563 HRNPQ_MACRO_VALIDATE_QUERY(1, PG_VERSION_94, TEST_PATH "/pg", NULL, NULL),
564 HRNPQ_MACRO_SET_APPLICATION_NAME(1),
565 HRNPQ_MACRO_IS_STANDBY_QUERY(1, true),
566 HRNPQ_MACRO_CLOSE(1),
567 HRNPQ_MACRO_DONE()
568 });
569
570 TEST_ERROR(dbGet(true, true, false), DbConnectError, "unable to find primary cluster - cannot proceed");
571
572 // -------------------------------------------------------------------------------------------------------------------------
573 TEST_TITLE("standby cluster required but not found");
574
575 harnessPqScriptSet((HarnessPq [])
576 {
577 HRNPQ_MACRO_OPEN(1, "dbname='postgres' port=5432 user='bob'"),
578 HRNPQ_MACRO_SET_SEARCH_PATH(1),
579 HRNPQ_MACRO_SET_CLIENT_ENCODING(1),
580 HRNPQ_MACRO_VALIDATE_QUERY(1, PG_VERSION_94, TEST_PATH "/pg", NULL, NULL),
581 HRNPQ_MACRO_SET_APPLICATION_NAME(1),
582 HRNPQ_MACRO_IS_STANDBY_QUERY(1, false),
583 HRNPQ_MACRO_CLOSE(1),
584 HRNPQ_MACRO_DONE()
585 });
586
587 TEST_ERROR(dbGet(false, false, true), DbConnectError, "unable to find standby cluster - cannot proceed");
588
589 // -------------------------------------------------------------------------------------------------------------------------
590 TEST_TITLE("primary cluster found");
591
592 harnessPqScriptSet((HarnessPq [])
593 {
594 HRNPQ_MACRO_OPEN_LE_91(1, "dbname='postgres' port=5432 user='bob'", PG_VERSION_84, TEST_PATH "/pg1", NULL, NULL),
595 HRNPQ_MACRO_CLOSE(1),
596 HRNPQ_MACRO_DONE()
597 });
598
599 TEST_ASSIGN(result, dbGet(true, true, false), "get primary only");
600
601 TEST_RESULT_INT(result.primaryIdx, 0, "check primary id");
602 TEST_RESULT_BOOL(result.primary != NULL, true, "check primary");
603 TEST_RESULT_INT(result.standbyIdx, 0, "check standby id");
604 TEST_RESULT_BOOL(result.standby == NULL, true, "check standby");
605 TEST_RESULT_INT(dbPgVersion(result.primary), PG_VERSION_84, "version set");
606 TEST_RESULT_STR_Z(dbPgDataPath(result.primary), TEST_PATH "/pg1", "path set");
607
608 TEST_RESULT_VOID(dbFree(result.primary), "free primary");
609
610 // -------------------------------------------------------------------------------------------------------------------------
611 TEST_TITLE("more than one primary found");
612
613 argList = strLstNew();
614 hrnCfgArgRawZ(argList, cfgOptStanza, "test1");
615 hrnCfgArgKeyRawZ(argList, cfgOptRepoRetentionFull, 1, "1");
616 hrnCfgArgKeyRawZ(argList, cfgOptPgPath, 1, TEST_PATH "/pg1");
617 hrnCfgArgKeyRawZ(argList, cfgOptPgPath, 8, TEST_PATH "/pg8");
618 hrnCfgArgKeyRawZ(argList, cfgOptPgPort, 8, "5433");
619 HRN_CFG_LOAD(cfgCmdBackup, argList);
620
621 harnessPqScriptSet((HarnessPq [])
622 {
623 HRNPQ_MACRO_OPEN_LE_91(1, "dbname='postgres' port=5432", PG_VERSION_84, TEST_PATH "/pg1", NULL, NULL),
624 HRNPQ_MACRO_OPEN_LE_91(8, "dbname='postgres' port=5433", PG_VERSION_84, TEST_PATH "/pg8", NULL, NULL),
625
626 HRNPQ_MACRO_CLOSE(1),
627 HRNPQ_MACRO_CLOSE(8),
628
629 HRNPQ_MACRO_DONE()
630 });
631
632 TEST_ERROR(dbGet(true, true, false), DbConnectError, "more than one primary cluster found");
633
634 // -------------------------------------------------------------------------------------------------------------------------
635 TEST_TITLE("two standbys found but no primary");
636
637 harnessPqScriptSet((HarnessPq [])
638 {
639 HRNPQ_MACRO_OPEN_GE_92(1, "dbname='postgres' port=5432", PG_VERSION_92, TEST_PATH "/pg1", true, NULL, NULL),
640 HRNPQ_MACRO_OPEN_GE_92(8, "dbname='postgres' port=5433", PG_VERSION_92, TEST_PATH "/pg8", true, NULL, NULL),
641
642 HRNPQ_MACRO_CLOSE(8),
643 HRNPQ_MACRO_CLOSE(1),
644
645 HRNPQ_MACRO_DONE()
646 });
647
648 TEST_ERROR(dbGet(false, true, false), DbConnectError, "unable to find primary cluster - cannot proceed");
649
650 // -------------------------------------------------------------------------------------------------------------------------
651 TEST_TITLE("two standbys and primary not required");
652
653 harnessPqScriptSet((HarnessPq [])
654 {
655 HRNPQ_MACRO_OPEN_GE_92(1, "dbname='postgres' port=5432", PG_VERSION_92, TEST_PATH "/pg1", true, NULL, NULL),
656 HRNPQ_MACRO_OPEN_GE_92(8, "dbname='postgres' port=5433", PG_VERSION_92, TEST_PATH "/pg8", true, NULL, NULL),
657
658 HRNPQ_MACRO_CLOSE(8),
659 HRNPQ_MACRO_CLOSE(1),
660
661 HRNPQ_MACRO_DONE()
662 });
663
664 TEST_ASSIGN(result, dbGet(false, false, false), "get standbys");
665
666 TEST_RESULT_INT(result.primaryIdx, 0, "check primary id");
667 TEST_RESULT_BOOL(result.primary == NULL, true, "check primary");
668 TEST_RESULT_INT(result.standbyIdx, 0, "check standby id");
669 TEST_RESULT_BOOL(result.standby != NULL, true, "check standby");
670
671 TEST_RESULT_VOID(dbFree(result.standby), "free standby");
672
673 // -------------------------------------------------------------------------------------------------------------------------
674 TEST_TITLE("primary and standby found");
675
676 argList = strLstNew();
677 hrnCfgArgRawZ(argList, cfgOptStanza, "test1");
678 hrnCfgArgKeyRawZ(argList, cfgOptRepoRetentionFull, 1, "1");
679 hrnCfgArgKeyRawZ(argList, cfgOptPgPath, 1, TEST_PATH "/pg1");
680 hrnCfgArgKeyRawZ(argList, cfgOptPgPath, 4, TEST_PATH "/pg4");
681 hrnCfgArgKeyRawZ(argList, cfgOptPgPort, 4, "5433");
682 hrnCfgArgKeyRawZ(argList, cfgOptPgHost, 5, "localhost");
683 hrnCfgArgKeyRawZ(argList, cfgOptPgHostUser, 5, TEST_USER);
684 hrnCfgArgKeyRawZ(argList, cfgOptPgPath, 5, TEST_PATH "/pg5");
685 hrnCfgArgKeyRawZ(argList, cfgOptPgPath, 8, TEST_PATH "/pg8");
686 hrnCfgArgKeyRawZ(argList, cfgOptPgPort, 8, "5434");
687 HRN_CFG_LOAD(cfgCmdBackup, argList);
688
689 harnessPqScriptSet((HarnessPq [])
690 {
691 HRNPQ_MACRO_OPEN_GE_92(1, "dbname='postgres' port=5432", PG_VERSION_92, TEST_PATH "/pg1", true, NULL, NULL),
692
693 // pg-4 error
694 {.session = 4, .function = HRNPQ_CONNECTDB, .param = "[\"dbname='postgres' port=5433\"]"},
695 {.session = 4, .function = HRNPQ_STATUS, .resultInt = CONNECTION_BAD},
696 {.session = 4, .function = HRNPQ_ERRORMESSAGE, .resultZ = "error"},
697 {.session = 4, .function = HRNPQ_FINISH},
698
699 HRNPQ_MACRO_OPEN_GE_92(8, "dbname='postgres' port=5434", PG_VERSION_92, TEST_PATH "/pg8", false, NULL, NULL),
700
701 HRNPQ_MACRO_CREATE_RESTORE_POINT(8, "2/3"),
702 HRNPQ_MACRO_WAL_SWITCH(8, "xlog", "000000010000000200000003"),
703
704 HRNPQ_MACRO_CLOSE(8),
705 HRNPQ_MACRO_CLOSE(1),
706
707 HRNPQ_MACRO_DONE()
708 });
709
710 TEST_ASSIGN(result, dbGet(false, true, false), "get primary and standy");
711
712 hrnLogReplaceAdd("(could not connect to server|connection to server on socket).*$", NULL, "PG ERROR", false);
713 TEST_RESULT_LOG(
714 "P00 WARN: unable to check pg-4: [DbConnectError] unable to connect to 'dbname='postgres' port=5433': error\n"
715 "P00 WARN: unable to check pg-5: [DbConnectError] raised from remote-0 protocol on 'localhost':"
716 " unable to connect to 'dbname='postgres' port=5432': [PG ERROR]");
717
718 TEST_RESULT_INT(result.primaryIdx, 3, "check primary idx");
719 TEST_RESULT_BOOL(result.primary != NULL, true, "check primary");
720 TEST_RESULT_STR_Z(dbArchiveMode(result.primary), "on", "dbArchiveMode");
721 TEST_RESULT_STR_Z(dbArchiveCommand(result.primary), PROJECT_BIN, "dbArchiveCommand");
722 TEST_RESULT_STR_Z(dbWalSwitch(result.primary), "000000010000000200000003", "wal switch");
723 TEST_RESULT_INT(result.standbyIdx, 0, "check standby id");
724 TEST_RESULT_BOOL(result.standby != NULL, true, "check standby");
725
726 TEST_RESULT_VOID(dbFree(result.primary), "free primary");
727 TEST_RESULT_VOID(dbFree(result.standby), "free standby");
728 }
729
730 FUNCTION_HARNESS_RETURN_VOID();
731 }
732