require Exporter;
our @ISA = qw(Exporter);
our @EXPORT = ();
-our @EXPORT_OK = qw(Catalogs RenameTempFile);
+our @EXPORT_OK = qw(Catalogs SplitDataLine RenameTempFile);
# Call this function with an array of names of header files to parse.
# Returns a nested data structure describing the data in the headers.
return \%catalogs;
}
+# Split a DATA line into fields.
+# Call this on the bki_values element of a DATA item returned by Catalogs();
+# it returns a list of field values. We don't strip quoting from the fields.
+# Note: it should be safe to assign the result to a list of length equal to
+# the nominal number of catalog fields, because check_natts already checked
+# the number of fields.
+sub SplitDataLine
+{
+ my $bki_values = shift;
+
+ # This handling of quoted strings might look too simplistic, but it
+ # matches what bootscanner.l does: that has no provision for quote marks
+ # inside quoted strings, either. If we don't have a quoted string, just
+ # snarf everything till next whitespace. That will accept some things
+ # that bootscanner.l will see as erroneous tokens; but it seems wiser
+ # to do that and let bootscanner.l complain than to silently drop
+ # non-whitespace characters.
+ my @result = $bki_values =~ /"[^"]*"|\S+/g;
+
+ return @result;
+}
+
# Rename temporary files to final names.
# Call this function with the final file name and the .tmp extension
# Note: recommended extension is ".tmp$$", so that parallel make steps
rename($temp_name, $final_name) || die "rename: $temp_name: $!";
}
-# verify the number of fields in the passed-in bki structure
+# verify the number of fields in the passed-in DATA line
sub check_natts
{
my ($catname, $natts, $bki_val, $file, $line) = @_;
+
die "Could not find definition for Natts_${catname} before start of DATA() in $file\n"
unless defined $natts;
- # we're working with a copy and need to count the fields only, so collapse
- $bki_val =~ s/"[^"]*?"/xxx/g;
- my @atts = split /\s+/, $bki_val;
+ my $nfields = scalar(SplitDataLine($bki_val));
die sprintf
"Wrong number of attributes in DATA() entry at %s:%d (expected %d but got %d)\n",
- $file, $line, $natts, scalar @atts
- unless $natts == @atts;
+ $file, $line, $natts, $nfields
+ unless $natts == $nfields;
}
1;
# vars to hold data needed for schemapg.h
my %schemapg_entries;
my @tables_needing_macros;
+my %regprocoids;
our @types;
# produce output, one catalog at a time
foreach my $row (@{ $catalog->{data} })
{
- # substitute constant values we acquired above
- $row->{bki_values} =~ s/\bPGUID\b/$BOOTSTRAP_SUPERUSERID/g;
- $row->{bki_values} =~ s/\bPGNSP\b/$PG_CATALOG_NAMESPACE/g;
+ # Split line into tokens without interpreting their meaning.
+ my %bki_values;
+ @bki_values{@attnames} = Catalog::SplitDataLine($row->{bki_values});
+
+ # Perform required substitutions on fields
+ foreach my $att (keys %bki_values)
+ {
+ # Substitute constant values we acquired above.
+ # (It's intentional that this can apply to parts of a field).
+ $bki_values{$att} =~ s/\bPGUID\b/$BOOTSTRAP_SUPERUSERID/g;
+ $bki_values{$att} =~ s/\bPGNSP\b/$PG_CATALOG_NAMESPACE/g;
+
+ # Replace regproc columns' values with OIDs.
+ # If we don't have a unique value to substitute,
+ # just do nothing (regprocin will complain).
+ if ($bki_attr{$att}->{type} eq 'regproc')
+ {
+ my $procoid = $regprocoids{$bki_values{$att}};
+ $bki_values{$att} = $procoid
+ if defined($procoid) && $procoid ne 'MULTIPLE';
+ }
+ }
+
+ # Save pg_proc oids for use in later regproc substitutions.
+ # This relies on the order we process the files in!
+ if ($catname eq 'pg_proc')
+ {
+ if (defined($regprocoids{$bki_values{proname}}))
+ {
+ $regprocoids{$bki_values{proname}} = 'MULTIPLE';
+ }
+ else
+ {
+ $regprocoids{$bki_values{proname}} = $row->{oid};
+ }
+ }
# Save pg_type info for pg_attribute processing below
if ($catname eq 'pg_type')
{
- my %type;
+ my %type = %bki_values;
$type{oid} = $row->{oid};
- @type{@attnames} = split /\s+/, $row->{bki_values};
push @types, \%type;
}
# Write to postgres.bki
my $oid = $row->{oid} ? "OID = $row->{oid} " : '';
- printf $bki "insert %s( %s)\n", $oid, $row->{bki_values};
+ printf $bki "insert %s( %s )\n", $oid,
+ join(' ', @bki_values{@attnames});
- # Write comments to postgres.description and postgres.shdescription
+ # Write comments to postgres.description and postgres.shdescription
if (defined $row->{descr})
{
printf $descr "%s\t%s\t0\t%s\n", $row->{oid}, $catname,
my @attnames = @_;
my $oid = $row->{oid} ? "OID = $row->{oid} " : '';
my $bki_values = join ' ', map { $_ eq '' ? '""' : $_ } map $row->{$_}, @attnames;
- printf $bki "insert %s( %s)\n", $oid, $bki_values;
+ printf $bki "insert %s( %s )\n", $oid, $bki_values;
}
# The field values of a Schema_pg_xxx declaration are similar, but not
#include <ctype.h>
-#include "access/genam.h"
-#include "access/heapam.h"
#include "access/htup_details.h"
-#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "catalog/pg_class.h"
#include "catalog/pg_operator.h"
#include "miscadmin.h"
#include "parser/parse_type.h"
#include "utils/builtins.h"
-#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
-#include "utils/tqual.h"
#include "utils/acl.h"
#include "utils/regproc.h"
#include "utils/varlena.h"
/* Else it's a name, possibly schema-qualified */
/*
- * In bootstrap mode we assume the given name is not schema-qualified, and
- * just search pg_proc for a unique match. This is needed for
- * initializing other system catalogs (pg_namespace may not exist yet, and
- * certainly there are no schemas other than pg_catalog).
+ * We should never get here in bootstrap mode, as all references should
+ * have been resolved by genbki.pl.
*/
if (IsBootstrapProcessingMode())
- {
- int matches = 0;
- Relation hdesc;
- ScanKeyData skey[1];
- SysScanDesc sysscan;
- HeapTuple tuple;
-
- ScanKeyInit(&skey[0],
- Anum_pg_proc_proname,
- BTEqualStrategyNumber, F_NAMEEQ,
- CStringGetDatum(pro_name_or_oid));
-
- hdesc = heap_open(ProcedureRelationId, AccessShareLock);
- sysscan = systable_beginscan(hdesc, ProcedureNameArgsNspIndexId, true,
- NULL, 1, skey);
-
- while (HeapTupleIsValid(tuple = systable_getnext(sysscan)))
- {
- result = (RegProcedure) HeapTupleGetOid(tuple);
- if (++matches > 1)
- break;
- }
-
- systable_endscan(sysscan);
- heap_close(hdesc, AccessShareLock);
-
- if (matches == 0)
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_FUNCTION),
- errmsg("function \"%s\" does not exist", pro_name_or_oid)));
-
- else if (matches > 1)
- ereport(ERROR,
- (errcode(ERRCODE_AMBIGUOUS_FUNCTION),
- errmsg("more than one function named \"%s\"",
- pro_name_or_oid)));
-
- PG_RETURN_OID(result);
- }
+ elog(ERROR, "regproc values must be OIDs in bootstrap mode");
/*
* Normal case: parse the name into components and see if it matches any
PG_RETURN_OID(result);
}
+ /* The rest of this wouldn't work in bootstrap mode */
+ if (IsBootstrapProcessingMode())
+ elog(ERROR, "regprocedure values must be OIDs in bootstrap mode");
+
/*
* Else it's a name and arguments. Parse the name and arguments, look up
* potential matches in the current namespace search list, and scan to see
* which one exactly matches the given argument types. (There will not be
* more than one match.)
- *
- * XXX at present, this code will not work in bootstrap mode, hence this
- * datatype cannot be used for any system column that needs to receive
- * data during bootstrap.
*/
parseNameAndArgTypes(pro_name_or_oid, false, &names, &nargs, argtypes);
StringInfoData buf;
/* XXX no support here for bootstrap mode */
+ Assert(!IsBootstrapProcessingMode());
initStringInfo(&buf);
/* Else it's a name, possibly schema-qualified */
- /*
- * In bootstrap mode we assume the given name is not schema-qualified, and
- * just search pg_operator for a unique match. This is needed for
- * initializing other system catalogs (pg_namespace may not exist yet, and
- * certainly there are no schemas other than pg_catalog).
- */
+ /* The rest of this wouldn't work in bootstrap mode */
if (IsBootstrapProcessingMode())
- {
- int matches = 0;
- Relation hdesc;
- ScanKeyData skey[1];
- SysScanDesc sysscan;
- HeapTuple tuple;
-
- ScanKeyInit(&skey[0],
- Anum_pg_operator_oprname,
- BTEqualStrategyNumber, F_NAMEEQ,
- CStringGetDatum(opr_name_or_oid));
-
- hdesc = heap_open(OperatorRelationId, AccessShareLock);
- sysscan = systable_beginscan(hdesc, OperatorNameNspIndexId, true,
- NULL, 1, skey);
-
- while (HeapTupleIsValid(tuple = systable_getnext(sysscan)))
- {
- result = HeapTupleGetOid(tuple);
- if (++matches > 1)
- break;
- }
-
- systable_endscan(sysscan);
- heap_close(hdesc, AccessShareLock);
-
- if (matches == 0)
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_FUNCTION),
- errmsg("operator does not exist: %s", opr_name_or_oid)));
- else if (matches > 1)
- ereport(ERROR,
- (errcode(ERRCODE_AMBIGUOUS_FUNCTION),
- errmsg("more than one operator named %s",
- opr_name_or_oid)));
-
- PG_RETURN_OID(result);
- }
+ elog(ERROR, "regoper values must be OIDs in bootstrap mode");
/*
* Normal case: parse the name into components and see if it matches any
PG_RETURN_OID(result);
}
+ /* The rest of this wouldn't work in bootstrap mode */
+ if (IsBootstrapProcessingMode())
+ elog(ERROR, "regoperator values must be OIDs in bootstrap mode");
+
/*
* Else it's a name and arguments. Parse the name and arguments, look up
* potential matches in the current namespace search list, and scan to see
* which one exactly matches the given argument types. (There will not be
* more than one match.)
- *
- * XXX at present, this code will not work in bootstrap mode, hence this
- * datatype cannot be used for any system column that needs to receive
- * data during bootstrap.
*/
parseNameAndArgTypes(opr_name_or_oid, true, &names, &nargs, argtypes);
if (nargs == 1)
StringInfoData buf;
/* XXX no support here for bootstrap mode */
+ Assert(!IsBootstrapProcessingMode());
initStringInfo(&buf);
/* Else it's a name, possibly schema-qualified */
- /*
- * In bootstrap mode we assume the given name is not schema-qualified, and
- * just search pg_class for a match. This is needed for initializing
- * other system catalogs (pg_namespace may not exist yet, and certainly
- * there are no schemas other than pg_catalog).
- */
+ /* The rest of this wouldn't work in bootstrap mode */
if (IsBootstrapProcessingMode())
- {
- Relation hdesc;
- ScanKeyData skey[1];
- SysScanDesc sysscan;
- HeapTuple tuple;
-
- ScanKeyInit(&skey[0],
- Anum_pg_class_relname,
- BTEqualStrategyNumber, F_NAMEEQ,
- CStringGetDatum(class_name_or_oid));
-
- hdesc = heap_open(RelationRelationId, AccessShareLock);
- sysscan = systable_beginscan(hdesc, ClassNameNspIndexId, true,
- NULL, 1, skey);
-
- if (HeapTupleIsValid(tuple = systable_getnext(sysscan)))
- result = HeapTupleGetOid(tuple);
- else
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_TABLE),
- errmsg("relation \"%s\" does not exist", class_name_or_oid)));
-
- /* We assume there can be only one match */
-
- systable_endscan(sysscan);
- heap_close(hdesc, AccessShareLock);
-
- PG_RETURN_OID(result);
- }
+ elog(ERROR, "regclass values must be OIDs in bootstrap mode");
/*
* Normal case: parse the name into components and see if it matches any
/*
* regtypein - converts "typename" to type OID
*
- * We also accept a numeric OID, for symmetry with the output routine.
+ * The type name can be specified using the full type syntax recognized by
+ * the parser; for example, DOUBLE PRECISION and INTEGER[] will work and be
+ * translated to the correct type names. (We ignore any typmod info
+ * generated by the parser, however.)
+ *
+ * We also accept a numeric OID, for symmetry with the output routine,
+ * and for possible use in bootstrap mode.
*
* '-' signifies unknown (OID 0). In all other cases, the input must
* match an existing pg_type entry.
- *
- * In bootstrap mode the name must just equal some existing name in pg_type.
- * In normal mode the type name can be specified using the full type syntax
- * recognized by the parser; for example, DOUBLE PRECISION and INTEGER[] will
- * work and be translated to the correct type names. (We ignore any typmod
- * info generated by the parser, however.)
*/
Datum
regtypein(PG_FUNCTION_ARGS)
/* Else it's a type name, possibly schema-qualified or decorated */
- /*
- * In bootstrap mode we assume the given name is not schema-qualified, and
- * just search pg_type for a match. This is needed for initializing other
- * system catalogs (pg_namespace may not exist yet, and certainly there
- * are no schemas other than pg_catalog).
- */
+ /* The rest of this wouldn't work in bootstrap mode */
if (IsBootstrapProcessingMode())
- {
- Relation hdesc;
- ScanKeyData skey[1];
- SysScanDesc sysscan;
- HeapTuple tuple;
-
- ScanKeyInit(&skey[0],
- Anum_pg_type_typname,
- BTEqualStrategyNumber, F_NAMEEQ,
- CStringGetDatum(typ_name_or_oid));
-
- hdesc = heap_open(TypeRelationId, AccessShareLock);
- sysscan = systable_beginscan(hdesc, TypeNameNspIndexId, true,
- NULL, 1, skey);
-
- if (HeapTupleIsValid(tuple = systable_getnext(sysscan)))
- result = HeapTupleGetOid(tuple);
- else
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("type \"%s\" does not exist", typ_name_or_oid)));
-
- /* We assume there can be only one match */
-
- systable_endscan(sysscan);
- heap_close(hdesc, AccessShareLock);
-
- PG_RETURN_OID(result);
- }
+ elog(ERROR, "regtype values must be OIDs in bootstrap mode");
/*
* Normal case: invoke the full parser to deal with special cases such as
*
* '-' signifies unknown (OID 0). In all other cases, the input must
* match an existing pg_ts_config entry.
- *
- * This function is not needed in bootstrap mode, so we don't worry about
- * making it work then.
*/
Datum
regconfigin(PG_FUNCTION_ARGS)
PG_RETURN_OID(result);
}
+ /* The rest of this wouldn't work in bootstrap mode */
+ if (IsBootstrapProcessingMode())
+ elog(ERROR, "regconfig values must be OIDs in bootstrap mode");
+
/*
* Normal case: parse the name into components and see if it matches any
* pg_ts_config entries in the current search path.
*
* '-' signifies unknown (OID 0). In all other cases, the input must
* match an existing pg_ts_dict entry.
- *
- * This function is not needed in bootstrap mode, so we don't worry about
- * making it work then.
*/
Datum
regdictionaryin(PG_FUNCTION_ARGS)
PG_RETURN_OID(result);
}
+ /* The rest of this wouldn't work in bootstrap mode */
+ if (IsBootstrapProcessingMode())
+ elog(ERROR, "regdictionary values must be OIDs in bootstrap mode");
+
/*
* Normal case: parse the name into components and see if it matches any
* pg_ts_dict entries in the current search path.
*
* '-' signifies unknown (OID 0). In all other cases, the input must
* match an existing pg_authid entry.
- *
- * This function is not needed in bootstrap mode, so we don't worry about
- * making it work then.
*/
Datum
regrolein(PG_FUNCTION_ARGS)
PG_RETURN_OID(result);
}
+ /* The rest of this wouldn't work in bootstrap mode */
+ if (IsBootstrapProcessingMode())
+ elog(ERROR, "regrole values must be OIDs in bootstrap mode");
+
/* Normal case: see if the name matches any pg_authid entry. */
names = stringToQualifiedNameList(role_name_or_oid);
PG_RETURN_OID(result);
}
+ /* The rest of this wouldn't work in bootstrap mode */
+ if (IsBootstrapProcessingMode())
+ elog(ERROR, "regnamespace values must be OIDs in bootstrap mode");
+
/* Normal case: see if the name matches any pg_namespace entry. */
names = stringToQualifiedNameList(nsp_name_or_oid);