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