2 #----------------------------------------------------------------------
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.
8 # Note: This does not reformat the .dat files, so you may want
9 # to run reformat_dat_file.pl afterwards.
11 # Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
12 # Portions Copyright (c) 1994, Regents of the University of California
14 # src/include/catalog/renumber_oids.pl
16 #----------------------------------------------------------------------
19 use warnings FATAL
=> 'all';
24 # Must run in src/include/catalog
25 chdir $FindBin::RealBin
or die "could not cd to $FindBin::RealBin: $!\n";
27 use lib
"$FindBin::RealBin/../../backend/catalog/";
30 # We'll need this number.
31 my $FirstGenbkiObjectId =
32 Catalog
::FindDefinedSymbol
('access/transam.h', '..', 'FirstGenbkiObjectId');
34 # Process command line switches.
36 my $first_mapped_oid = 0;
37 my $last_mapped_oid = $FirstGenbkiObjectId - 1;
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
();
46 # Sanity check arguments.
47 die "Unexpected non-switch arguments.\n" if @ARGV;
48 die "--first-mapped-oid must be specified.\n"
49 if $first_mapped_oid <= 0;
50 die "Empty mapped OID range.\n"
51 if $last_mapped_oid < $first_mapped_oid;
52 die "--target-oid must be specified.\n"
54 die "--target-oid must not be within mapped OID range.\n"
55 if $target_oid >= $first_mapped_oid && $target_oid <= $last_mapped_oid;
57 # Make sure output_path ends in a slash.
58 if ($output_path ne '' && substr($output_path, -1) ne '/')
63 # Collect all the existing assigned OIDs (including those to be remapped).
64 my @header_files = glob("pg_*.h");
65 my $oids = Catalog
::FindAllOidsFromHeaders
(@header_files);
67 # Hash-ify the existing OIDs for convenient lookup.
69 @oidhash{@
$oids} = undef;
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.
76 my $next_oid = $target_oid;
79 my $mapped_oid = $first_mapped_oid;
80 $mapped_oid <= $last_mapped_oid;
83 next if !exists $oidhash{$mapped_oid};
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;
95 die "There are no OIDs in the mapped range.\n" if $next_oid == $target_oid;
97 # Read each .h file and write out modified data.
98 foreach my $input_file (@header_files)
100 $input_file =~ /(\w+)\.h$/
101 or die "Input file $input_file needs to be a .h file.\n";
104 # Ignore generated *_d.h files.
105 next if $catname =~ /_d$/;
107 open(my $ifd, '<', $input_file) || die "$input_file: $!";
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: $!";
117 # Scan the input file.
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+)\)/)
128 if (exists $maphash{$oid2})
130 $oid2 = $maphash{$oid2};
131 my $repl = $1 . $oid2 . $3 . $oid4 . ")";
132 $line =~ s/^DECLARE_TOAST\(\s*\w+,\s*\d+,\s*\d+\)/$repl/;
135 if (exists $maphash{$oid4})
137 $oid4 = $maphash{$oid4};
138 my $repl = $1 . $oid2 . $3 . $oid4 . ")";
139 $line =~ s/^DECLARE_TOAST\(\s*\w+,\s*\d+,\s*\d+\)/$repl/;
144 m/^(DECLARE_TOAST_WITH_MACRO\(\s*\w+,\s*)(\d+)(,\s*)(\d+)(,\s*\w+,\s*\w+)\)/
149 if (exists $maphash{$oid2})
151 $oid2 = $maphash{$oid2};
152 my $repl = $1 . $oid2 . $3 . $oid4 . $5 . ")";
154 s/^DECLARE_TOAST_WITH_MACRO\(\s*\w+,\s*\d+,\s*\d+,\s*\w+,\s*\w+\)/$repl/;
157 if (exists $maphash{$oid4})
159 $oid4 = $maphash{$oid4};
160 my $repl = $1 . $oid2 . $3 . $oid4 . $5 . ")";
162 s/^DECLARE_TOAST_WITH_MACRO\(\s*\w+,\s*\d+,\s*\d+,\s*\w+,\s*\w+\)/$repl/;
167 m/^(DECLARE_(UNIQUE_)?INDEX(_PKEY)?\(\s*\w+,\s*)(\d+)(,\s*.+)\)/)
169 if (exists $maphash{$4})
171 my $repl = $1 . $maphash{$4} . $5 . ")";
173 s/^DECLARE_(UNIQUE_)?INDEX(_PKEY)?\(\s*\w+,\s*\d+,\s*.+\)/$repl/;
177 elsif (/^(DECLARE_OID_DEFINING_MACRO\(\s*\w+,\s*)(\d+)\)/)
179 if (exists $maphash{$2})
181 my $repl = $1 . $maphash{$2} . ")";
183 s/^DECLARE_OID_DEFINING_MACRO\(\s*\w+,\s*\d+\)/$repl/;
187 elsif ($line =~ m/^CATALOG\((\w+),(\d+),(\w+)\)/)
189 if (exists $maphash{$2})
192 "CATALOG(" . $1 . "," . $maphash{$2} . "," . $3 . ")";
193 $line =~ s/^CATALOG\(\w+,\d+,\w+\)/$repl/;
197 if ($line =~ m/BKI_ROWTYPE_OID\((\d+),(\w+)\)/)
199 if (exists $maphash{$1})
202 "BKI_ROWTYPE_OID(" . $maphash{$1} . "," . $2 . ")";
203 $line =~ s/BKI_ROWTYPE_OID\(\d+,\w+\)/$repl/;
215 # Avoid updating files if we didn't change them.
216 if ($changed || $output_path ne '')
218 rename $tmp_output_file, $output_file
219 or die "can't rename $tmp_output_file to $output_file: $!";
223 unlink $tmp_output_file
224 or die "can't unlink $tmp_output_file: $!";
228 # Likewise, read each .dat file and write out modified data.
229 foreach my $input_file (glob("pg_*.dat"))
231 $input_file =~ /(\w+)\.dat$/
232 or die "Input file $input_file needs to be a .dat file.\n";
235 open(my $ifd, '<', $input_file) || die "$input_file: $!";
237 # Write output files to specified directory.
238 # Use a .tmp suffix, then rename into place, in case we're overwriting.
239 my $output_file = "$output_path$catname.dat";
240 my $tmp_output_file = "$output_file.tmp";
241 open my $ofd, '>', $tmp_output_file
242 or die "can't open $tmp_output_file: $!";
245 # Scan the input file.
250 # Check for oid => 'nnnn', and replace if within mapped range.
251 if ($line =~ m/\b(oid\s*=>\s*)'(\d+)'/)
253 if (exists $maphash{$2})
255 my $repl = $1 . "'" . $maphash{$2} . "'";
256 $line =~ s/\boid\s*=>\s*'\d+'/$repl/;
261 # Likewise for array_type_oid.
262 if ($line =~ m/\b(array_type_oid\s*=>\s*)'(\d+)'/)
264 if (exists $maphash{$2})
266 my $repl = $1 . "'" . $maphash{$2} . "'";
267 $line =~ s/\barray_type_oid\s*=>\s*'\d+'/$repl/;
278 # Avoid updating files if we didn't change them.
279 if ($changed || $output_path ne '')
281 rename $tmp_output_file, $output_file
282 or die "can't rename $tmp_output_file to $output_file: $!";
286 unlink $tmp_output_file
287 or die "can't unlink $tmp_output_file: $!";
293 my $last = $FirstGenbkiObjectId - 1;
295 Usage: renumber_oids.pl [--output PATH] --first-mapped-oid X [--last-mapped-oid Y] --target-oid Z
298 --output PATH output directory (default '.')
299 --first-mapped-oid X first OID to be moved
300 --last-mapped-oid Y last OID to be moved (default $last)
301 --target-oid Z first OID to move to
303 Catalog *.h and *.dat files are updated and written to the
304 output directory; by default, this overwrites the input files.
306 Caution: the output PATH will be interpreted relative to
307 src/include/catalog, even if you start the script
308 in some other directory.