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