summaryrefslogtreecommitdiffstats
path: root/src/backend/catalog/genbki.pl
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:19:15 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:19:15 +0000
commit6eb9c5a5657d1fe77b55cc261450f3538d35a94d (patch)
tree657d8194422a5daccecfd42d654b8a245ef7b4c8 /src/backend/catalog/genbki.pl
parentInitial commit. (diff)
downloadpostgresql-13-6eb9c5a5657d1fe77b55cc261450f3538d35a94d.tar.xz
postgresql-13-6eb9c5a5657d1fe77b55cc261450f3538d35a94d.zip
Adding upstream version 13.4.upstream/13.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/backend/catalog/genbki.pl')
-rw-r--r--src/backend/catalog/genbki.pl977
1 files changed, 977 insertions, 0 deletions
diff --git a/src/backend/catalog/genbki.pl b/src/backend/catalog/genbki.pl
new file mode 100644
index 0000000..b07537f
--- /dev/null
+++ b/src/backend/catalog/genbki.pl
@@ -0,0 +1,977 @@
+#!/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-2020, 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;
+
+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;
+
+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} }++;
+ }
+}
+
+# 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 FirstBootstrapObjectId.
+my $FirstGenbkiObjectId =
+ Catalog::FindDefinedSymbol('access/transam.h', $include_path,
+ 'FirstGenbkiObjectId');
+my $FirstBootstrapObjectId =
+ Catalog::FindDefinedSymbol('access/transam.h', $include_path,
+ 'FirstBootstrapObjectId');
+my $GenbkiNextOid = $FirstGenbkiObjectId;
+
+
+# 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 $BOOTSTRAP_SUPERUSERID =
+ Catalog::FindDefinedSymbolFromData($catalog_data{pg_authid},
+ 'BOOTSTRAP_SUPERUSERID');
+my $C_COLLATION_OID =
+ Catalog::FindDefinedSymbolFromData($catalog_data{pg_collation},
+ 'C_COLLATION_OID');
+my $PG_CATALOG_NAMESPACE =
+ Catalog::FindDefinedSymbolFromData($catalog_data{pg_namespace},
+ 'PG_CATALOG_NAMESPACE');
+
+
+# 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};
+}
+
+# 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};
+}
+
+# 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_class => \%classoids,
+ pg_collation => \%collationoids,
+ pg_language => \%langoids,
+ 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: $!";
+
+# 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-2020, 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};
+ 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} = $GenbkiNextOid;
+ $GenbkiNextOid++;
+ }
+
+ # Substitute constant values we acquired above.
+ # (It's intentional that this can apply to parts of a field).
+ $bki_values{$attname} =~ s/\bPGUID\b/$BOOTSTRAP_SUPERUSERID/g;
+ $bki_values{$attname} =~ s/\bPGNSP\b/$PG_CATALOG_NAMESPACE/g;
+
+ # 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 @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, \%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, \%bki_values,
+ @lookupnames);
+ $bki_values{$attname} = sprintf "{%s}",
+ join(',', @lookupoids);
+ }
+ }
+ else
+ {
+ $lookupnames[0] = $bki_values{$attname};
+ @lookupoids = lookup_oids($lookup, $catname, \%bki_values,
+ @lookupnames);
+ $bki_values{$attname} = $lookupoids[0];
+ }
+ }
+ }
+
+ # Special hack to generate OID symbols for pg_type entries
+ # that lack one.
+ if ($catname eq 'pg_type' and !exists $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})
+ {
+ 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";
+
+# check that we didn't overrun available OIDs
+die
+ "genbki OID counter reached $GenbkiNextOid, overrunning FirstBootstrapObjectId\n"
+ if $GenbkiNextOid > $FirstBootstrapObjectId;
+
+
+# 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-2020, 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";
+
+# We're done emitting data
+close $bki;
+close $schemapg;
+
+# Finally, rename the completed files into place.
+Catalog::RenameTempFile($bkifile, $tmpext);
+Catalog::RenameTempFile($schemafile, $tmpext);
+
+exit 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 $priornotnull = 1;
+ foreach my $attr (@{ $table->{columns} })
+ {
+ $attnum++;
+ my %row;
+ $row{attnum} = $attnum;
+ $row{attrelid} = $table->{relation_oid};
+
+ morph_row_for_pgattr(\%row, $schema, $attr, $priornotnull);
+ $priornotnull &= ($row{attnotnull} eq 't');
+
+ # 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
+# $priornotnull (whether all prior attributes in this catalog are 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, $priornotnull) = @_;
+ 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->{attstorage} = $type->{typstorage};
+ $row->{attalign} = $type->{typalign};
+
+ # 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 ($priornotnull)
+ {
+
+ # attnotnull will automatically be set if the type is
+ # fixed-width and prior columns are all NOT NULL ---
+ # compare DefineAttr in bootstrap.c. oidvector and
+ # int2vector are also treated as not-nullable.
+ $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';
+ }
+
+ 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, and double quotes by
+ # converting them to octal escapes, 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;
+ $bki_value =~ s/"/\\042/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(qq'"%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.
+# (A warning seems sufficient because the bootstrap backend will reject
+# non-numeric values anyway. So we might as well detect multiple problems
+# within this genbki.pl run.)
+sub lookup_oids
+{
+ my ($lookup, $catname, $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;
+ warn sprintf
+ "unresolved OID reference \"%s\" in %s.dat line %s\n",
+ $lookupname, $catname, $bki_values->{line_number}
+ if $lookupname ne '-' and $lookupname ne '0';
+ }
+ }
+ 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';
+}
+
+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
+}