mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-07-13 11:36:13 +02:00
27b77ecf9f
Backpatch-through: 10
1127 lines
30 KiB
Perl
1127 lines
30 KiB
Perl
#!/usr/bin/perl
|
|
#----------------------------------------------------------------------
|
|
#
|
|
# genbki.pl
|
|
# Perl script that generates postgres.bki and symbol definition
|
|
# headers from specially formatted header files and data files.
|
|
# postgres.bki is used to initialize the postgres template database.
|
|
#
|
|
# Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
|
|
# Portions Copyright (c) 1994, Regents of the University of California
|
|
#
|
|
# src/backend/catalog/genbki.pl
|
|
#
|
|
#----------------------------------------------------------------------
|
|
|
|
use strict;
|
|
use warnings;
|
|
use Getopt::Long;
|
|
|
|
use FindBin;
|
|
use lib $FindBin::RealBin;
|
|
|
|
use Catalog;
|
|
|
|
my $output_path = '';
|
|
my $major_version;
|
|
my $include_path;
|
|
|
|
my $num_errors = 0;
|
|
|
|
GetOptions(
|
|
'output:s' => \$output_path,
|
|
'set-version:s' => \$major_version,
|
|
'include-path:s' => \$include_path) || usage();
|
|
|
|
# Sanity check arguments.
|
|
die "No input files.\n" unless @ARGV;
|
|
die "--set-version must be specified.\n" unless $major_version;
|
|
die "Invalid version string: $major_version\n"
|
|
unless $major_version =~ /^\d+$/;
|
|
die "--include-path must be specified.\n" unless $include_path;
|
|
|
|
# Make sure paths end with a slash.
|
|
if ($output_path ne '' && substr($output_path, -1) ne '/')
|
|
{
|
|
$output_path .= '/';
|
|
}
|
|
if (substr($include_path, -1) ne '/')
|
|
{
|
|
$include_path .= '/';
|
|
}
|
|
|
|
# Read all the files into internal data structures.
|
|
my @catnames;
|
|
my %catalogs;
|
|
my %catalog_data;
|
|
my @toast_decls;
|
|
my @index_decls;
|
|
my %oidcounts;
|
|
my @system_constraints;
|
|
|
|
foreach my $header (@ARGV)
|
|
{
|
|
$header =~ /(.+)\.h$/
|
|
or die "Input files need to be header files.\n";
|
|
my $datfile = "$1.dat";
|
|
|
|
my $catalog = Catalog::ParseHeader($header);
|
|
my $catname = $catalog->{catname};
|
|
my $schema = $catalog->{columns};
|
|
|
|
if (defined $catname)
|
|
{
|
|
push @catnames, $catname;
|
|
$catalogs{$catname} = $catalog;
|
|
}
|
|
|
|
# While checking for duplicated OIDs, we ignore the pg_class OID and
|
|
# rowtype OID of bootstrap catalogs, as those are expected to appear
|
|
# in the initial data for pg_class and pg_type. For regular catalogs,
|
|
# include these OIDs. (See also Catalog::FindAllOidsFromHeaders
|
|
# if you change this logic.)
|
|
if (!$catalog->{bootstrap})
|
|
{
|
|
$oidcounts{ $catalog->{relation_oid} }++
|
|
if ($catalog->{relation_oid});
|
|
$oidcounts{ $catalog->{rowtype_oid} }++
|
|
if ($catalog->{rowtype_oid});
|
|
}
|
|
|
|
# Not all catalogs have a data file.
|
|
if (-e $datfile)
|
|
{
|
|
my $data = Catalog::ParseData($datfile, $schema, 0);
|
|
$catalog_data{$catname} = $data;
|
|
|
|
foreach my $row (@$data)
|
|
{
|
|
# Generate entries for pg_description and pg_shdescription.
|
|
if (defined $row->{descr})
|
|
{
|
|
my %descr = (
|
|
objoid => $row->{oid},
|
|
classoid => $catalog->{relation_oid},
|
|
objsubid => 0,
|
|
description => $row->{descr});
|
|
|
|
if ($catalog->{shared_relation})
|
|
{
|
|
delete $descr{objsubid};
|
|
push @{ $catalog_data{pg_shdescription} }, \%descr;
|
|
}
|
|
else
|
|
{
|
|
push @{ $catalog_data{pg_description} }, \%descr;
|
|
}
|
|
}
|
|
|
|
# Check for duplicated OIDs while we're at it.
|
|
$oidcounts{ $row->{oid} }++ if defined $row->{oid};
|
|
}
|
|
}
|
|
|
|
# If the header file contained toast or index info, build BKI
|
|
# commands for those, which we'll output later.
|
|
foreach my $toast (@{ $catalog->{toasting} })
|
|
{
|
|
push @toast_decls,
|
|
sprintf "declare toast %s %s on %s\n",
|
|
$toast->{toast_oid}, $toast->{toast_index_oid},
|
|
$toast->{parent_table};
|
|
$oidcounts{ $toast->{toast_oid} }++;
|
|
$oidcounts{ $toast->{toast_index_oid} }++;
|
|
}
|
|
foreach my $index (@{ $catalog->{indexing} })
|
|
{
|
|
push @index_decls,
|
|
sprintf "declare %sindex %s %s %s\n",
|
|
$index->{is_unique} ? 'unique ' : '',
|
|
$index->{index_name}, $index->{index_oid},
|
|
$index->{index_decl};
|
|
$oidcounts{ $index->{index_oid} }++;
|
|
|
|
if ($index->{is_unique})
|
|
{
|
|
$index->{index_decl} =~ /on (\w+) using/;
|
|
my $tblname = $1;
|
|
push @system_constraints,
|
|
sprintf "ALTER TABLE %s ADD %s USING INDEX %s;",
|
|
$tblname,
|
|
$index->{is_pkey} ? "PRIMARY KEY" : "UNIQUE",
|
|
$index->{index_name};
|
|
}
|
|
}
|
|
}
|
|
|
|
# Complain and exit if we found any duplicate OIDs.
|
|
# While duplicate OIDs would only cause a failure if they appear in
|
|
# the same catalog, our project policy is that manually assigned OIDs
|
|
# should be globally unique, to avoid confusion.
|
|
my $found = 0;
|
|
foreach my $oid (keys %oidcounts)
|
|
{
|
|
next unless $oidcounts{$oid} > 1;
|
|
print STDERR "Duplicate OIDs detected:\n" if !$found;
|
|
print STDERR "$oid\n";
|
|
$found++;
|
|
}
|
|
die "found $found duplicate OID(s) in catalog data\n" if $found;
|
|
|
|
|
|
# OIDs not specified in the input files are automatically assigned,
|
|
# starting at FirstGenbkiObjectId, extending up to FirstUnpinnedObjectId.
|
|
# We allow such OIDs to be assigned independently within each catalog.
|
|
my $FirstGenbkiObjectId =
|
|
Catalog::FindDefinedSymbol('access/transam.h', $include_path,
|
|
'FirstGenbkiObjectId');
|
|
my $FirstUnpinnedObjectId =
|
|
Catalog::FindDefinedSymbol('access/transam.h', $include_path,
|
|
'FirstUnpinnedObjectId');
|
|
# Hash of next available OID, indexed by catalog name.
|
|
my %GenbkiNextOids;
|
|
|
|
|
|
# Fetch some special data that we will substitute into the output file.
|
|
# CAUTION: be wary about what symbols you substitute into the .bki file here!
|
|
# It's okay to substitute things that are expected to be really constant
|
|
# within a given Postgres release, such as fixed OIDs. Do not substitute
|
|
# anything that could depend on platform or configuration. (The right place
|
|
# to handle those sorts of things is in initdb.c's bootstrap_template1().)
|
|
my $C_COLLATION_OID =
|
|
Catalog::FindDefinedSymbolFromData($catalog_data{pg_collation},
|
|
'C_COLLATION_OID');
|
|
|
|
|
|
# Fill in pg_class.relnatts by looking at the referenced catalog's schema.
|
|
# This is ugly but there's no better place; Catalog::AddDefaultValues
|
|
# can't do it, for lack of easy access to the other catalog.
|
|
foreach my $row (@{ $catalog_data{pg_class} })
|
|
{
|
|
$row->{relnatts} = scalar(@{ $catalogs{ $row->{relname} }->{columns} });
|
|
}
|
|
|
|
|
|
# Build lookup tables.
|
|
|
|
# access method OID lookup
|
|
my %amoids;
|
|
foreach my $row (@{ $catalog_data{pg_am} })
|
|
{
|
|
$amoids{ $row->{amname} } = $row->{oid};
|
|
}
|
|
|
|
# role OID lookup
|
|
my %authidoids;
|
|
foreach my $row (@{ $catalog_data{pg_authid} })
|
|
{
|
|
$authidoids{ $row->{rolname} } = $row->{oid};
|
|
}
|
|
|
|
# class (relation) OID lookup (note this only covers bootstrap catalogs!)
|
|
my %classoids;
|
|
foreach my $row (@{ $catalog_data{pg_class} })
|
|
{
|
|
$classoids{ $row->{relname} } = $row->{oid};
|
|
}
|
|
|
|
# collation OID lookup
|
|
my %collationoids;
|
|
foreach my $row (@{ $catalog_data{pg_collation} })
|
|
{
|
|
$collationoids{ $row->{collname} } = $row->{oid};
|
|
}
|
|
|
|
# language OID lookup
|
|
my %langoids;
|
|
foreach my $row (@{ $catalog_data{pg_language} })
|
|
{
|
|
$langoids{ $row->{lanname} } = $row->{oid};
|
|
}
|
|
|
|
# namespace (schema) OID lookup
|
|
my %namespaceoids;
|
|
foreach my $row (@{ $catalog_data{pg_namespace} })
|
|
{
|
|
$namespaceoids{ $row->{nspname} } = $row->{oid};
|
|
}
|
|
|
|
# opclass OID lookup
|
|
my %opcoids;
|
|
foreach my $row (@{ $catalog_data{pg_opclass} })
|
|
{
|
|
# There is no unique name, so we need to combine access method
|
|
# and opclass name.
|
|
my $key = sprintf "%s/%s", $row->{opcmethod}, $row->{opcname};
|
|
$opcoids{$key} = $row->{oid};
|
|
}
|
|
|
|
# operator OID lookup
|
|
my %operoids;
|
|
foreach my $row (@{ $catalog_data{pg_operator} })
|
|
{
|
|
# There is no unique name, so we need to invent one that contains
|
|
# the relevant type names.
|
|
my $key = sprintf "%s(%s,%s)",
|
|
$row->{oprname}, $row->{oprleft}, $row->{oprright};
|
|
$operoids{$key} = $row->{oid};
|
|
}
|
|
|
|
# opfamily OID lookup
|
|
my %opfoids;
|
|
foreach my $row (@{ $catalog_data{pg_opfamily} })
|
|
{
|
|
# There is no unique name, so we need to combine access method
|
|
# and opfamily name.
|
|
my $key = sprintf "%s/%s", $row->{opfmethod}, $row->{opfname};
|
|
$opfoids{$key} = $row->{oid};
|
|
}
|
|
|
|
# procedure OID lookup
|
|
my %procoids;
|
|
foreach my $row (@{ $catalog_data{pg_proc} })
|
|
{
|
|
# Generate an entry under just the proname (corresponds to regproc lookup)
|
|
my $prokey = $row->{proname};
|
|
if (defined $procoids{$prokey})
|
|
{
|
|
$procoids{$prokey} = 'MULTIPLE';
|
|
}
|
|
else
|
|
{
|
|
$procoids{$prokey} = $row->{oid};
|
|
}
|
|
|
|
# Also generate an entry using proname(proargtypes). This is not quite
|
|
# identical to regprocedure lookup because we don't worry much about
|
|
# special SQL names for types etc; we just use the names in the source
|
|
# proargtypes field. These *should* be unique, but do a multiplicity
|
|
# check anyway.
|
|
$prokey .= '(' . join(',', split(/\s+/, $row->{proargtypes})) . ')';
|
|
if (defined $procoids{$prokey})
|
|
{
|
|
$procoids{$prokey} = 'MULTIPLE';
|
|
}
|
|
else
|
|
{
|
|
$procoids{$prokey} = $row->{oid};
|
|
}
|
|
}
|
|
|
|
# tablespace OID lookup
|
|
my %tablespaceoids;
|
|
foreach my $row (@{ $catalog_data{pg_tablespace} })
|
|
{
|
|
$tablespaceoids{ $row->{spcname} } = $row->{oid};
|
|
}
|
|
|
|
# text search configuration OID lookup
|
|
my %tsconfigoids;
|
|
foreach my $row (@{ $catalog_data{pg_ts_config} })
|
|
{
|
|
$tsconfigoids{ $row->{cfgname} } = $row->{oid};
|
|
}
|
|
|
|
# text search dictionary OID lookup
|
|
my %tsdictoids;
|
|
foreach my $row (@{ $catalog_data{pg_ts_dict} })
|
|
{
|
|
$tsdictoids{ $row->{dictname} } = $row->{oid};
|
|
}
|
|
|
|
# text search parser OID lookup
|
|
my %tsparseroids;
|
|
foreach my $row (@{ $catalog_data{pg_ts_parser} })
|
|
{
|
|
$tsparseroids{ $row->{prsname} } = $row->{oid};
|
|
}
|
|
|
|
# text search template OID lookup
|
|
my %tstemplateoids;
|
|
foreach my $row (@{ $catalog_data{pg_ts_template} })
|
|
{
|
|
$tstemplateoids{ $row->{tmplname} } = $row->{oid};
|
|
}
|
|
|
|
# type lookups
|
|
my %typeoids;
|
|
my %types;
|
|
foreach my $row (@{ $catalog_data{pg_type} })
|
|
{
|
|
# for OID macro substitutions
|
|
$typeoids{ $row->{typname} } = $row->{oid};
|
|
|
|
# for pg_attribute copies of pg_type values
|
|
$types{ $row->{typname} } = $row;
|
|
}
|
|
|
|
# Encoding identifier lookup. This uses the same replacement machinery
|
|
# as for OIDs, but we have to dig the values out of pg_wchar.h.
|
|
my %encids;
|
|
|
|
my $encfile = $include_path . 'mb/pg_wchar.h';
|
|
open(my $ef, '<', $encfile) || die "$encfile: $!";
|
|
|
|
# We're parsing an enum, so start with 0 and increment
|
|
# every time we find an enum member.
|
|
my $encid = 0;
|
|
my $collect_encodings = 0;
|
|
while (<$ef>)
|
|
{
|
|
if (/typedef\s+enum\s+pg_enc/)
|
|
{
|
|
$collect_encodings = 1;
|
|
next;
|
|
}
|
|
|
|
last if /_PG_LAST_ENCODING_/;
|
|
|
|
if ($collect_encodings and /^\s+(PG_\w+)/)
|
|
{
|
|
$encids{$1} = $encid;
|
|
$encid++;
|
|
}
|
|
}
|
|
|
|
close $ef;
|
|
|
|
# Map lookup name to the corresponding hash table.
|
|
my %lookup_kind = (
|
|
pg_am => \%amoids,
|
|
pg_authid => \%authidoids,
|
|
pg_class => \%classoids,
|
|
pg_collation => \%collationoids,
|
|
pg_language => \%langoids,
|
|
pg_namespace => \%namespaceoids,
|
|
pg_opclass => \%opcoids,
|
|
pg_operator => \%operoids,
|
|
pg_opfamily => \%opfoids,
|
|
pg_proc => \%procoids,
|
|
pg_tablespace => \%tablespaceoids,
|
|
pg_ts_config => \%tsconfigoids,
|
|
pg_ts_dict => \%tsdictoids,
|
|
pg_ts_parser => \%tsparseroids,
|
|
pg_ts_template => \%tstemplateoids,
|
|
pg_type => \%typeoids,
|
|
encoding => \%encids);
|
|
|
|
|
|
# Open temp files
|
|
my $tmpext = ".tmp$$";
|
|
my $bkifile = $output_path . 'postgres.bki';
|
|
open my $bki, '>', $bkifile . $tmpext
|
|
or die "can't open $bkifile$tmpext: $!";
|
|
my $schemafile = $output_path . 'schemapg.h';
|
|
open my $schemapg, '>', $schemafile . $tmpext
|
|
or die "can't open $schemafile$tmpext: $!";
|
|
my $fk_info_file = $output_path . 'system_fk_info.h';
|
|
open my $fk_info, '>', $fk_info_file . $tmpext
|
|
or die "can't open $fk_info_file$tmpext: $!";
|
|
my $constraints_file = $output_path . 'system_constraints.sql';
|
|
open my $constraints, '>', $constraints_file . $tmpext
|
|
or die "can't open $constraints_file$tmpext: $!";
|
|
|
|
# Generate postgres.bki and pg_*_d.h headers.
|
|
|
|
# version marker for .bki file
|
|
print $bki "# PostgreSQL $major_version\n";
|
|
|
|
# vars to hold data needed for schemapg.h
|
|
my %schemapg_entries;
|
|
my @tables_needing_macros;
|
|
|
|
# produce output, one catalog at a time
|
|
foreach my $catname (@catnames)
|
|
{
|
|
my $catalog = $catalogs{$catname};
|
|
|
|
# Create one definition header with macro definitions for each catalog.
|
|
my $def_file = $output_path . $catname . '_d.h';
|
|
open my $def, '>', $def_file . $tmpext
|
|
or die "can't open $def_file$tmpext: $!";
|
|
|
|
# Opening boilerplate for pg_*_d.h
|
|
printf $def <<EOM, $catname, $catname, uc $catname, uc $catname;
|
|
/*-------------------------------------------------------------------------
|
|
*
|
|
* %s_d.h
|
|
* Macro definitions for %s
|
|
*
|
|
* Portions Copyright (c) 1996-2022, 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 src/backend/catalog/genbki.pl
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#ifndef %s_D_H
|
|
#define %s_D_H
|
|
|
|
EOM
|
|
|
|
# Emit OID macros for catalog's OID and rowtype OID, if wanted
|
|
printf $def "#define %s %s\n",
|
|
$catalog->{relation_oid_macro}, $catalog->{relation_oid}
|
|
if $catalog->{relation_oid_macro};
|
|
printf $def "#define %s %s\n",
|
|
$catalog->{rowtype_oid_macro}, $catalog->{rowtype_oid}
|
|
if $catalog->{rowtype_oid_macro};
|
|
|
|
foreach my $index (@{ $catalog->{indexing} })
|
|
{
|
|
printf $def "#define %s %s\n",
|
|
$index->{index_oid_macro}, $index->{index_oid}
|
|
if $index->{index_oid_macro};
|
|
}
|
|
|
|
print $def "\n";
|
|
|
|
# .bki CREATE command for this catalog
|
|
print $bki "create $catname $catalog->{relation_oid}"
|
|
. $catalog->{shared_relation}
|
|
. $catalog->{bootstrap}
|
|
. $catalog->{rowtype_oid_clause};
|
|
|
|
my $first = 1;
|
|
|
|
print $bki "\n (\n";
|
|
my $schema = $catalog->{columns};
|
|
my %attnames;
|
|
my $attnum = 0;
|
|
foreach my $column (@$schema)
|
|
{
|
|
$attnum++;
|
|
my $attname = $column->{name};
|
|
my $atttype = $column->{type};
|
|
|
|
# Build hash of column names for use later
|
|
$attnames{$attname} = 1;
|
|
|
|
# Emit column definitions
|
|
if (!$first)
|
|
{
|
|
print $bki " ,\n";
|
|
}
|
|
$first = 0;
|
|
|
|
print $bki " $attname = $atttype";
|
|
|
|
if (defined $column->{forcenotnull})
|
|
{
|
|
print $bki " FORCE NOT NULL";
|
|
}
|
|
elsif (defined $column->{forcenull})
|
|
{
|
|
print $bki " FORCE NULL";
|
|
}
|
|
|
|
# Emit Anum_* constants
|
|
printf $def "#define Anum_%s_%s %s\n", $catname, $attname, $attnum;
|
|
}
|
|
print $bki "\n )\n";
|
|
|
|
# Emit Natts_* constant
|
|
print $def "\n#define Natts_$catname $attnum\n\n";
|
|
|
|
# Emit client code copied from source header
|
|
foreach my $line (@{ $catalog->{client_code} })
|
|
{
|
|
print $def $line;
|
|
}
|
|
|
|
# Open it, unless it's a bootstrap catalog (create bootstrap does this
|
|
# automatically)
|
|
if (!$catalog->{bootstrap})
|
|
{
|
|
print $bki "open $catname\n";
|
|
}
|
|
|
|
# For pg_attribute.h, we generate data entries ourselves.
|
|
if ($catname eq 'pg_attribute')
|
|
{
|
|
gen_pg_attribute($schema);
|
|
}
|
|
|
|
# Ordinary catalog with a data file
|
|
foreach my $row (@{ $catalog_data{$catname} })
|
|
{
|
|
my %bki_values = %$row;
|
|
|
|
# Complain about unrecognized keys; they are presumably misspelled
|
|
foreach my $key (keys %bki_values)
|
|
{
|
|
next
|
|
if $key eq "oid_symbol"
|
|
|| $key eq "array_type_oid"
|
|
|| $key eq "descr"
|
|
|| $key eq "autogenerated"
|
|
|| $key eq "line_number";
|
|
die sprintf "unrecognized field name \"%s\" in %s.dat line %s\n",
|
|
$key, $catname, $bki_values{line_number}
|
|
if (!exists($attnames{$key}));
|
|
}
|
|
|
|
# Perform required substitutions on fields
|
|
foreach my $column (@$schema)
|
|
{
|
|
my $attname = $column->{name};
|
|
my $atttype = $column->{type};
|
|
|
|
# Assign oid if oid column exists and no explicit assignment in row
|
|
if ($attname eq "oid" and not defined $bki_values{$attname})
|
|
{
|
|
$bki_values{$attname} = assign_next_oid($catname);
|
|
}
|
|
|
|
# Replace OID synonyms with OIDs per the appropriate lookup rule.
|
|
#
|
|
# If the column type is oidvector or _oid, we have to replace
|
|
# each element of the array as per the lookup rule.
|
|
if ($column->{lookup})
|
|
{
|
|
my $lookup = $lookup_kind{ $column->{lookup} };
|
|
my $lookup_opt = $column->{lookup_opt};
|
|
my @lookupnames;
|
|
my @lookupoids;
|
|
|
|
die "unrecognized BKI_LOOKUP type " . $column->{lookup}
|
|
if !defined($lookup);
|
|
|
|
if ($atttype eq 'oidvector')
|
|
{
|
|
@lookupnames = split /\s+/, $bki_values{$attname};
|
|
@lookupoids =
|
|
lookup_oids($lookup, $catname, $attname, $lookup_opt,
|
|
\%bki_values, @lookupnames);
|
|
$bki_values{$attname} = join(' ', @lookupoids);
|
|
}
|
|
elsif ($atttype eq '_oid')
|
|
{
|
|
if ($bki_values{$attname} ne '_null_')
|
|
{
|
|
$bki_values{$attname} =~ s/[{}]//g;
|
|
@lookupnames = split /,/, $bki_values{$attname};
|
|
@lookupoids =
|
|
lookup_oids($lookup, $catname, $attname,
|
|
$lookup_opt, \%bki_values, @lookupnames);
|
|
$bki_values{$attname} = sprintf "{%s}",
|
|
join(',', @lookupoids);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
$lookupnames[0] = $bki_values{$attname};
|
|
@lookupoids =
|
|
lookup_oids($lookup, $catname, $attname, $lookup_opt,
|
|
\%bki_values, @lookupnames);
|
|
$bki_values{$attname} = $lookupoids[0];
|
|
}
|
|
}
|
|
}
|
|
|
|
# Special hack to generate OID symbols for pg_type entries
|
|
if ($catname eq 'pg_type')
|
|
{
|
|
die sprintf
|
|
"custom OID symbols are not allowed for pg_type entries: '%s'",
|
|
$bki_values{oid_symbol}
|
|
if defined $bki_values{oid_symbol};
|
|
|
|
my $symbol = form_pg_type_symbol($bki_values{typname});
|
|
$bki_values{oid_symbol} = $symbol
|
|
if defined $symbol;
|
|
}
|
|
|
|
# Write to postgres.bki
|
|
print_bki_insert(\%bki_values, $schema);
|
|
|
|
# Emit OID symbol
|
|
if (defined $bki_values{oid_symbol})
|
|
{
|
|
# OID symbols for builtin functions are handled automatically
|
|
# by utils/Gen_fmgrtab.pl
|
|
die sprintf
|
|
"custom OID symbols are not allowed for pg_proc entries: '%s'",
|
|
$bki_values{oid_symbol}
|
|
if $catname eq 'pg_proc';
|
|
|
|
printf $def "#define %s %s\n",
|
|
$bki_values{oid_symbol}, $bki_values{oid};
|
|
}
|
|
}
|
|
|
|
print $bki "close $catname\n";
|
|
printf $def "\n#endif\t\t\t\t\t\t\t/* %s_D_H */\n", uc $catname;
|
|
|
|
# Close and rename definition header
|
|
close $def;
|
|
Catalog::RenameTempFile($def_file, $tmpext);
|
|
}
|
|
|
|
# Any information needed for the BKI that is not contained in a pg_*.h header
|
|
# (i.e., not contained in a header with a CATALOG() statement) comes here
|
|
|
|
# Write out declare toast/index statements
|
|
foreach my $declaration (@toast_decls)
|
|
{
|
|
print $bki $declaration;
|
|
}
|
|
|
|
foreach my $declaration (@index_decls)
|
|
{
|
|
print $bki $declaration;
|
|
}
|
|
|
|
# last command in the BKI file: build the indexes declared above
|
|
print $bki "build indices\n";
|
|
|
|
# Now generate system_constraints.sql
|
|
|
|
foreach my $c (@system_constraints)
|
|
{
|
|
# leave blank lines to localize any bootstrap error messages better
|
|
print $constraints $c, "\n\n";
|
|
}
|
|
|
|
# Now generate schemapg.h
|
|
|
|
# Opening boilerplate for schemapg.h
|
|
print $schemapg <<EOM;
|
|
/*-------------------------------------------------------------------------
|
|
*
|
|
* schemapg.h
|
|
* Schema_pg_xxx macros for use by relcache.c
|
|
*
|
|
* Portions Copyright (c) 1996-2022, 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 src/backend/catalog/genbki.pl
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#ifndef SCHEMAPG_H
|
|
#define SCHEMAPG_H
|
|
EOM
|
|
|
|
# Emit schemapg declarations
|
|
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";
|
|
}
|
|
|
|
# Closing boilerplate for schemapg.h
|
|
print $schemapg "\n#endif\t\t\t\t\t\t\t/* SCHEMAPG_H */\n";
|
|
|
|
# Now generate system_fk_info.h
|
|
|
|
# Opening boilerplate for system_fk_info.h
|
|
print $fk_info <<EOM;
|
|
/*-------------------------------------------------------------------------
|
|
*
|
|
* system_fk_info.h
|
|
* Data about the foreign-key relationships in the system catalogs
|
|
*
|
|
* Portions Copyright (c) 1996-2022, 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 src/backend/catalog/genbki.pl
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#ifndef SYSTEM_FK_INFO_H
|
|
#define SYSTEM_FK_INFO_H
|
|
|
|
typedef struct SysFKRelationship
|
|
{
|
|
Oid fk_table; /* referencing catalog */
|
|
Oid pk_table; /* referenced catalog */
|
|
const char *fk_columns; /* referencing column name(s) */
|
|
const char *pk_columns; /* referenced column name(s) */
|
|
bool is_array; /* if true, last fk_column is an array */
|
|
bool is_opt; /* if true, fk_column can be zero */
|
|
} SysFKRelationship;
|
|
|
|
static const SysFKRelationship sys_fk_relationships[] = {
|
|
EOM
|
|
|
|
# Emit system_fk_info data
|
|
foreach my $catname (@catnames)
|
|
{
|
|
my $catalog = $catalogs{$catname};
|
|
foreach my $fkinfo (@{ $catalog->{foreign_keys} })
|
|
{
|
|
my $pktabname = $fkinfo->{pk_table};
|
|
|
|
# We use BKI_LOOKUP for encodings, but there's no real catalog there
|
|
next if $pktabname eq 'encoding';
|
|
|
|
printf $fk_info
|
|
"\t{ /* %s */ %s, /* %s */ %s, \"{%s}\", \"{%s}\", %s, %s},\n",
|
|
$catname, $catalog->{relation_oid},
|
|
$pktabname, $catalogs{$pktabname}->{relation_oid},
|
|
$fkinfo->{fk_cols},
|
|
$fkinfo->{pk_cols},
|
|
($fkinfo->{is_array} ? "true" : "false"),
|
|
($fkinfo->{is_opt} ? "true" : "false");
|
|
}
|
|
}
|
|
|
|
# Closing boilerplate for system_fk_info.h
|
|
print $fk_info "};\n\n#endif\t\t\t\t\t\t\t/* SYSTEM_FK_INFO_H */\n";
|
|
|
|
# We're done emitting data
|
|
close $bki;
|
|
close $schemapg;
|
|
close $fk_info;
|
|
close $constraints;
|
|
|
|
# Finally, rename the completed files into place.
|
|
Catalog::RenameTempFile($bkifile, $tmpext);
|
|
Catalog::RenameTempFile($schemafile, $tmpext);
|
|
Catalog::RenameTempFile($fk_info_file, $tmpext);
|
|
Catalog::RenameTempFile($constraints_file, $tmpext);
|
|
|
|
exit ($num_errors != 0 ? 1 : 0);
|
|
|
|
#################### Subroutines ########################
|
|
|
|
|
|
# For each catalog marked as needing a schema macro, generate the
|
|
# per-user-attribute data to be incorporated into schemapg.h. Also, for
|
|
# bootstrap catalogs, emit pg_attribute entries into the .bki file
|
|
# for both user and system attributes.
|
|
sub gen_pg_attribute
|
|
{
|
|
my $schema = shift;
|
|
|
|
my @attnames;
|
|
foreach my $column (@$schema)
|
|
{
|
|
push @attnames, $column->{name};
|
|
}
|
|
|
|
foreach my $table_name (@catnames)
|
|
{
|
|
my $table = $catalogs{$table_name};
|
|
|
|
# Currently, all bootstrap catalogs also need schemapg.h
|
|
# entries, so skip if it isn't to be in schemapg.h.
|
|
next if !$table->{schema_macro};
|
|
|
|
$schemapg_entries{$table_name} = [];
|
|
push @tables_needing_macros, $table_name;
|
|
|
|
# Generate entries for user attributes.
|
|
my $attnum = 0;
|
|
my $priorfixedwidth = 1;
|
|
foreach my $attr (@{ $table->{columns} })
|
|
{
|
|
$attnum++;
|
|
my %row;
|
|
$row{attnum} = $attnum;
|
|
$row{attrelid} = $table->{relation_oid};
|
|
|
|
morph_row_for_pgattr(\%row, $schema, $attr, $priorfixedwidth);
|
|
|
|
# Update $priorfixedwidth --- must match morph_row_for_pgattr
|
|
$priorfixedwidth &=
|
|
($row{attnotnull} eq 't'
|
|
&& ($row{attlen} eq 'NAMEDATALEN' || $row{attlen} > 0));
|
|
|
|
# If it's bootstrapped, put an entry in postgres.bki.
|
|
print_bki_insert(\%row, $schema) if $table->{bootstrap};
|
|
|
|
# Store schemapg entries for later.
|
|
morph_row_for_schemapg(\%row, $schema);
|
|
push @{ $schemapg_entries{$table_name} },
|
|
sprintf "{ %s }",
|
|
join(', ', grep { defined $_ } @row{@attnames});
|
|
}
|
|
|
|
# Generate entries for system attributes.
|
|
# We only need postgres.bki entries, not schemapg.h entries.
|
|
if ($table->{bootstrap})
|
|
{
|
|
$attnum = 0;
|
|
my @SYS_ATTRS = (
|
|
{ name => 'ctid', type => 'tid' },
|
|
{ name => 'xmin', type => 'xid' },
|
|
{ name => 'cmin', type => 'cid' },
|
|
{ name => 'xmax', type => 'xid' },
|
|
{ name => 'cmax', type => 'cid' },
|
|
{ name => 'tableoid', type => 'oid' });
|
|
foreach my $attr (@SYS_ATTRS)
|
|
{
|
|
$attnum--;
|
|
my %row;
|
|
$row{attnum} = $attnum;
|
|
$row{attrelid} = $table->{relation_oid};
|
|
$row{attstattarget} = '0';
|
|
|
|
morph_row_for_pgattr(\%row, $schema, $attr, 1);
|
|
print_bki_insert(\%row, $schema);
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
# Given $pgattr_schema (the pg_attribute schema for a catalog sufficient for
|
|
# AddDefaultValues), $attr (the description of a catalog row), and
|
|
# $priorfixedwidth (all prior columns are fixed-width and not null),
|
|
# modify the $row hashref for print_bki_insert. This includes setting data
|
|
# from the corresponding pg_type element and filling in any default values.
|
|
# Any value not handled here must be supplied by caller.
|
|
sub morph_row_for_pgattr
|
|
{
|
|
my ($row, $pgattr_schema, $attr, $priorfixedwidth) = @_;
|
|
my $attname = $attr->{name};
|
|
my $atttype = $attr->{type};
|
|
|
|
$row->{attname} = $attname;
|
|
|
|
# Copy the type data from pg_type, and add some type-dependent items
|
|
my $type = $types{$atttype};
|
|
|
|
$row->{atttypid} = $type->{oid};
|
|
$row->{attlen} = $type->{typlen};
|
|
$row->{attbyval} = $type->{typbyval};
|
|
$row->{attalign} = $type->{typalign};
|
|
$row->{attstorage} = $type->{typstorage};
|
|
|
|
# set attndims if it's an array type
|
|
$row->{attndims} = $type->{typcategory} eq 'A' ? '1' : '0';
|
|
|
|
# collation-aware catalog columns must use C collation
|
|
$row->{attcollation} =
|
|
$type->{typcollation} ne '0' ? $C_COLLATION_OID : 0;
|
|
|
|
if (defined $attr->{forcenotnull})
|
|
{
|
|
$row->{attnotnull} = 't';
|
|
}
|
|
elsif (defined $attr->{forcenull})
|
|
{
|
|
$row->{attnotnull} = 'f';
|
|
}
|
|
elsif ($priorfixedwidth)
|
|
{
|
|
|
|
# attnotnull will automatically be set if the type is
|
|
# fixed-width and prior columns are likewise fixed-width
|
|
# and NOT NULL --- compare DefineAttr in bootstrap.c.
|
|
# At this point the width of type name is still symbolic,
|
|
# so we need a special test.
|
|
$row->{attnotnull} =
|
|
$row->{attlen} eq 'NAMEDATALEN' ? 't'
|
|
: $row->{attlen} > 0 ? 't'
|
|
: 'f';
|
|
}
|
|
else
|
|
{
|
|
$row->{attnotnull} = 'f';
|
|
}
|
|
|
|
Catalog::AddDefaultValues($row, $pgattr_schema, 'pg_attribute');
|
|
return;
|
|
}
|
|
|
|
# Write an entry to postgres.bki.
|
|
sub print_bki_insert
|
|
{
|
|
my $row = shift;
|
|
my $schema = shift;
|
|
|
|
my @bki_values;
|
|
|
|
foreach my $column (@$schema)
|
|
{
|
|
my $attname = $column->{name};
|
|
my $atttype = $column->{type};
|
|
my $bki_value = $row->{$attname};
|
|
|
|
# Fold backslash-zero to empty string if it's the entire string,
|
|
# since that represents a NUL char in C code.
|
|
$bki_value = '' if $bki_value eq '\0';
|
|
|
|
# Handle single quotes by doubling them, because that's what the
|
|
# bootstrap scanner requires. We do not process backslashes
|
|
# specially; this allows escape-string-style backslash escapes
|
|
# to be used in catalog data.
|
|
$bki_value =~ s/'/''/g;
|
|
|
|
# Quote value if needed. We need not quote values that satisfy
|
|
# the "id" pattern in bootscanner.l, currently "[-A-Za-z0-9_]+".
|
|
$bki_value = sprintf("'%s'", $bki_value)
|
|
if length($bki_value) == 0
|
|
or $bki_value =~ /[^-A-Za-z0-9_]/;
|
|
|
|
push @bki_values, $bki_value;
|
|
}
|
|
printf $bki "insert ( %s )\n", join(' ', @bki_values);
|
|
return;
|
|
}
|
|
|
|
# Given a row reference, modify it so that it becomes a valid entry for
|
|
# a catalog schema declaration in schemapg.h.
|
|
#
|
|
# The field values of a Schema_pg_xxx declaration are similar, but not
|
|
# quite identical, to the corresponding values in postgres.bki.
|
|
sub morph_row_for_schemapg
|
|
{
|
|
my $row = shift;
|
|
my $pgattr_schema = shift;
|
|
|
|
foreach my $column (@$pgattr_schema)
|
|
{
|
|
my $attname = $column->{name};
|
|
my $atttype = $column->{type};
|
|
|
|
# Some data types have special formatting rules.
|
|
if ($atttype eq 'name')
|
|
{
|
|
# add {" ... "} quoting
|
|
$row->{$attname} = sprintf(qq'{"%s"}', $row->{$attname});
|
|
}
|
|
elsif ($atttype eq 'char')
|
|
{
|
|
# Add single quotes
|
|
$row->{$attname} = sprintf("'%s'", $row->{$attname});
|
|
}
|
|
|
|
# Expand booleans from 'f'/'t' to 'false'/'true'.
|
|
# Some values might be other macros (eg FLOAT8PASSBYVAL),
|
|
# don't change.
|
|
elsif ($atttype eq 'bool')
|
|
{
|
|
$row->{$attname} = 'true' if $row->{$attname} eq 't';
|
|
$row->{$attname} = 'false' if $row->{$attname} eq 'f';
|
|
}
|
|
|
|
# We don't emit initializers for the variable length fields at all.
|
|
# Only the fixed-size portions of the descriptors are ever used.
|
|
delete $row->{$attname} if $column->{is_varlen};
|
|
}
|
|
return;
|
|
}
|
|
|
|
# Perform OID lookups on an array of OID names.
|
|
# If we don't have a unique value to substitute, warn and
|
|
# leave the entry unchanged.
|
|
# (We don't exit right away so that we can detect multiple problems
|
|
# within this genbki.pl run.)
|
|
sub lookup_oids
|
|
{
|
|
my ($lookup, $catname, $attname, $lookup_opt, $bki_values, @lookupnames)
|
|
= @_;
|
|
|
|
my @lookupoids;
|
|
foreach my $lookupname (@lookupnames)
|
|
{
|
|
my $lookupoid = $lookup->{$lookupname};
|
|
if (defined($lookupoid) and $lookupoid ne 'MULTIPLE')
|
|
{
|
|
push @lookupoids, $lookupoid;
|
|
}
|
|
else
|
|
{
|
|
push @lookupoids, $lookupname;
|
|
if ($lookupname eq '-' or $lookupname eq '0')
|
|
{
|
|
if (!$lookup_opt)
|
|
{
|
|
warn sprintf
|
|
"invalid zero OID reference in %s.dat field %s line %s\n",
|
|
$catname, $attname, $bki_values->{line_number};
|
|
$num_errors++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
warn sprintf
|
|
"unresolved OID reference \"%s\" in %s.dat field %s line %s\n",
|
|
$lookupname, $catname, $attname, $bki_values->{line_number};
|
|
$num_errors++;
|
|
}
|
|
}
|
|
}
|
|
return @lookupoids;
|
|
}
|
|
|
|
# Determine canonical pg_type OID #define symbol from the type name.
|
|
sub form_pg_type_symbol
|
|
{
|
|
my $typename = shift;
|
|
|
|
# Skip for rowtypes of bootstrap catalogs, since they have their
|
|
# own naming convention defined elsewhere.
|
|
return
|
|
if $typename eq 'pg_type'
|
|
or $typename eq 'pg_proc'
|
|
or $typename eq 'pg_attribute'
|
|
or $typename eq 'pg_class';
|
|
|
|
# Transform like so:
|
|
# foo_bar -> FOO_BAROID
|
|
# _foo_bar -> FOO_BARARRAYOID
|
|
$typename =~ /(_)?(.+)/;
|
|
my $arraystr = $1 ? 'ARRAY' : '';
|
|
my $name = uc $2;
|
|
return $name . $arraystr . 'OID';
|
|
}
|
|
|
|
# Assign an unused OID within the specified catalog.
|
|
sub assign_next_oid
|
|
{
|
|
my $catname = shift;
|
|
|
|
# Initialize, if no previous request for this catalog.
|
|
$GenbkiNextOids{$catname} = $FirstGenbkiObjectId
|
|
if !defined($GenbkiNextOids{$catname});
|
|
|
|
my $result = $GenbkiNextOids{$catname}++;
|
|
|
|
# Check that we didn't overrun available OIDs
|
|
die
|
|
"genbki OID counter for $catname reached $result, overrunning FirstUnpinnedObjectId\n"
|
|
if $result >= $FirstUnpinnedObjectId;
|
|
|
|
return $result;
|
|
}
|
|
|
|
sub usage
|
|
{
|
|
die <<EOM;
|
|
Usage: perl -I [directory of Catalog.pm] genbki.pl [--output/-o <path>] [--include-path/-i <path>] header...
|
|
|
|
Options:
|
|
--output Output directory (default '.')
|
|
--set-version PostgreSQL version number for initdb cross-check
|
|
--include-path Include path in source tree
|
|
|
|
genbki.pl generates postgres.bki and symbol definition
|
|
headers from specially formatted header files and .dat
|
|
files. postgres.bki is used to initialize the
|
|
postgres template database.
|
|
|
|
Report bugs to <pgsql-bugs\@lists.postgresql.org>.
|
|
EOM
|
|
}
|