mirror of
https://github.com/postgres/postgres.git
synced 2025-11-09 06:21:09 +03:00
The signedness of the 'char' type in C is implementation-dependent. For instance, 'signed char' is used by default on x86 CPUs, while 'unsigned char' is used on aarch CPUs. Previously, we accidentally let C implementation signedness affect persistent data. This led to inconsistent results when comparing char data across different platforms. This commit introduces a new 'default_char_signedness' field in ControlFileData to store the signedness of the 'char' type. While this change does not encourage the use of 'char' without explicitly specifying its signedness, this field can be used as a hint to ensure consistent behavior for pre-v18 data files that store data sorted by the 'char' type on disk (e.g., GIN and GiST indexes), especially in cross-platform replication scenarios. Newly created database clusters unconditionally set the default char signedness to true. pg_upgrade (with an upcoming commit) changes this flag for clusters if the source database cluster has signedness=false. As a result, signedness=false setting will become rare over time. If we had known about the problem during the last development cycle that forced initdb (v8.3), we would have made all clusters signed or all clusters unsigned. Making pg_upgrade the only source of signedness=false will cause the population of database clusters to converge toward that retrospective ideal. Bump catalog version (for the catalog changes) and PG_CONTROL_VERSION (for the additions in ControlFileData). Reviewed-by: Noah Misch <noah@leadboat.com> Discussion: https://postgr.es/m/CB11ADBC-0C3F-4FE0-A678-666EE80CBB07%40amazon.com
264 lines
7.1 KiB
C
264 lines
7.1 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* pg_controldata.c
|
|
*
|
|
* Routines to expose the contents of the control data file via
|
|
* a set of SQL functions.
|
|
*
|
|
* Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
* IDENTIFICATION
|
|
* src/backend/utils/misc/pg_controldata.c
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
|
|
#include "access/htup_details.h"
|
|
#include "access/transam.h"
|
|
#include "access/xlog.h"
|
|
#include "access/xlog_internal.h"
|
|
#include "catalog/pg_control.h"
|
|
#include "common/controldata_utils.h"
|
|
#include "funcapi.h"
|
|
#include "miscadmin.h"
|
|
#include "storage/lwlock.h"
|
|
#include "utils/builtins.h"
|
|
#include "utils/pg_lsn.h"
|
|
#include "utils/timestamp.h"
|
|
|
|
Datum
|
|
pg_control_system(PG_FUNCTION_ARGS)
|
|
{
|
|
Datum values[4];
|
|
bool nulls[4];
|
|
TupleDesc tupdesc;
|
|
HeapTuple htup;
|
|
ControlFileData *ControlFile;
|
|
bool crc_ok;
|
|
|
|
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
|
|
elog(ERROR, "return type must be a row type");
|
|
|
|
/* read the control file */
|
|
LWLockAcquire(ControlFileLock, LW_SHARED);
|
|
ControlFile = get_controlfile(DataDir, &crc_ok);
|
|
LWLockRelease(ControlFileLock);
|
|
if (!crc_ok)
|
|
ereport(ERROR,
|
|
(errmsg("calculated CRC checksum does not match value stored in file")));
|
|
|
|
values[0] = Int32GetDatum(ControlFile->pg_control_version);
|
|
nulls[0] = false;
|
|
|
|
values[1] = Int32GetDatum(ControlFile->catalog_version_no);
|
|
nulls[1] = false;
|
|
|
|
values[2] = Int64GetDatum(ControlFile->system_identifier);
|
|
nulls[2] = false;
|
|
|
|
values[3] = TimestampTzGetDatum(time_t_to_timestamptz(ControlFile->time));
|
|
nulls[3] = false;
|
|
|
|
htup = heap_form_tuple(tupdesc, values, nulls);
|
|
|
|
PG_RETURN_DATUM(HeapTupleGetDatum(htup));
|
|
}
|
|
|
|
Datum
|
|
pg_control_checkpoint(PG_FUNCTION_ARGS)
|
|
{
|
|
Datum values[18];
|
|
bool nulls[18];
|
|
TupleDesc tupdesc;
|
|
HeapTuple htup;
|
|
ControlFileData *ControlFile;
|
|
XLogSegNo segno;
|
|
char xlogfilename[MAXFNAMELEN];
|
|
bool crc_ok;
|
|
|
|
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
|
|
elog(ERROR, "return type must be a row type");
|
|
|
|
/* Read the control file. */
|
|
LWLockAcquire(ControlFileLock, LW_SHARED);
|
|
ControlFile = get_controlfile(DataDir, &crc_ok);
|
|
LWLockRelease(ControlFileLock);
|
|
if (!crc_ok)
|
|
ereport(ERROR,
|
|
(errmsg("calculated CRC checksum does not match value stored in file")));
|
|
|
|
/*
|
|
* Calculate name of the WAL file containing the latest checkpoint's REDO
|
|
* start point.
|
|
*/
|
|
XLByteToSeg(ControlFile->checkPointCopy.redo, segno, wal_segment_size);
|
|
XLogFileName(xlogfilename, ControlFile->checkPointCopy.ThisTimeLineID,
|
|
segno, wal_segment_size);
|
|
|
|
/* Populate the values and null arrays */
|
|
values[0] = LSNGetDatum(ControlFile->checkPoint);
|
|
nulls[0] = false;
|
|
|
|
values[1] = LSNGetDatum(ControlFile->checkPointCopy.redo);
|
|
nulls[1] = false;
|
|
|
|
values[2] = CStringGetTextDatum(xlogfilename);
|
|
nulls[2] = false;
|
|
|
|
values[3] = Int32GetDatum(ControlFile->checkPointCopy.ThisTimeLineID);
|
|
nulls[3] = false;
|
|
|
|
values[4] = Int32GetDatum(ControlFile->checkPointCopy.PrevTimeLineID);
|
|
nulls[4] = false;
|
|
|
|
values[5] = BoolGetDatum(ControlFile->checkPointCopy.fullPageWrites);
|
|
nulls[5] = false;
|
|
|
|
values[6] = CStringGetTextDatum(psprintf("%u:%u",
|
|
EpochFromFullTransactionId(ControlFile->checkPointCopy.nextXid),
|
|
XidFromFullTransactionId(ControlFile->checkPointCopy.nextXid)));
|
|
nulls[6] = false;
|
|
|
|
values[7] = ObjectIdGetDatum(ControlFile->checkPointCopy.nextOid);
|
|
nulls[7] = false;
|
|
|
|
values[8] = TransactionIdGetDatum(ControlFile->checkPointCopy.nextMulti);
|
|
nulls[8] = false;
|
|
|
|
values[9] = TransactionIdGetDatum(ControlFile->checkPointCopy.nextMultiOffset);
|
|
nulls[9] = false;
|
|
|
|
values[10] = TransactionIdGetDatum(ControlFile->checkPointCopy.oldestXid);
|
|
nulls[10] = false;
|
|
|
|
values[11] = ObjectIdGetDatum(ControlFile->checkPointCopy.oldestXidDB);
|
|
nulls[11] = false;
|
|
|
|
values[12] = TransactionIdGetDatum(ControlFile->checkPointCopy.oldestActiveXid);
|
|
nulls[12] = false;
|
|
|
|
values[13] = TransactionIdGetDatum(ControlFile->checkPointCopy.oldestMulti);
|
|
nulls[13] = false;
|
|
|
|
values[14] = ObjectIdGetDatum(ControlFile->checkPointCopy.oldestMultiDB);
|
|
nulls[14] = false;
|
|
|
|
values[15] = TransactionIdGetDatum(ControlFile->checkPointCopy.oldestCommitTsXid);
|
|
nulls[15] = false;
|
|
|
|
values[16] = TransactionIdGetDatum(ControlFile->checkPointCopy.newestCommitTsXid);
|
|
nulls[16] = false;
|
|
|
|
values[17] = TimestampTzGetDatum(time_t_to_timestamptz(ControlFile->checkPointCopy.time));
|
|
nulls[17] = false;
|
|
|
|
htup = heap_form_tuple(tupdesc, values, nulls);
|
|
|
|
PG_RETURN_DATUM(HeapTupleGetDatum(htup));
|
|
}
|
|
|
|
Datum
|
|
pg_control_recovery(PG_FUNCTION_ARGS)
|
|
{
|
|
Datum values[5];
|
|
bool nulls[5];
|
|
TupleDesc tupdesc;
|
|
HeapTuple htup;
|
|
ControlFileData *ControlFile;
|
|
bool crc_ok;
|
|
|
|
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
|
|
elog(ERROR, "return type must be a row type");
|
|
|
|
/* read the control file */
|
|
LWLockAcquire(ControlFileLock, LW_SHARED);
|
|
ControlFile = get_controlfile(DataDir, &crc_ok);
|
|
LWLockRelease(ControlFileLock);
|
|
if (!crc_ok)
|
|
ereport(ERROR,
|
|
(errmsg("calculated CRC checksum does not match value stored in file")));
|
|
|
|
values[0] = LSNGetDatum(ControlFile->minRecoveryPoint);
|
|
nulls[0] = false;
|
|
|
|
values[1] = Int32GetDatum(ControlFile->minRecoveryPointTLI);
|
|
nulls[1] = false;
|
|
|
|
values[2] = LSNGetDatum(ControlFile->backupStartPoint);
|
|
nulls[2] = false;
|
|
|
|
values[3] = LSNGetDatum(ControlFile->backupEndPoint);
|
|
nulls[3] = false;
|
|
|
|
values[4] = BoolGetDatum(ControlFile->backupEndRequired);
|
|
nulls[4] = false;
|
|
|
|
htup = heap_form_tuple(tupdesc, values, nulls);
|
|
|
|
PG_RETURN_DATUM(HeapTupleGetDatum(htup));
|
|
}
|
|
|
|
Datum
|
|
pg_control_init(PG_FUNCTION_ARGS)
|
|
{
|
|
Datum values[12];
|
|
bool nulls[12];
|
|
TupleDesc tupdesc;
|
|
HeapTuple htup;
|
|
ControlFileData *ControlFile;
|
|
bool crc_ok;
|
|
|
|
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
|
|
elog(ERROR, "return type must be a row type");
|
|
|
|
/* read the control file */
|
|
LWLockAcquire(ControlFileLock, LW_SHARED);
|
|
ControlFile = get_controlfile(DataDir, &crc_ok);
|
|
LWLockRelease(ControlFileLock);
|
|
if (!crc_ok)
|
|
ereport(ERROR,
|
|
(errmsg("calculated CRC checksum does not match value stored in file")));
|
|
|
|
values[0] = Int32GetDatum(ControlFile->maxAlign);
|
|
nulls[0] = false;
|
|
|
|
values[1] = Int32GetDatum(ControlFile->blcksz);
|
|
nulls[1] = false;
|
|
|
|
values[2] = Int32GetDatum(ControlFile->relseg_size);
|
|
nulls[2] = false;
|
|
|
|
values[3] = Int32GetDatum(ControlFile->xlog_blcksz);
|
|
nulls[3] = false;
|
|
|
|
values[4] = Int32GetDatum(ControlFile->xlog_seg_size);
|
|
nulls[4] = false;
|
|
|
|
values[5] = Int32GetDatum(ControlFile->nameDataLen);
|
|
nulls[5] = false;
|
|
|
|
values[6] = Int32GetDatum(ControlFile->indexMaxKeys);
|
|
nulls[6] = false;
|
|
|
|
values[7] = Int32GetDatum(ControlFile->toast_max_chunk_size);
|
|
nulls[7] = false;
|
|
|
|
values[8] = Int32GetDatum(ControlFile->loblksize);
|
|
nulls[8] = false;
|
|
|
|
values[9] = BoolGetDatum(ControlFile->float8ByVal);
|
|
nulls[9] = false;
|
|
|
|
values[10] = Int32GetDatum(ControlFile->data_checksum_version);
|
|
nulls[10] = false;
|
|
|
|
values[11] = BoolGetDatum(ControlFile->default_char_signedness);
|
|
nulls[11] = false;
|
|
|
|
htup = heap_form_tuple(tupdesc, values, nulls);
|
|
|
|
PG_RETURN_DATUM(HeapTupleGetDatum(htup));
|
|
}
|