use Catalog;
use strict;
use warnings;
my @input_files;
our @include_path;
my $output_path = '';
my $major_version;
while (@ARGV)
{
my $arg = shift @ARGV;
if ($arg !~ /^-/)
{
push @input_files, $arg;
}
elsif ($arg =~ /^-o/)
{
$output_path = length($arg) > 2 ? substr($arg, 2) : shift @ARGV;
}
elsif ($arg =~ /^-I/)
{
push @include_path, length($arg) > 2 ? substr($arg, 2) : shift @ARGV;
}
elsif ($arg =~ /^--set-version=(.*)$/)
{
$major_version = $1;
die "Version must be in format nn.nn.\n"
if !($major_version =~ /^\d+\.\d+$/);
}
else
{
usage();
}
}
die "No input files.\n" if !@input_files;
die "No include path; you must specify -I at least once.\n" if !@include_path;
die "--set-version must be specified.\n" if !defined $major_version;
if ($output_path ne '' && substr($output_path, -1) ne '/')
{
$output_path .= '/';
}
my $tmpext = ".tmp$$";
my $bkifile = $output_path . 'postgres.bki';
open BKI, '>', $bkifile . $tmpext
or die "can't open $bkifile$tmpext: $!";
my $schemafile = $output_path . 'schemapg.h';
open SCHEMAPG, '>', $schemafile . $tmpext
or die "can't open $schemafile$tmpext: $!";
my $descrfile = $output_path . 'postgres.description';
open DESCR, '>', $descrfile . $tmpext
or die "can't open $descrfile$tmpext: $!";
my $shdescrfile = $output_path . 'postgres.shdescription';
open SHDESCR, '>', $shdescrfile . $tmpext
or die "can't open $shdescrfile$tmpext: $!";
my $BOOTSTRAP_SUPERUSERID =
find_defined_symbol('pg_authid.h', 'BOOTSTRAP_SUPERUSERID');
my $PG_CATALOG_NAMESPACE =
find_defined_symbol('pg_namespace.h', 'PG_CATALOG_NAMESPACE');
my $catalogs = Catalog::Catalogs(@input_files);
print BKI "# PostgreSQL $major_version\n";
my %schemapg_entries;
my @tables_needing_macros;
our @types;
foreach my $catname (@{ $catalogs->{names} })
{
my $catalog = $catalogs->{$catname};
print BKI "create $catname $catalog->{relation_oid}"
. $catalog->{shared_relation}
. $catalog->{bootstrap}
. $catalog->{without_oids}
. $catalog->{rowtype_oid} . "\n";
my %bki_attr;
my @attnames;
foreach my $column (@{ $catalog->{columns} })
{
my ($attname, $atttype) = %$column;
$bki_attr{$attname} = $atttype;
push @attnames, $attname;
}
print BKI " (\n";
print BKI join " ,\n", map(" $_ = $bki_attr{$_}", @attnames);
print BKI "\n )\n";
if ($catalog->{bootstrap} eq '')
{
print BKI "open $catname\n";
}
if (defined $catalog->{data})
{
foreach my $row (@{ $catalog->{data} })
{
$row->{bki_values} =~ s/\bPGUID\b/$BOOTSTRAP_SUPERUSERID/g;
$row->{bki_values} =~ s/\bPGNSP\b/$PG_CATALOG_NAMESPACE/g;
if ($catname eq 'pg_type')
{
my %type;
$type{oid} = $row->{oid};
@type{@attnames} = split /\s+/, $row->{bki_values};
push @types, \%type;
}
my $oid = $row->{oid} ? "OID = $row->{oid} " : '';
printf BKI "insert %s( %s)\n", $oid, $row->{bki_values};
if (defined $row->{descr})
{
printf DESCR "%s\t%s\t0\t%s\n", $row->{oid}, $catname,
$row->{descr};
}
if (defined $row->{shdescr})
{
printf SHDESCR "%s\t%s\t%s\n", $row->{oid}, $catname,
$row->{shdescr};
}
}
}
if ($catname eq 'pg_attribute')
{
foreach my $table_name (@{ $catalogs->{names} })
{
my $table = $catalogs->{$table_name};
next if $table->{schema_macro} ne 'True';
$schemapg_entries{$table_name} = [];
push @tables_needing_macros, $table_name;
my $is_bootstrap = $table->{bootstrap};
my $attnum = 0;
my $priornotnull = 1;
my @user_attrs = @{ $table->{columns} };
foreach my $attr (@user_attrs)
{
$attnum++;
my $row = emit_pgattr_row($table_name, $attr, $priornotnull);
$row->{attnum} = $attnum;
$row->{attstattarget} = '-1';
$priornotnull &= ($row->{attnotnull} eq 't');
if ($is_bootstrap eq ' bootstrap')
{
bki_insert($row, @attnames);
}
$row =
emit_schemapg_row($row,
grep { $bki_attr{$_} eq 'bool' } @attnames);
push @{ $schemapg_entries{$table_name} }, '{ '
. join(
', ', grep { defined $_ }
map $row->{$_}, @attnames) . ' }';
}
if ($is_bootstrap eq ' bootstrap')
{
$attnum = 0;
my @SYS_ATTRS = (
{ ctid => 'tid' },
{ oid => 'oid' },
{ xmin => 'xid' },
{ cmin => 'cid' },
{ xmax => 'xid' },
{ cmax => 'cid' },
{ tableoid => 'oid' }
,{xc_node_id => 'int4'}
);
foreach my $attr (@SYS_ATTRS)
{
$attnum--;
my $row = emit_pgattr_row($table_name, $attr, 1);
$row->{attnum} = $attnum;
$row->{attstattarget} = '0';
next
if $table->{without_oids} eq ' without_oids'
&& $row->{attname} eq 'oid';
bki_insert($row, @attnames);
}
}
}
}
print BKI "close $catname\n";
}
foreach my $declaration (@{ $catalogs->{toasting}->{data} })
{
print BKI $declaration;
}
foreach my $declaration (@{ $catalogs->{indexing}->{data} })
{
print BKI $declaration;
}
print SCHEMAPG <<EOM;
/*-------------------------------------------------------------------------
*
* schemapg.h
* Schema_pg_xxx macros for use by relcache.c
*
* Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* NOTES
* ******************************
* *** DO NOT EDIT THIS FILE! ***
* ******************************
*
* It has been GENERATED by $0
*
*-------------------------------------------------------------------------
*/
EOM
foreach my $table_name (@tables_needing_macros)
{
print SCHEMAPG "\n#define Schema_$table_name \\\n";
print SCHEMAPG join ", \\\n", @{ $schemapg_entries{$table_name} };
print SCHEMAPG "\n";
}
print SCHEMAPG "\n#endif /* SCHEMAPG_H */\n";
close BKI;
close SCHEMAPG;
close DESCR;
close SHDESCR;
Catalog::RenameTempFile($bkifile, $tmpext);
Catalog::RenameTempFile($schemafile, $tmpext);
Catalog::RenameTempFile($descrfile, $tmpext);
Catalog::RenameTempFile($shdescrfile, $tmpext);
exit 0;
sub emit_pgattr_row
{
my ($table_name, $attr, $priornotnull) = @_;
my ($attname, $atttype) = %$attr;
my %row;
$row{attrelid} = $catalogs->{$table_name}->{relation_oid};
$row{attname} = $attname;
if ($atttype =~ /(.+)\[\]$/)
{
$atttype = '_' . $1;
}
foreach my $type (@types)
{
if (defined $type->{typname} && $type->{typname} eq $atttype)
{
$row{atttypid} = $type->{oid};
$row{attlen} = $type->{typlen};
$row{attbyval} = $type->{typbyval};
$row{attstorage} = $type->{typstorage};
$row{attalign} = $type->{typalign};
$row{attndims} = $type->{typcategory} eq 'A' ? '1' : '0';
$row{attcollation} = $type->{typcollation};
if ($priornotnull)
{
$row{attnotnull} =
$type->{typname} eq 'oidvector' ? 't'
: $type->{typname} eq 'int2vector' ? 't'
: $type->{typlen} eq 'NAMEDATALEN' ? 't'
: $type->{typlen} > 0 ? 't'
: 'f';
}
else
{
$row{attnotnull} = 'f';
}
last;
}
}
my %PGATTR_DEFAULTS = (
attcacheoff => '-1',
atttypmod => '-1',
atthasdef => 'f',
attisdropped => 'f',
attcmprmode => '0',
attislocal => 't',
attinhcount => '0',
attacl => '_null_',
attoptions => '_null_',
attfdwoptions => '_null_',
attinitdefval => '_null_',
attkvtype => '0',
attdroppedname => '_null_'
);
return { %PGATTR_DEFAULTS, %row };
}
sub bki_insert
{
my $row = shift;
my @attnames = @_;
my $oid = $row->{oid} ? "OID = $row->{oid} " : '';
my $bki_values = join ' ', map $row->{$_}, @attnames;
printf BKI "insert %s( %s)\n", $oid, $bki_values;
}
sub emit_schemapg_row
{
my $row = shift;
my @bool_attrs = @_;
$row->{attname} = q|{"| . $row->{attname} . q|"}|;
$row->{attstorage} = q|'| . $row->{attstorage} . q|'|;
$row->{attalign} = q|'| . $row->{attalign} . q|'|;
delete $row->{attacl};
delete $row->{attoptions};
delete $row->{attfdwoptions};
delete $row->{attinitdefval};
delete $row->{attdroppedname};
foreach my $attr (@bool_attrs)
{
$row->{$attr} =
$row->{$attr} eq 't' ? 'true'
: $row->{$attr} eq 'f' ? 'false'
: $row->{$attr};
}
return $row;
}
sub find_defined_symbol
{
my ($catalog_header, $symbol) = @_;
for my $path (@include_path)
{
if (substr($path, -1) ne '/')
{
$path .= '/';
}
my $file = $path . $catalog_header;
next if !-f $file;
open(FIND_DEFINED_SYMBOL, '<', $file) || die "$file: $!";
while (<FIND_DEFINED_SYMBOL>)
{
if (/^#define\s+\Q$symbol\E\s+(\S+)/)
{
return $1;
}
}
close FIND_DEFINED_SYMBOL;
die "$file: no definition found for $symbol\n";
}
die "$catalog_header: not found in any include directory\n";
}
sub usage
{
die <<EOM;
Usage: genbki.pl [options] header...
Options:
-I path to include files
-o output path
--set-version PostgreSQL version number for initdb cross-check
genbki.pl generates BKI files from specially formatted
header files. These BKI files are used to initialize the
postgres template database.
Report bugs to <pgsql-bugs\@postgresql.org>.
EOM
}