aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAlvaro Herrera <alvherre@alvh.no-ip.org>2018-01-12 11:21:42 -0300
committerAlvaro Herrera <alvherre@alvh.no-ip.org>2018-01-12 11:21:42 -0300
commit49c784ece766781250224a371be14af71e7eda93 (patch)
treeebd1bb26ebd20d2024105974b31174e4c413dfe7 /src
parentbdb70c12b3a2e69eec6e51411df60d9f43ecc841 (diff)
downloadpostgresql-49c784ece766781250224a371be14af71e7eda93.tar.gz
postgresql-49c784ece766781250224a371be14af71e7eda93.zip
Remove hard-coded schema knowledge about pg_attribute from genbki.pl
Add the ability to label a column's default value in the catalog header, and implement this for pg_attribute. A new function in Catalog.pm is used to fill in a tuple with defaults. The build process will complain loudly if a catalog entry is incomplete, Commit 8137f2c3232 labeled variable length columns for the C preprocessor. Expose that label to genbki.pl so we can exclude those columns from schema macros in a general fashion. Also, format schema macro entries according to their types. This means slightly less code maintenance, but more importantly it's a proving ground for mechanisms intended to be used in later commits. While at it, I (Álvaro) couldn't resist making some changes in genbki.pl: rename some functions to actually indicate their purpose instead of actively misleading onlookers; and don't iterate on the whole of pg_type to find the entry for each catalog row, using a hash instead of an array. Author: John Naylor, some changes by Álvaro Herrera Discussion: https://postgr.es/m/CAJVSVGVJHwD8sfDfZW9TbCHWKf=C1YDRM-rF=2JenRU_y+VcFg@mail.gmail.com
Diffstat (limited to 'src')
-rw-r--r--src/backend/catalog/Catalog.pm70
-rw-r--r--src/backend/catalog/genbki.pl228
-rw-r--r--src/include/catalog/genbki.h3
-rw-r--r--src/include/catalog/pg_attribute.h22
4 files changed, 187 insertions, 136 deletions
diff --git a/src/backend/catalog/Catalog.pm b/src/backend/catalog/Catalog.pm
index f18b400bd71..9ced1547f6b 100644
--- a/src/backend/catalog/Catalog.pm
+++ b/src/backend/catalog/Catalog.pm
@@ -37,6 +37,8 @@ sub Catalogs
foreach my $input_file (@_)
{
my %catalog;
+ my $is_varlen = 0;
+
$catalog{columns} = [];
$catalog{data} = [];
@@ -164,7 +166,11 @@ sub Catalogs
elsif ($declaring_attributes)
{
next if (/^{|^$/);
- next if (/^#/);
+ if (/^#/)
+ {
+ $is_varlen = 1 if /^#ifdef\s+CATALOG_VARLEN/;
+ next;
+ }
if (/^}/)
{
undef $declaring_attributes;
@@ -172,8 +178,12 @@ sub Catalogs
else
{
my %column;
- my ($atttype, $attname, $attopt) = split /\s+/, $_;
- die "parse error ($input_file)" unless $attname;
+ my @attopts = split /\s+/, $_;
+ my $atttype = shift @attopts;
+ my $attname = shift @attopts;
+ die "parse error ($input_file)"
+ unless ($attname and $atttype);
+
if (exists $RENAME_ATTTYPE{$atttype})
{
$atttype = $RENAME_ATTTYPE{$atttype};
@@ -181,13 +191,14 @@ sub Catalogs
if ($attname =~ /(.*)\[.*\]/) # array attribute
{
$attname = $1;
- $atttype .= '[]'; # variable-length only
+ $atttype .= '[]';
}
$column{type} = $atttype;
$column{name} = $attname;
+ $column{is_varlen} = 1 if $is_varlen;
- if (defined $attopt)
+ foreach my $attopt (@attopts)
{
if ($attopt eq 'BKI_FORCE_NULL')
{
@@ -197,11 +208,20 @@ sub Catalogs
{
$column{forcenotnull} = 1;
}
+ elsif ($attopt =~ /BKI_DEFAULT\((\S+)\)/)
+ {
+ $column{default} = $1;
+ }
else
{
die
"unknown column option $attopt on column $attname";
}
+
+ if ($column{forcenull} and $column{forcenotnull})
+ {
+ die "$attname is forced both null and not null";
+ }
}
push @{ $catalog{columns} }, \%column;
}
@@ -235,6 +255,46 @@ sub SplitDataLine
return @result;
}
+# Fill in default values of a record using the given schema. It's the
+# caller's responsibility to specify other values beforehand.
+sub AddDefaultValues
+{
+ my ($row, $schema) = @_;
+ my @missing_fields;
+ my $msg;
+
+ foreach my $column (@$schema)
+ {
+ my $attname = $column->{name};
+ my $atttype = $column->{type};
+
+ if (defined $row->{$attname})
+ {
+ ;
+ }
+ elsif (defined $column->{default})
+ {
+ $row->{$attname} = $column->{default};
+ }
+ else
+ {
+ # Failed to find a value.
+ push @missing_fields, $attname;
+ }
+ }
+
+ if (@missing_fields)
+ {
+ $msg = "Missing values for: " . join(', ', @missing_fields);
+ $msg .= "\nShowing other values for context:\n";
+ while (my($key, $value) = each %$row)
+ {
+ $msg .= "$key => $value, ";
+ }
+ }
+ return $msg;
+}
+
# 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
diff --git a/src/backend/catalog/genbki.pl b/src/backend/catalog/genbki.pl
index 1d3bbcc532d..ed90a023035 100644
--- a/src/backend/catalog/genbki.pl
+++ b/src/backend/catalog/genbki.pl
@@ -105,7 +105,7 @@ print $bki "# PostgreSQL $major_version\n";
my %schemapg_entries;
my @tables_needing_macros;
my %regprocoids;
-my @types;
+my %types;
# produce output, one catalog at a time
foreach my $catname (@{ $catalogs->{names} })
@@ -119,7 +119,6 @@ foreach my $catname (@{ $catalogs->{names} })
. $catalog->{without_oids}
. $catalog->{rowtype_oid} . "\n";
- my %bki_attr;
my @attnames;
my $first = 1;
@@ -129,7 +128,6 @@ foreach my $catname (@{ $catalogs->{names} })
{
my $attname = $column->{name};
my $atttype = $column->{type};
- $bki_attr{$attname} = $column;
push @attnames, $attname;
if (!$first)
@@ -211,7 +209,7 @@ foreach my $catname (@{ $catalogs->{names} })
{
my %type = %bki_values;
$type{oid} = $row->{oid};
- push @types, \%type;
+ $types{ $type{typname} } = \%type;
}
# Write to postgres.bki
@@ -253,28 +251,24 @@ foreach my $catname (@{ $catalogs->{names} })
# Generate entries for user attributes.
my $attnum = 0;
my $priornotnull = 1;
- my @user_attrs = @{ $table->{columns} };
- foreach my $attr (@user_attrs)
+ foreach my $attr (@{ $table->{columns} })
{
$attnum++;
- my $row = emit_pgattr_row($table_name, $attr, $priornotnull);
- $row->{attnum} = $attnum;
- $row->{attstattarget} = '-1';
- $priornotnull &= ($row->{attnotnull} eq 't');
+ 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.
- if ($table->{bootstrap})
- {
- bki_insert($row, @attnames);
- }
+ print_bki_insert(\%row, @attnames) if $table->{bootstrap};
# Store schemapg entries for later.
- $row =
- emit_schemapg_row($row,
- grep { $bki_attr{$_}{type} eq 'bool' } @attnames);
+ morph_row_for_schemapg(\%row, $schema);
push @{ $schemapg_entries{$table_name} },
sprintf "{ %s }",
- join(', ', grep { defined $_ } @{$row}{@attnames});
+ join(', ', grep { defined $_ } @row{@attnames});
}
# Generate entries for system attributes.
@@ -293,16 +287,18 @@ foreach my $catname (@{ $catalogs->{names} })
foreach my $attr (@SYS_ATTRS)
{
$attnum--;
- my $row = emit_pgattr_row($table_name, $attr, 1);
- $row->{attnum} = $attnum;
- $row->{attstattarget} = '0';
+ my %row;
+ $row{attnum} = $attnum;
+ $row{attrelid} = $table->{relation_oid};
+ $row{attstattarget} = '0';
# Omit the oid column if the catalog doesn't have them
next
if $table->{without_oids}
- && $row->{attname} eq 'oid';
+ && $attr->{name} eq 'oid';
- bki_insert($row, @attnames);
+ morph_row_for_pgattr(\%row, $schema, $attr, 1);
+ print_bki_insert(\%row, @attnames);
}
}
}
@@ -379,130 +375,122 @@ exit 0;
#################### Subroutines ########################
-# Given a system catalog name and a reference to a key-value pair corresponding
-# to the name and type of a column, generate a reference to a hash that
-# represents a pg_attribute entry. We must also be told whether preceding
-# columns were all not-null.
-sub emit_pgattr_row
+# 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 ($table_name, $attr, $priornotnull) = @_;
+ my ($row, $pgattr_schema, $attr, $priornotnull) = @_;
my $attname = $attr->{name};
my $atttype = $attr->{type};
- my %row;
- $row{attrelid} = $catalogs->{$table_name}->{relation_oid};
- $row{attname} = $attname;
+ $row->{attname} = $attname;
- # Adjust type name for arrays: foo[] becomes _foo
- # so we can look it up in pg_type
- if ($atttype =~ /(.+)\[\]$/)
- {
- $atttype = '_' . $1;
- }
+ # Adjust type name for arrays: foo[] becomes _foo, so we can look it up in
+ # pg_type
+ $atttype = '_' . $1 if $atttype =~ /(.+)\[\]$/;
# Copy the type data from pg_type, and add some type-dependent items
- 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};
+ my $type = $types{$atttype};
- # set attndims if it's an array type
- $row{attndims} = $type->{typcategory} eq 'A' ? '1' : '0';
- $row{attcollation} = $type->{typcollation};
+ $row->{atttypid} = $type->{oid};
+ $row->{attlen} = $type->{typlen};
+ $row->{attbyval} = $type->{typbyval};
+ $row->{attstorage} = $type->{typstorage};
+ $row->{attalign} = $type->{typalign};
- if (defined $attr->{forcenotnull})
- {
- $row{attnotnull} = 't';
- }
- elsif (defined $attr->{forcenull})
- {
- $row{attnotnull} = 'f';
- }
- elsif ($priornotnull)
- {
+ # set attndims if it's an array type
+ $row->{attndims} = $type->{typcategory} eq 'A' ? '1' : '0';
+ $row->{attcollation} = $type->{typcollation};
- # 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';
- }
- last;
- }
+ if (defined $attr->{forcenotnull})
+ {
+ $row->{attnotnull} = 't';
+ }
+ elsif (defined $attr->{forcenull})
+ {
+ $row->{attnotnull} = 'f';
}
+ elsif ($priornotnull)
+ {
- # Add in default values for pg_attribute
- my %PGATTR_DEFAULTS = (
- attcacheoff => '-1',
- atttypmod => '-1',
- atthasdef => 'f',
- attidentity => '',
- attisdropped => 'f',
- attislocal => 't',
- attinhcount => '0',
- attacl => '_null_',
- attoptions => '_null_',
- attfdwoptions => '_null_');
- return { %PGATTR_DEFAULTS, %row };
+ # 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';
+ }
+
+ my $error = Catalog::AddDefaultValues($row, $pgattr_schema);
+ if ($error)
+ {
+ die "Failed to form full tuple for pg_attribute: ", $error;
+ }
}
# Write a pg_attribute entry to postgres.bki
-sub bki_insert
+sub print_bki_insert
{
my $row = shift;
my @attnames = @_;
my $oid = $row->{oid} ? "OID = $row->{oid} " : '';
- my $bki_values = join ' ', map { $_ eq '' ? '""' : $_ } map $row->{$_},
- @attnames;
+ my $bki_values = join ' ', @{$row}{@attnames};
printf $bki "insert %s( %s )\n", $oid, $bki_values;
}
+# 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 emit_schemapg_row
+sub morph_row_for_schemapg
{
- my $row = shift;
- my @bool_attrs = @_;
-
- # Replace empty string by zero char constant
- $row->{attidentity} ||= '\0';
-
- # Supply appropriate quoting for these fields.
- $row->{attname} = q|{"| . $row->{attname} . q|"}|;
- $row->{attstorage} = q|'| . $row->{attstorage} . q|'|;
- $row->{attalign} = q|'| . $row->{attalign} . q|'|;
- $row->{attidentity} = q|'| . $row->{attidentity} . q|'|;
-
- # 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->{attacl};
- delete $row->{attoptions};
- delete $row->{attfdwoptions};
-
- # Expand booleans from 'f'/'t' to 'false'/'true'.
- # Some values might be other macros (eg FLOAT4PASSBYVAL), don't change.
- foreach my $attr (@bool_attrs)
+ my $row = shift;
+ my $pgattr_schema = shift;
+
+ foreach my $column (@$pgattr_schema)
{
- $row->{$attr} =
- $row->{$attr} eq 't' ? 'true'
- : $row->{$attr} eq 'f' ? 'false'
- : $row->{$attr};
+ 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')
+ {
+ # Replace empty string by zero char constant; add single quotes
+ $row->{$attname} = '\0' if $row->{$attname} eq q|""|;
+ $row->{$attname} = sprintf("'%s'", $row->{$attname});
+ }
+
+ # Expand booleans from 'f'/'t' to 'false'/'true'.
+ # Some values might be other macros (eg FLOAT4PASSBYVAL),
+ # 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 $row;
}
sub usage
diff --git a/src/include/catalog/genbki.h b/src/include/catalog/genbki.h
index 59b0f8ed5d6..96ac4025de2 100644
--- a/src/include/catalog/genbki.h
+++ b/src/include/catalog/genbki.h
@@ -31,6 +31,9 @@
#define BKI_FORCE_NULL
#define BKI_FORCE_NOT_NULL
+/* Specifies a default value for a catalog field */
+#define BKI_DEFAULT(value)
+
/*
* This is never defined; it's here only for documentation.
*
diff --git a/src/include/catalog/pg_attribute.h b/src/include/catalog/pg_attribute.h
index 6104254d7be..8159383834d 100644
--- a/src/include/catalog/pg_attribute.h
+++ b/src/include/catalog/pg_attribute.h
@@ -54,7 +54,7 @@ CATALOG(pg_attribute,1249) BKI_BOOTSTRAP BKI_WITHOUT_OIDS BKI_ROWTYPE_OID(75) BK
* that no value has been explicitly set for this column, so ANALYZE
* should use the default setting.
*/
- int32 attstattarget;
+ int32 attstattarget BKI_DEFAULT(-1);
/*
* attlen is a copy of the typlen field from pg_type for this attribute.
@@ -90,7 +90,7 @@ CATALOG(pg_attribute,1249) BKI_BOOTSTRAP BKI_WITHOUT_OIDS BKI_ROWTYPE_OID(75) BK
* descriptor, we may then update attcacheoff in the copies. This speeds
* up the attribute walking process.
*/
- int32 attcacheoff;
+ int32 attcacheoff BKI_DEFAULT(-1);
/*
* atttypmod records type-specific data supplied at table creation time
@@ -98,7 +98,7 @@ CATALOG(pg_attribute,1249) BKI_BOOTSTRAP BKI_WITHOUT_OIDS BKI_ROWTYPE_OID(75) BK
* type-specific input and output functions as the third argument. The
* value will generally be -1 for types that do not need typmod.
*/
- int32 atttypmod;
+ int32 atttypmod BKI_DEFAULT(-1);
/*
* attbyval is a copy of the typbyval field from pg_type for this
@@ -131,13 +131,13 @@ CATALOG(pg_attribute,1249) BKI_BOOTSTRAP BKI_WITHOUT_OIDS BKI_ROWTYPE_OID(75) BK
bool attnotnull;
/* Has DEFAULT value or not */
- bool atthasdef;
+ bool atthasdef BKI_DEFAULT(f);
/* One of the ATTRIBUTE_IDENTITY_* constants below, or '\0' */
- char attidentity;
+ char attidentity BKI_DEFAULT("");
/* Is dropped (ie, logically invisible) or not */
- bool attisdropped;
+ bool attisdropped BKI_DEFAULT(f);
/*
* This flag specifies whether this column has ever had a local
@@ -148,10 +148,10 @@ CATALOG(pg_attribute,1249) BKI_BOOTSTRAP BKI_WITHOUT_OIDS BKI_ROWTYPE_OID(75) BK
* not dropped by a parent's DROP COLUMN even if this causes the column's
* attinhcount to become zero.
*/
- bool attislocal;
+ bool attislocal BKI_DEFAULT(t);
/* Number of times inherited from direct parent relation(s) */
- int32 attinhcount;
+ int32 attinhcount BKI_DEFAULT(0);
/* attribute's collation */
Oid attcollation;
@@ -160,13 +160,13 @@ CATALOG(pg_attribute,1249) BKI_BOOTSTRAP BKI_WITHOUT_OIDS BKI_ROWTYPE_OID(75) BK
/* NOTE: The following fields are not present in tuple descriptors. */
/* Column-level access permissions */
- aclitem attacl[1];
+ aclitem attacl[1] BKI_DEFAULT(_null_);
/* Column-level options */
- text attoptions[1];
+ text attoptions[1] BKI_DEFAULT(_null_);
/* Column-level FDW options */
- text attfdwoptions[1];
+ text attfdwoptions[1] BKI_DEFAULT(_null_);
#endif
} FormData_pg_attribute;