Aleksander Alekseev <aleksan...@timescale.com> writes: > Hi Dagfinn, > >> Notwitstanding Tom's objections, these are not reasons for not being >> able to manipulate these values from Perl. The `i` and `I` formats for >> pack/unpack (see https://perldoc.perl.org/functions/pack) use what the C >> compiler calls `int`, in terms of both endianness and size. >> >> > For named reasons, manipulating pg_upgrade from Perl is impractical, >> > considering the number of environments we support. >> > >> > I see two possible solutions: >> > >> > 1) Provide a tool written in C that allows changing pg_control, e.g. >> > `pg_writecontoldata` or maybe a flat like `pg_controldata -w`. The >> > tool can be executed from Perl, so it shouldn't know about >> > sizeof(int), alignment and endiness. >> >> 1.5) Use Perl's pack/unpack functions, which are explicitly desgined for >> exactly this use case. > > Thanks for your reply. > > It is my understanding that Perl is not extremely aware of alignment. > For instance if I want to modify the checksum of the file and the > offset of the checksum is let's say 200 bytes on one platform, 204 on > another and 208 on a third, pack/unpack will not help me. > > Or did I miss something?
You have to specify alignment manually using the `x![$type]` notation. I actually wrote up a pack template for the entire control file (assuming all types have alignof==sizeof), and it seems to work. Run the attached with a Postgres 17 data directory as the arguemnt, and it should show all the fields. - ilmari
use v5.14; use strict; use warnings FATAL => 'all'; use File::Spec; use List::Util qw(max pairmap mesh); # pack templates for C types my $XLogRecPtr = 'x![Q]Q'; my $TimeLineID = 'x![L]L'; my $FullTransactionId = 'x![Q]Q'; my $MultiXactId = 'x![L]L'; my $TransactionId = 'x![L]L'; my $MultiXactOffset = 'x![L]L'; my $DBState = 'x![i]i'; my $pg_time_t = 'x![q]q'; my $Oid = 'x![I]I'; my $bool = 'x![C]C'; my $int = 'x![i]i'; my $uint64 = 'x![Q]Q'; my $uint32 = 'x![L]L'; my $int32 = 'x![l]l'; my $double = 'x![d]d'; my $char = 'x![a]a'; my $pg_crc32c = 'x![L]L'; my $CheckPoint = qq{ $XLogRecPtr # redo $TimeLineID # ThisTimeLineID $TimeLineID # PrevTimeLineID $bool # fullPageWrites $int # wal_level $FullTransactionId # nextXid $Oid # nextOid $MultiXactId # nextMulti $MultiXactOffset # nextMultiOffset $TransactionId # oldestXid $Oid # oldestXidDB $MultiXactId # oldestMulti $Oid # oldestMultiDB $pg_time_t # time $TransactionId # oldestCommitTsXid $TransactionId # newestCommitTsXid $TransactionId # oldestActiveXid }; my @CheckPointField = qw{ redo ThisTimeLineID PrevTimeLineID fullPageWrites wal_level nextXid nextOid nextMulti nextMultiOffset oldestXid oldestXidDB oldestMulti oldestMultiDB time oldestCommitTsXid newestCommitTsXid oldestActiveXid }; my $ControlFileData = qq{ $uint64 # system_identifier $uint32 # pg_control_version $uint32 # catalog_version_no $DBState # state $pg_time_t # time $XLogRecPtr # checkPoint $CheckPoint # checkPointCopy $XLogRecPtr # unloggedLSN $XLogRecPtr # minRecoveryPoint $TimeLineID # minRecoveryPointTLI $XLogRecPtr # backupStartPoint $XLogRecPtr # backupEndPoint $bool # backupEndRequired $int # wal_level $bool # wal_log_hints $int # MaxConnections $int # max_worker_processes $int # max_wal_senders $int # max_prepared_xacts $int # max_locks_per_xact $bool # track_commit_timestamp $uint32 # maxAlign $double # floatFormat $uint32 # blcksz $uint32 # relseg_size $uint32 # xlog_blcksz $uint32 # xlog_seg_size $uint32 # nameDataLen $uint32 # indexMaxKeys $uint32 # toast_max_chunk_size $uint32 # loblksize $bool # float8ByVal $uint32 # data_checksum_version ${char}32 # mock_authentication_nonce[MOCK_AUTH_NONCE_LEN] $pg_crc32c # crc }; my @ControlFileDataField = (qw{ system_identifier pg_control_version catalog_version_no state time checkPoint}, @CheckPointField, qw{ unloggedLSN minRecoveryPoint minRecoveryPointTLI backupStartPoint backupEndPoint backupEndRequired wal_level wal_log_hints MaxConnections max_worker_processes max_wal_senders max_prepared_xacts max_locks_per_xact track_commit_timestamp maxAlign floatFormat blcksz relseg_size xlog_blcksz xlog_seg_size nameDataLen indexMaxKeys toast_max_chunk_size loblksize float8ByVal data_checksum_version mock_authentication_nonce crc }); my $MaxFieldLen = max map length, @ControlFileDataField; my $pgdata = shift or die "Usage $0 <pgdata>\n"; my $controlfile = File::Spec->catfile($pgdata, qw(global pg_control)); my $control_file_data = do { local $/; open my $fh, '<:raw', $controlfile or die "Can't open $controlfile: $!\n"; <$fh>; } or die "Can't read $controlfile: $!\n"; my @control_file_data = unpack $ControlFileData, $control_file_data; for my ($i) (0..$#control_file_data) { printf "%-*s %s\n", $MaxFieldLen+1, $ControlFileDataField[$i].':', $control_file_data[$i]; } my %ControlData = mesh \@ControlFileDataField, \@control_file_data; say "last checkpoint: ", scalar gmtime $ControlData{time};