1 /***********************************************************************************************************************************
2 PostgreSQL Interface
3 ***********************************************************************************************************************************/
4 #include "build.auto.h"
5 
6 #include <string.h>
7 
8 #include "common/debug.h"
9 #include "common/log.h"
10 #include "common/memContext.h"
11 #include "common/regExp.h"
12 #include "postgres/interface.h"
13 #include "postgres/interface/version.h"
14 #include "postgres/version.h"
15 #include "storage/helper.h"
16 
17 /***********************************************************************************************************************************
18 Defines for various Postgres paths and files
19 ***********************************************************************************************************************************/
20 STRING_EXTERN(PG_FILE_PGVERSION_STR,                                PG_FILE_PGVERSION);
21 STRING_EXTERN(PG_FILE_POSTGRESQLAUTOCONF_STR,                       PG_FILE_POSTGRESQLAUTOCONF);
22 STRING_EXTERN(PG_FILE_POSTMASTERPID_STR,                            PG_FILE_POSTMASTERPID);
23 STRING_EXTERN(PG_FILE_RECOVERYCONF_STR,                             PG_FILE_RECOVERYCONF);
24 STRING_EXTERN(PG_FILE_RECOVERYDONE_STR,                             PG_FILE_RECOVERYDONE);
25 STRING_EXTERN(PG_FILE_RECOVERYSIGNAL_STR,                           PG_FILE_RECOVERYSIGNAL);
26 STRING_EXTERN(PG_FILE_STANDBYSIGNAL_STR,                            PG_FILE_STANDBYSIGNAL);
27 
28 STRING_EXTERN(PG_PATH_GLOBAL_STR,                                   PG_PATH_GLOBAL);
29 
30 STRING_EXTERN(PG_NAME_WAL_STR,                                      PG_NAME_WAL);
31 STRING_EXTERN(PG_NAME_XLOG_STR,                                     PG_NAME_XLOG);
32 
33 // Wal path names depending on version
34 STRING_STATIC(PG_PATH_PGWAL_STR,                                    "pg_wal");
35 STRING_STATIC(PG_PATH_PGXLOG_STR,                                   "pg_xlog");
36 
37 // Transaction commit log path names depending on version
38 STRING_STATIC(PG_PATH_PGCLOG_STR,                                   "pg_clog");
39 STRING_STATIC(PG_PATH_PGXACT_STR,                                   "pg_xact");
40 
41 // Lsn name used in functions depnding on version
42 STRING_STATIC(PG_NAME_LSN_STR,                                      "lsn");
43 STRING_STATIC(PG_NAME_LOCATION_STR,                                 "location");
44 
45 /***********************************************************************************************************************************
46 The control file is 8192 bytes but only the first 512 bytes are used to prevent torn pages even on really old storage with 512-byte
47 sectors. This is true across all versions of PostgreSQL.
48 ***********************************************************************************************************************************/
49 #define PG_CONTROL_DATA_SIZE                                        512
50 
51 /***********************************************************************************************************************************
52 PostgreSQL interface definitions
53 
54 Each supported version of PostgreSQL must have interface files named postgres/interface/vXXX.c/h that implement the functions
55 specified in the interface structure below.  The functions are documented here rather than in the interface files so that a change
56 in wording does not need to be propagated through N source files.
57 ***********************************************************************************************************************************/
58 typedef struct PgInterface
59 {
60     // Version of PostgreSQL supported by this interface
61     unsigned int version;
62 
63     // Does pg_control match this version of PostgreSQL?
64     bool (*controlIs)(const unsigned char *);
65 
66     // Convert pg_control to a common data structure
67     PgControl (*control)(const unsigned char *);
68 
69     // Get the control version for this version of PostgreSQL
70     uint32_t (*controlVersion)(void);
71 
72     // Does the WAL header match this version of PostgreSQL?
73     bool (*walIs)(const unsigned char *);
74 
75     // Convert WAL header to a common data structure
76     PgWal (*wal)(const unsigned char *);
77 } PgInterface;
78 
79 static const PgInterface pgInterface[] =
80 {
81     {
82         .version = PG_VERSION_14,
83 
84         .controlIs = pgInterfaceControlIs140,
85         .control = pgInterfaceControl140,
86         .controlVersion = pgInterfaceControlVersion140,
87 
88         .walIs = pgInterfaceWalIs140,
89         .wal = pgInterfaceWal140,
90     },
91     {
92         .version = PG_VERSION_13,
93 
94         .controlIs = pgInterfaceControlIs130,
95         .control = pgInterfaceControl130,
96         .controlVersion = pgInterfaceControlVersion130,
97 
98         .walIs = pgInterfaceWalIs130,
99         .wal = pgInterfaceWal130,
100     },
101     {
102         .version = PG_VERSION_12,
103 
104         .controlIs = pgInterfaceControlIs120,
105         .control = pgInterfaceControl120,
106         .controlVersion = pgInterfaceControlVersion120,
107 
108         .walIs = pgInterfaceWalIs120,
109         .wal = pgInterfaceWal120,
110     },
111     {
112         .version = PG_VERSION_11,
113 
114         .controlIs = pgInterfaceControlIs110,
115         .control = pgInterfaceControl110,
116         .controlVersion = pgInterfaceControlVersion110,
117 
118         .walIs = pgInterfaceWalIs110,
119         .wal = pgInterfaceWal110,
120     },
121     {
122         .version = PG_VERSION_10,
123 
124         .controlIs = pgInterfaceControlIs100,
125         .control = pgInterfaceControl100,
126         .controlVersion = pgInterfaceControlVersion100,
127 
128         .walIs = pgInterfaceWalIs100,
129         .wal = pgInterfaceWal100,
130     },
131     {
132         .version = PG_VERSION_96,
133 
134         .controlIs = pgInterfaceControlIs096,
135         .control = pgInterfaceControl096,
136         .controlVersion = pgInterfaceControlVersion096,
137 
138         .walIs = pgInterfaceWalIs096,
139         .wal = pgInterfaceWal096,
140     },
141     {
142         .version = PG_VERSION_95,
143 
144         .controlIs = pgInterfaceControlIs095,
145         .control = pgInterfaceControl095,
146         .controlVersion = pgInterfaceControlVersion095,
147 
148         .walIs = pgInterfaceWalIs095,
149         .wal = pgInterfaceWal095,
150     },
151     {
152         .version = PG_VERSION_94,
153 
154         .controlIs = pgInterfaceControlIs094,
155         .control = pgInterfaceControl094,
156         .controlVersion = pgInterfaceControlVersion094,
157 
158         .walIs = pgInterfaceWalIs094,
159         .wal = pgInterfaceWal094,
160     },
161     {
162         .version = PG_VERSION_93,
163 
164         .controlIs = pgInterfaceControlIs093,
165         .control = pgInterfaceControl093,
166         .controlVersion = pgInterfaceControlVersion093,
167 
168         .walIs = pgInterfaceWalIs093,
169         .wal = pgInterfaceWal093,
170     },
171     {
172         .version = PG_VERSION_92,
173 
174         .controlIs = pgInterfaceControlIs092,
175         .control = pgInterfaceControl092,
176         .controlVersion = pgInterfaceControlVersion092,
177 
178         .walIs = pgInterfaceWalIs092,
179         .wal = pgInterfaceWal092,
180     },
181     {
182         .version = PG_VERSION_91,
183 
184         .controlIs = pgInterfaceControlIs091,
185         .control = pgInterfaceControl091,
186         .controlVersion = pgInterfaceControlVersion091,
187 
188         .walIs = pgInterfaceWalIs091,
189         .wal = pgInterfaceWal091,
190     },
191     {
192         .version = PG_VERSION_90,
193 
194         .controlIs = pgInterfaceControlIs090,
195         .control = pgInterfaceControl090,
196         .controlVersion = pgInterfaceControlVersion090,
197 
198         .walIs = pgInterfaceWalIs090,
199         .wal = pgInterfaceWal090,
200     },
201     {
202         .version = PG_VERSION_84,
203 
204         .controlIs = pgInterfaceControlIs084,
205         .control = pgInterfaceControl084,
206         .controlVersion = pgInterfaceControlVersion084,
207 
208         .walIs = pgInterfaceWalIs084,
209         .wal = pgInterfaceWal084,
210     },
211     {
212         .version = PG_VERSION_83,
213 
214         .controlIs = pgInterfaceControlIs083,
215         .control = pgInterfaceControl083,
216         .controlVersion = pgInterfaceControlVersion083,
217 
218         .walIs = pgInterfaceWalIs083,
219         .wal = pgInterfaceWal083,
220     },
221 };
222 
223 // Total PostgreSQL versions in pgInterface
224 #define PG_INTERFACE_SIZE                                           (sizeof(pgInterface) / sizeof(PgInterface))
225 
226 /***********************************************************************************************************************************
227 These pg_control fields are common to all versions of PostgreSQL, so we can use them to generate error messages when the pg_control
228 version cannot be found.
229 ***********************************************************************************************************************************/
230 typedef struct PgControlCommon
231 {
232     uint64_t systemId;
233     uint32_t controlVersion;
234     uint32_t catalogVersion;
235 } PgControlCommon;
236 
237 /***********************************************************************************************************************************
238 Get the interface for a PostgreSQL version
239 ***********************************************************************************************************************************/
240 static const PgInterface *
pgInterfaceVersion(unsigned int pgVersion)241 pgInterfaceVersion(unsigned int pgVersion)
242 {
243     FUNCTION_TEST_BEGIN();
244         FUNCTION_TEST_PARAM(UINT, pgVersion);
245     FUNCTION_TEST_END();
246 
247     const PgInterface *result = NULL;
248 
249     for (unsigned int interfaceIdx = 0; interfaceIdx < PG_INTERFACE_SIZE; interfaceIdx++)
250     {
251         if (pgInterface[interfaceIdx].version == pgVersion)
252         {
253             result = &pgInterface[interfaceIdx];
254             break;
255         }
256     }
257 
258     // If the version was not found then error
259     if (result == NULL)
260         THROW_FMT(AssertError, "invalid " PG_NAME " version %u", pgVersion);
261 
262     FUNCTION_TEST_RETURN(result);
263 }
264 
265 /***********************************************************************************************************************************
266 Check expected WAL segment size for older PostgreSQL versions
267 ***********************************************************************************************************************************/
268 static void
pgWalSegmentSizeCheck(unsigned int pgVersion,unsigned int walSegmentSize)269 pgWalSegmentSizeCheck(unsigned int pgVersion, unsigned int walSegmentSize)
270 {
271     FUNCTION_TEST_BEGIN();
272         FUNCTION_TEST_PARAM(UINT, pgVersion);
273         FUNCTION_TEST_PARAM(UINT, walSegmentSize);
274     FUNCTION_TEST_END();
275 
276     if (pgVersion < PG_VERSION_11 && walSegmentSize != PG_WAL_SEGMENT_SIZE_DEFAULT)
277     {
278         THROW_FMT(
279             FormatError, "wal segment size is %u but must be %u for " PG_NAME " <= " PG_VERSION_10_STR, walSegmentSize,
280             PG_WAL_SEGMENT_SIZE_DEFAULT);
281     }
282 
283     FUNCTION_TEST_RETURN_VOID();
284 }
285 
286 /**********************************************************************************************************************************/
287 static PgControl
pgControlFromBuffer(const Buffer * controlFile)288 pgControlFromBuffer(const Buffer *controlFile)
289 {
290     FUNCTION_LOG_BEGIN(logLevelTrace);
291         FUNCTION_LOG_PARAM(BUFFER, controlFile);
292     FUNCTION_LOG_END();
293 
294     ASSERT(controlFile != NULL);
295 
296     // Search for the version of PostgreSQL that uses this control file
297     const PgInterface *interface = NULL;
298 
299     for (unsigned int interfaceIdx = 0; interfaceIdx < PG_INTERFACE_SIZE; interfaceIdx++)
300     {
301         if (pgInterface[interfaceIdx].controlIs(bufPtrConst(controlFile)))
302         {
303             interface = &pgInterface[interfaceIdx];
304             break;
305         }
306     }
307 
308     // If the version was not found then error with the control and catalog version that were found
309     if (interface == NULL)
310     {
311         const PgControlCommon *controlCommon = (const PgControlCommon *)bufPtrConst(controlFile);
312 
313         THROW_FMT(
314             VersionNotSupportedError,
315             "unexpected control version = %u and catalog version = %u\n"
316                 "HINT: is this version of PostgreSQL supported?",
317             controlCommon->controlVersion, controlCommon->catalogVersion);
318     }
319 
320     // Get info from the control file
321     PgControl result = interface->control(bufPtrConst(controlFile));
322     result.version = interface->version;
323 
324     // Check the segment size
325     pgWalSegmentSizeCheck(result.version, result.walSegmentSize);
326 
327     // Check the page size
328     if (result.pageSize != PG_PAGE_SIZE_DEFAULT)
329         THROW_FMT(FormatError, "page size is %u but must be %u", result.pageSize, PG_PAGE_SIZE_DEFAULT);
330 
331     FUNCTION_LOG_RETURN(PG_CONTROL, result);
332 }
333 
334 PgControl
pgControlFromFile(const Storage * storage)335 pgControlFromFile(const Storage *storage)
336 {
337     FUNCTION_LOG_BEGIN(logLevelDebug);
338         FUNCTION_LOG_PARAM(STORAGE, storage);
339     FUNCTION_LOG_END();
340 
341     ASSERT(storage != NULL);
342 
343     PgControl result = {0};
344 
345     MEM_CONTEXT_TEMP_BEGIN()
346     {
347         // Read control file
348         Buffer *controlFile = storageGetP(
349             storageNewReadP(storage, STRDEF(PG_PATH_GLOBAL "/" PG_FILE_PGCONTROL)), .exactSize = PG_CONTROL_DATA_SIZE);
350 
351         result = pgControlFromBuffer(controlFile);
352     }
353     MEM_CONTEXT_TEMP_END();
354 
355     FUNCTION_LOG_RETURN(PG_CONTROL, result);
356 }
357 
358 /**********************************************************************************************************************************/
359 uint32_t
pgControlVersion(unsigned int pgVersion)360 pgControlVersion(unsigned int pgVersion)
361 {
362     FUNCTION_TEST_BEGIN();
363         FUNCTION_TEST_PARAM(UINT, pgVersion);
364     FUNCTION_TEST_END();
365 
366     FUNCTION_TEST_RETURN(pgInterfaceVersion(pgVersion)->controlVersion());
367 }
368 
369 /***********************************************************************************************************************************
370 These WAL header fields are common to all versions of PostgreSQL, so we can use them to generate error messages when the WAL magic
371 cannot be found.
372 ***********************************************************************************************************************************/
373 typedef struct PgWalCommon
374 {
375     uint16_t magic;
376     uint16_t flag;
377 } PgWalCommon;
378 
379 #define PG_WAL_LONG_HEADER                                          0x0002
380 
381 /**********************************************************************************************************************************/
382 PgWal
pgWalFromBuffer(const Buffer * walBuffer)383 pgWalFromBuffer(const Buffer *walBuffer)
384 {
385     FUNCTION_LOG_BEGIN(logLevelTrace);
386         FUNCTION_LOG_PARAM(BUFFER, walBuffer);
387     FUNCTION_LOG_END();
388 
389     ASSERT(walBuffer != NULL);
390 
391     // Check that this is a long format WAL header
392     if (!(((const PgWalCommon *)bufPtrConst(walBuffer))->flag & PG_WAL_LONG_HEADER))
393         THROW_FMT(FormatError, "first page header in WAL file is expected to be in long format");
394 
395     // Search for the version of PostgreSQL that uses this WAL magic
396     const PgInterface *interface = NULL;
397 
398     for (unsigned int interfaceIdx = 0; interfaceIdx < PG_INTERFACE_SIZE; interfaceIdx++)
399     {
400         if (pgInterface[interfaceIdx].walIs(bufPtrConst(walBuffer)))
401         {
402             interface = &pgInterface[interfaceIdx];
403             break;
404         }
405     }
406 
407     // If the version was not found then error with the magic that was found
408     if (interface == NULL)
409     {
410         THROW_FMT(
411             VersionNotSupportedError,
412             "unexpected WAL magic %u\n"
413                 "HINT: is this version of PostgreSQL supported?",
414             ((const PgWalCommon *)bufPtrConst(walBuffer))->magic);
415     }
416 
417     // Get info from the control file
418     PgWal result = interface->wal(bufPtrConst(walBuffer));
419     result.version = interface->version;
420 
421     // Check the segment size
422     pgWalSegmentSizeCheck(result.version, result.size);
423 
424     FUNCTION_LOG_RETURN(PG_WAL, result);
425 }
426 
427 PgWal
pgWalFromFile(const String * walFile,const Storage * storage)428 pgWalFromFile(const String *walFile, const Storage *storage)
429 {
430     FUNCTION_LOG_BEGIN(logLevelDebug);
431         FUNCTION_LOG_PARAM(STRING, walFile);
432     FUNCTION_LOG_END();
433 
434     ASSERT(walFile != NULL);
435 
436     PgWal result = {0};
437 
438     MEM_CONTEXT_TEMP_BEGIN()
439     {
440         // Read WAL segment header
441         Buffer *walBuffer = storageGetP(storageNewReadP(storage, walFile), .exactSize = PG_WAL_HEADER_SIZE);
442 
443         result = pgWalFromBuffer(walBuffer);
444     }
445     MEM_CONTEXT_TEMP_END();
446 
447     FUNCTION_LOG_RETURN(PG_WAL, result);
448 }
449 
450 /**********************************************************************************************************************************/
451 String *
pgTablespaceId(unsigned int pgVersion,unsigned int pgCatalogVersion)452 pgTablespaceId(unsigned int pgVersion, unsigned int pgCatalogVersion)
453 {
454     FUNCTION_TEST_BEGIN();
455         FUNCTION_TEST_PARAM(UINT, pgVersion);
456         FUNCTION_TEST_PARAM(UINT, pgCatalogVersion);
457     FUNCTION_TEST_END();
458 
459     String *result = NULL;
460 
461     if (pgVersion >= PG_VERSION_90)
462     {
463         MEM_CONTEXT_TEMP_BEGIN()
464         {
465             String *pgVersionStr = pgVersionToStr(pgVersion);
466 
467             MEM_CONTEXT_PRIOR_BEGIN()
468             {
469                 result = strNewFmt("PG_%s_%u", strZ(pgVersionStr), pgCatalogVersion);
470             }
471             MEM_CONTEXT_PRIOR_END();
472         }
473         MEM_CONTEXT_TEMP_END();
474     }
475 
476     FUNCTION_TEST_RETURN(result);
477 }
478 
479 /**********************************************************************************************************************************/
480 uint64_t
pgLsnFromStr(const String * lsn)481 pgLsnFromStr(const String *lsn)
482 {
483     FUNCTION_TEST_BEGIN();
484         FUNCTION_TEST_PARAM(STRING, lsn);
485     FUNCTION_TEST_END();
486 
487     uint64_t result = 0;
488 
489     MEM_CONTEXT_TEMP_BEGIN()
490     {
491         StringList *lsnPart = strLstNewSplit(lsn, FSLASH_STR);
492 
493         CHECK(strLstSize(lsnPart) == 2);
494 
495         result = (cvtZToUInt64Base(strZ(strLstGet(lsnPart, 0)), 16) << 32) + cvtZToUInt64Base(strZ(strLstGet(lsnPart, 1)), 16);
496     }
497     MEM_CONTEXT_TEMP_END();
498 
499     FUNCTION_TEST_RETURN(result);
500 }
501 
502 String *
pgLsnToStr(uint64_t lsn)503 pgLsnToStr(uint64_t lsn)
504 {
505     FUNCTION_TEST_BEGIN();
506         FUNCTION_TEST_PARAM(UINT64, lsn);
507     FUNCTION_TEST_END();
508 
509     FUNCTION_TEST_RETURN(strNewFmt("%x/%x", (unsigned int)(lsn >> 32), (unsigned int)(lsn & 0xFFFFFFFF)));
510 }
511 
512 /**********************************************************************************************************************************/
513 String *
pgLsnToWalSegment(uint32_t timeline,uint64_t lsn,unsigned int walSegmentSize)514 pgLsnToWalSegment(uint32_t timeline, uint64_t lsn, unsigned int walSegmentSize)
515 {
516     FUNCTION_TEST_BEGIN();
517         FUNCTION_TEST_PARAM(UINT, timeline);
518         FUNCTION_TEST_PARAM(UINT64, lsn);
519         FUNCTION_TEST_PARAM(UINT, walSegmentSize);
520     FUNCTION_TEST_END();
521 
522     FUNCTION_TEST_RETURN(
523         strNewFmt("%08X%08X%08X", timeline, (unsigned int)(lsn >> 32), (unsigned int)(lsn & 0xFFFFFFFF) / walSegmentSize));
524 }
525 
526 uint64_t
pgLsnFromWalSegment(const String * walSegment,unsigned int walSegmentSize)527 pgLsnFromWalSegment(const String *walSegment, unsigned int walSegmentSize)
528 {
529     FUNCTION_TEST_BEGIN();
530         FUNCTION_TEST_PARAM(STRING, walSegment);
531         FUNCTION_TEST_PARAM(UINT, walSegmentSize);
532     FUNCTION_TEST_END();
533 
534     ASSERT(walSegment != NULL);
535     ASSERT(strSize(walSegment) == 24);
536     ASSERT(walSegmentSize > 0);
537 
538     FUNCTION_TEST_RETURN(
539         (cvtZToUInt64Base(strZ(strSubN(walSegment, 8, 8)), 16) << 32) +
540         (cvtZToUInt64Base(strZ(strSubN(walSegment, 16, 8)), 16) * walSegmentSize));
541 }
542 
543 /**********************************************************************************************************************************/
544 StringList *
pgLsnRangeToWalSegmentList(unsigned int pgVersion,uint32_t timeline,uint64_t lsnStart,uint64_t lsnStop,unsigned int walSegmentSize)545 pgLsnRangeToWalSegmentList(
546     unsigned int pgVersion, uint32_t timeline, uint64_t lsnStart, uint64_t lsnStop, unsigned int walSegmentSize)
547 {
548     FUNCTION_TEST_BEGIN();
549         FUNCTION_TEST_PARAM(UINT, pgVersion);
550         FUNCTION_TEST_PARAM(UINT, timeline);
551         FUNCTION_TEST_PARAM(UINT64, lsnStart);
552         FUNCTION_TEST_PARAM(UINT64, lsnStop);
553         FUNCTION_TEST_PARAM(UINT, walSegmentSize);
554     FUNCTION_TEST_END();
555 
556     ASSERT(pgVersion != 0);
557     ASSERT(timeline != 0);
558     ASSERT(lsnStart <= lsnStop);
559     ASSERT(walSegmentSize != 0);
560     ASSERT(pgVersion > PG_VERSION_92 || walSegmentSize == PG_WAL_SEGMENT_SIZE_DEFAULT);
561 
562     StringList *result = NULL;
563 
564     MEM_CONTEXT_TEMP_BEGIN()
565     {
566         result = strLstNew();
567 
568         // Skip the FF segment when PostgreSQL <= 9.2 (in this case segment size should always be 16MB)
569         bool skipFF = pgVersion <= PG_VERSION_92;
570 
571         // Calculate the start and stop segments
572         unsigned int startMajor = (unsigned int)(lsnStart >> 32);
573         unsigned int startMinor = (unsigned int)(lsnStart & 0xFFFFFFFF) / walSegmentSize;
574 
575         unsigned int stopMajor = (unsigned int)(lsnStop >> 32);
576         unsigned int stopMinor = (unsigned int)(lsnStop & 0xFFFFFFFF) / walSegmentSize;
577 
578         unsigned int minorPerMajor = 0xFFFFFFFF / walSegmentSize;
579 
580         // Create list
581         strLstAdd(result, strNewFmt("%08X%08X%08X", timeline, startMajor, startMinor));
582 
583         while (!(startMajor == stopMajor && startMinor == stopMinor))
584         {
585             startMinor++;
586 
587             if ((skipFF && startMinor == 0xFF) || (!skipFF && startMinor > minorPerMajor))
588             {
589                 startMajor++;
590                 startMinor = 0;
591             }
592 
593             strLstAdd(result, strNewFmt("%08X%08X%08X", timeline, startMajor, startMinor));
594         }
595 
596         strLstMove(result, memContextPrior());
597     }
598     MEM_CONTEXT_TEMP_END();
599 
600     FUNCTION_TEST_RETURN(result);
601 }
602 
603 /**********************************************************************************************************************************/
604 const String *
pgLsnName(unsigned int pgVersion)605 pgLsnName(unsigned int pgVersion)
606 {
607     FUNCTION_TEST_BEGIN();
608         FUNCTION_TEST_PARAM(UINT, pgVersion);
609     FUNCTION_TEST_END();
610 
611     FUNCTION_TEST_RETURN(pgVersion >= PG_VERSION_WAL_RENAME ? PG_NAME_LSN_STR : PG_NAME_LOCATION_STR);
612 }
613 
614 /***********************************************************************************************************************************
615 Get WAL name (wal/xlog) for a PostgreSQL version
616 ***********************************************************************************************************************************/
617 const String *
pgWalName(unsigned int pgVersion)618 pgWalName(unsigned int pgVersion)
619 {
620     FUNCTION_TEST_BEGIN();
621         FUNCTION_TEST_PARAM(UINT, pgVersion);
622     FUNCTION_TEST_END();
623 
624     FUNCTION_TEST_RETURN(pgVersion >= PG_VERSION_WAL_RENAME ? PG_NAME_WAL_STR : PG_NAME_XLOG_STR);
625 }
626 
627 /**********************************************************************************************************************************/
628 const String *
pgWalPath(unsigned int pgVersion)629 pgWalPath(unsigned int pgVersion)
630 {
631     FUNCTION_TEST_BEGIN();
632         FUNCTION_TEST_PARAM(UINT, pgVersion);
633     FUNCTION_TEST_END();
634 
635     FUNCTION_TEST_RETURN(pgVersion >= PG_VERSION_WAL_RENAME ? PG_PATH_PGWAL_STR : PG_PATH_PGXLOG_STR);
636 }
637 
638 /**********************************************************************************************************************************/
639 const String *
pgXactPath(unsigned int pgVersion)640 pgXactPath(unsigned int pgVersion)
641 {
642     FUNCTION_TEST_BEGIN();
643         FUNCTION_TEST_PARAM(UINT, pgVersion);
644     FUNCTION_TEST_END();
645 
646     FUNCTION_TEST_RETURN(pgVersion >= PG_VERSION_WAL_RENAME ? PG_PATH_PGXACT_STR : PG_PATH_PGCLOG_STR);
647 }
648 
649 /**********************************************************************************************************************************/
650 unsigned int
pgVersionFromStr(const String * version)651 pgVersionFromStr(const String *version)
652 {
653     FUNCTION_LOG_BEGIN(logLevelTrace);
654         FUNCTION_LOG_PARAM(STRING, version);
655     FUNCTION_LOG_END();
656 
657     ASSERT(version != NULL);
658 
659     unsigned int result = 0;
660 
661     MEM_CONTEXT_TEMP_BEGIN()
662     {
663         // If format is not number.number (9.4) or number only (10) then error
664         if (!regExpMatchOne(STRDEF("^[0-9]+[.]*[0-9]+$"), version))
665             THROW_FMT(AssertError, "version %s format is invalid", strZ(version));
666 
667         // If there is a dot set the major and minor versions, else just the major
668         int idxStart = strChr(version, '.');
669         unsigned int major;
670         unsigned int minor = 0;
671 
672         if (idxStart != -1)
673         {
674             major = cvtZToUInt(strZ(strSubN(version, 0, (size_t)idxStart)));
675             minor = cvtZToUInt(strZ(strSub(version, (size_t)idxStart + 1)));
676         }
677         else
678             major = cvtZToUInt(strZ(version));
679 
680         // No check to see if valid/supported PG version is on purpose
681         result = major * 10000 + minor * 100;
682     }
683     MEM_CONTEXT_TEMP_END();
684 
685     FUNCTION_LOG_RETURN(UINT, result);
686 }
687 
688 String *
pgVersionToStr(unsigned int version)689 pgVersionToStr(unsigned int version)
690 {
691     FUNCTION_LOG_BEGIN(logLevelTrace);
692         FUNCTION_LOG_PARAM(UINT, version);
693     FUNCTION_LOG_END();
694 
695     String *result = version >= PG_VERSION_10 ?
696         strNewFmt("%u", version / 10000) : strNewFmt("%u.%u", version / 10000, version % 10000 / 100);
697 
698     FUNCTION_LOG_RETURN(STRING, result);
699 }
700 
701 /**********************************************************************************************************************************/
702 String *
pgControlToLog(const PgControl * pgControl)703 pgControlToLog(const PgControl *pgControl)
704 {
705     return strNewFmt(
706         "{version: %u, systemId: %" PRIu64 ", walSegmentSize: %u, pageChecksum: %s}", pgControl->version, pgControl->systemId,
707         pgControl->walSegmentSize, cvtBoolToConstZ(pgControl->pageChecksum));
708 }
709 
710 String *
pgWalToLog(const PgWal * pgWal)711 pgWalToLog(const PgWal *pgWal)
712 {
713     return strNewFmt("{version: %u, systemId: %" PRIu64 "}", pgWal->version, pgWal->systemId);
714 }
715