#!/usr/bin/perl
#----------------------------------------------------------------------
#
# unused_oids
#    Finds blocks of manually-assignable OIDs that have not already been
#    claimed by previous hackers.  The main use is for finding available
#    OIDs for new internal functions.  The numbers printed are inclusive
#    ranges of unused OIDs.
#
#    Before using a large empty block, make sure you aren't about
#    to take over what was intended as expansion space for something
#    else.
#
# Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
# Portions Copyright (c) 1994, Regents of the University of California
# Portions Copyright (c) 2023-2026, IvorySQL Global Development Team
#
# src/include/catalog/unused_oids
#
#----------------------------------------------------------------------

use strict;
use warnings FATAL => 'all';

# Must run in src/include/catalog
use FindBin;
chdir $FindBin::RealBin or die "could not cd to $FindBin::RealBin: $!\n";

use lib "$FindBin::RealBin/../../backend/catalog/";
use Catalog;

my @input_files = glob("pg_*.h");

my $oids = Catalog::FindAllOidsFromHeaders(@input_files);

# Also push FirstGenbkiObjectId to serve as a terminator for the last gap.
my $FirstGenbkiObjectId =
  Catalog::FindDefinedSymbol('access/transam.h', '..', 'FirstGenbkiObjectId');
# IvorySQL:BEGIN   add OIDs for new types
my $MinTypeObjectId = 
  Catalog::FindDefinedSymbol('access/transam.h', '..', 'MinTypeObjectId');
my $MaxTypeObjectId = 
  Catalog::FindDefinedSymbol('access/transam.h', '..', 'MaxTypeObjectId');
# IvorySQL:END   add OIDs for new types
push @{$oids}, $FirstGenbkiObjectId;

my $prev_oid = 0;
# IvorySQL:BEGIN   add OIDs for new types
my $min = 0;
my $max = 0;
# IvorySQL:END   add OIDs for new types
my @sortedoids = sort { $a <=> $b } @{$oids};
# IvorySQL: The unused OID values between 9000 and 9100 are specified for the newly added type in IvorySQL,
# IvorySQL: so they are not shown in the search results executed by the script "unused_OIDS"
foreach my $oid (@sortedoids)
{
	if ($oid > $prev_oid + 1)
	{
		if ($oid > $prev_oid + 2)
		{
			#IvorySQL:BEGIN   add OIDs for new types
			$min = $prev_oid + 1;
			$max = $oid - 1;

			if ($max < $MinTypeObjectId || $min > $MaxTypeObjectId)
			{
				printf "%d - %d\n", $min, $max;
			}
			elsif ($min < $MinTypeObjectId)
			{
				if ($MinTypeObjectId - 1 > $min)
				{
					printf "%d - %d\n", $min, $MinTypeObjectId - 1;
				}
				else
				{
					printf "%d\n", $min;
				}
				if ($max > $MaxTypeObjectId)
				{
					if ($max > $MaxTypeObjectId + 1)
					{
						printf "%d - %d\n", $MaxTypeObjectId + 1, $max;
					}
					else
					{
						printf "%d\n", $max;
					}
				}
			}
			elsif ($max > $MaxTypeObjectId)
			{
				if ($max > $MaxTypeObjectId + 1)
				{
					printf "%d - %d\n", $MaxTypeObjectId + 1, $max;
				}
				else
				{
					printf "%d\n", $max;
				}
			}
			# IvorySQL:END   add OIDs for new types
		}
		else
		{
			# IvorySQL:BEGIN   add OIDs for new types
			$min = $prev_oid + 1;
			
			if (($MinTypeObjectId <= $min) && ($min <= $MaxTypeObjectId))
			{
				next;
			}
			else
			{
				printf "%d\n", $min;
			}
			# IvorySQL:END   add OIDs for new types
		}
	}
	$prev_oid = $oid;
}

my $suggestion;
do
{
	$suggestion = int(8000 + rand(2000));
# IvorySQL:BEGIN   add OIDs for new types
} while (grep(/^$suggestion$/, @{$oids}) || ($MinTypeObjectId <= $suggestion) && ($suggestion <= $MaxTypeObjectId));
# IvorySQL:END   add OIDs for new types
my $navailable = 0;
foreach my $oid (@sortedoids)
{
	if ($oid > $suggestion)
	{
		$navailable = $oid - $suggestion;
		last;
	}
}

printf "Patches should use a more-or-less consecutive range of OIDs.\n";
printf
  "Best practice is to start with a random choice in the range 8000-9999.\n";
printf
  "Suggested random unused OID: $suggestion ($navailable consecutive OID(s) available starting here)\n";
