1#!/usr/bin/perl 2#---------------------------------------------------------------------- 3# 4# renumber_oids.pl 5# Perl script that shifts a range of OIDs in the Postgres catalog data 6# to a different range, skipping any OIDs that are already in use. 7# 8# Note: This does not reformat the .dat files, so you may want 9# to run reformat_dat_file.pl afterwards. 10# 11# Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group 12# Portions Copyright (c) 1994, Regents of the University of California 13# 14# src/include/catalog/renumber_oids.pl 15# 16#---------------------------------------------------------------------- 17 18use strict; 19use warnings; 20 21use FindBin; 22use Getopt::Long; 23 24# Must run in src/include/catalog 25chdir $FindBin::RealBin or die "could not cd to $FindBin::RealBin: $!\n"; 26 27use lib "$FindBin::RealBin/../../backend/catalog/"; 28use Catalog; 29 30# We'll need this number. 31my $FirstGenbkiObjectId = 32 Catalog::FindDefinedSymbol('access/transam.h', '..', 'FirstGenbkiObjectId'); 33 34# Process command line switches. 35my $output_path = ''; 36my $first_mapped_oid = 0; 37my $last_mapped_oid = $FirstGenbkiObjectId - 1; 38my $target_oid = 0; 39 40GetOptions( 41 'output=s' => \$output_path, 42 'first-mapped-oid=i' => \$first_mapped_oid, 43 'last-mapped-oid=i' => \$last_mapped_oid, 44 'target-oid=i' => \$target_oid) || usage(); 45 46# Sanity check arguments. 47die "Unexpected non-switch arguments.\n" if @ARGV; 48die "--first-mapped-oid must be specified.\n" 49 if $first_mapped_oid <= 0; 50die "Empty mapped OID range.\n" 51 if $last_mapped_oid < $first_mapped_oid; 52die "--target-oid must be specified.\n" 53 if $target_oid <= 0; 54die "--target-oid must not be within mapped OID range.\n" 55 if $target_oid >= $first_mapped_oid && $target_oid <= $last_mapped_oid; 56 57# Make sure output_path ends in a slash. 58if ($output_path ne '' && substr($output_path, -1) ne '/') 59{ 60 $output_path .= '/'; 61} 62 63# Collect all the existing assigned OIDs (including those to be remapped). 64my @header_files = (glob("pg_*.h"), qw(indexing.h toasting.h)); 65my $oids = Catalog::FindAllOidsFromHeaders(@header_files); 66 67# Hash-ify the existing OIDs for convenient lookup. 68my %oidhash; 69@oidhash{@$oids} = undef; 70 71# Select new OIDs for existing OIDs in the mapped range. 72# We do this first so that we preserve the ordering of the mapped OIDs 73# (for reproducibility's sake), and so that if we fail due to running out 74# of OID room, that happens before we've overwritten any files. 75my %maphash; 76my $next_oid = $target_oid; 77 78for ( 79 my $mapped_oid = $first_mapped_oid; 80 $mapped_oid <= $last_mapped_oid; 81 $mapped_oid++) 82{ 83 next if !exists $oidhash{$mapped_oid}; 84 $next_oid++ 85 while ( 86 exists $oidhash{$next_oid} 87 || ( $next_oid >= $first_mapped_oid 88 && $next_oid <= $last_mapped_oid)); 89 die "Reached FirstGenbkiObjectId before assigning all OIDs.\n" 90 if $next_oid >= $FirstGenbkiObjectId; 91 $maphash{$mapped_oid} = $next_oid; 92 $next_oid++; 93} 94 95die "There are no OIDs in the mapped range.\n" if $next_oid == $target_oid; 96 97# Read each .h file and write out modified data. 98foreach my $input_file (@header_files) 99{ 100 $input_file =~ /(\w+)\.h$/ 101 or die "Input file $input_file needs to be a .h file.\n"; 102 my $catname = $1; 103 104 # Ignore generated *_d.h files. 105 next if $catname =~ /_d$/; 106 107 open(my $ifd, '<', $input_file) || die "$input_file: $!"; 108 109 # Write output files to specified directory. 110 # Use a .tmp suffix, then rename into place, in case we're overwriting. 111 my $output_file = "$output_path$catname.h"; 112 my $tmp_output_file = "$output_file.tmp"; 113 open my $ofd, '>', $tmp_output_file 114 or die "can't open $tmp_output_file: $!"; 115 my $changed = 0; 116 117 # Scan the input file. 118 while (<$ifd>) 119 { 120 my $line = $_; 121 122 # Check for OID-defining macros that Catalog::ParseHeader knows about, 123 # and update OIDs as needed. 124 if ($line =~ m/^(DECLARE_TOAST\(\s*\w+,\s*)(\d+)(,\s*)(\d+)\)/) 125 { 126 my $oid2 = $2; 127 my $oid4 = $4; 128 if (exists $maphash{$oid2}) 129 { 130 $oid2 = $maphash{$oid2}; 131 my $repl = $1 . $oid2 . $3 . $oid4 . ")"; 132 $line =~ s/^DECLARE_TOAST\(\s*\w+,\s*\d+,\s*\d+\)/$repl/; 133 $changed = 1; 134 } 135 if (exists $maphash{$oid4}) 136 { 137 $oid4 = $maphash{$oid4}; 138 my $repl = $1 . $oid2 . $3 . $oid4 . ")"; 139 $line =~ s/^DECLARE_TOAST\(\s*\w+,\s*\d+,\s*\d+\)/$repl/; 140 $changed = 1; 141 } 142 } 143 elsif ( 144 $line =~ m/^(DECLARE_(UNIQUE_)?INDEX\(\s*\w+,\s*)(\d+)(,\s*.+)\)/) 145 { 146 if (exists $maphash{$3}) 147 { 148 my $repl = $1 . $maphash{$3} . $4 . ")"; 149 $line =~ 150 s/^DECLARE_(UNIQUE_)?INDEX\(\s*\w+,\s*\d+,\s*.+\)/$repl/; 151 $changed = 1; 152 } 153 } 154 elsif ($line =~ m/^CATALOG\((\w+),(\d+),(\w+)\)/) 155 { 156 if (exists $maphash{$2}) 157 { 158 my $repl = 159 "CATALOG(" . $1 . "," . $maphash{$2} . "," . $3 . ")"; 160 $line =~ s/^CATALOG\(\w+,\d+,\w+\)/$repl/; 161 $changed = 1; 162 } 163 164 if ($line =~ m/BKI_ROWTYPE_OID\((\d+),(\w+)\)/) 165 { 166 if (exists $maphash{$1}) 167 { 168 my $repl = 169 "BKI_ROWTYPE_OID(" . $maphash{$1} . "," . $2 . ")"; 170 $line =~ s/BKI_ROWTYPE_OID\(\d+,\w+\)/$repl/; 171 $changed = 1; 172 } 173 } 174 } 175 176 # In indexing.h and toasting.h only, check for #define SYM nnnn, 177 # and replace if within mapped range. 178 elsif ($line =~ m/^(\s*#\s*define\s+\w+\s+)(\d+)\b/) 179 { 180 if (($catname eq 'indexing' || $catname eq 'toasting') 181 && exists $maphash{$2}) 182 { 183 my $repl = $1 . $maphash{$2}; 184 $line =~ s/^\s*#\s*define\s+\w+\s+\d+\b/$repl/; 185 $changed = 1; 186 } 187 } 188 189 print $ofd $line; 190 } 191 192 close $ifd; 193 close $ofd; 194 195 # Avoid updating files if we didn't change them. 196 if ($changed || $output_path ne '') 197 { 198 rename $tmp_output_file, $output_file 199 or die "can't rename $tmp_output_file to $output_file: $!"; 200 } 201 else 202 { 203 unlink $tmp_output_file 204 or die "can't unlink $tmp_output_file: $!"; 205 } 206} 207 208# Likewise, read each .dat file and write out modified data. 209foreach my $input_file (glob("pg_*.dat")) 210{ 211 $input_file =~ /(\w+)\.dat$/ 212 or die "Input file $input_file needs to be a .dat file.\n"; 213 my $catname = $1; 214 215 open(my $ifd, '<', $input_file) || die "$input_file: $!"; 216 217 # Write output files to specified directory. 218 # Use a .tmp suffix, then rename into place, in case we're overwriting. 219 my $output_file = "$output_path$catname.dat"; 220 my $tmp_output_file = "$output_file.tmp"; 221 open my $ofd, '>', $tmp_output_file 222 or die "can't open $tmp_output_file: $!"; 223 my $changed = 0; 224 225 # Scan the input file. 226 while (<$ifd>) 227 { 228 my $line = $_; 229 230 # Check for oid => 'nnnn', and replace if within mapped range. 231 if ($line =~ m/\b(oid\s*=>\s*)'(\d+)'/) 232 { 233 if (exists $maphash{$2}) 234 { 235 my $repl = $1 . "'" . $maphash{$2} . "'"; 236 $line =~ s/\boid\s*=>\s*'\d+'/$repl/; 237 $changed = 1; 238 } 239 } 240 241 # Likewise for array_type_oid. 242 if ($line =~ m/\b(array_type_oid\s*=>\s*)'(\d+)'/) 243 { 244 if (exists $maphash{$2}) 245 { 246 my $repl = $1 . "'" . $maphash{$2} . "'"; 247 $line =~ s/\barray_type_oid\s*=>\s*'\d+'/$repl/; 248 $changed = 1; 249 } 250 } 251 252 print $ofd $line; 253 } 254 255 close $ifd; 256 close $ofd; 257 258 # Avoid updating files if we didn't change them. 259 if ($changed || $output_path ne '') 260 { 261 rename $tmp_output_file, $output_file 262 or die "can't rename $tmp_output_file to $output_file: $!"; 263 } 264 else 265 { 266 unlink $tmp_output_file 267 or die "can't unlink $tmp_output_file: $!"; 268 } 269} 270 271sub usage 272{ 273 my $last = $FirstGenbkiObjectId - 1; 274 die <<EOM; 275Usage: renumber_oids.pl [--output PATH] --first-mapped-oid X [--last-mapped-oid Y] --target-oid Z 276 277Options: 278 --output PATH output directory (default '.') 279 --first-mapped-oid X first OID to be moved 280 --last-mapped-oid Y last OID to be moved (default $last) 281 --target-oid Z first OID to move to 282 283Catalog *.h and *.dat files are updated and written to the 284output directory; by default, this overwrites the input files. 285 286Caution: the output PATH will be interpreted relative to 287src/include/catalog, even if you start the script 288in some other directory. 289 290EOM 291} 292