From eb68379c38202180bc8e33fb9987284e314b7fc8 Mon Sep 17 00:00:00 2001 From: Andres Freund Date: Sat, 21 Feb 2015 22:25:49 +0100 Subject: [PATCH] Allow forcing nullness of columns during bootstrap. Bootstrap determines whether a column is null based on simple builtin rules. Those work surprisingly well, but nonetheless a few existing columns aren't set correctly. Additionally there is at least one patch sent to hackers where forcing the nullness of a column would be helpful. The boostrap format has gained FORCE [NOT] NULL for this, which will be emitted by genbki.pl when BKI_FORCE_(NOT_)?NULL is specified for a column in a catalog header. This patch doesn't change the marking of any existing columns. Discussion: 20150215170014.GE15326@awork2.anarazel.de --- doc/src/sgml/bki.sgml | 9 ++-- src/backend/bootstrap/bootparse.y | 13 ++++-- src/backend/bootstrap/bootscanner.l | 3 ++ src/backend/bootstrap/bootstrap.c | 56 +++++++++++++++---------- src/backend/catalog/Catalog.pm | 24 ++++++++++- src/backend/catalog/genbki.pl | 65 +++++++++++++++++++++-------- src/backend/utils/Gen_fmgrtab.pl | 2 +- src/include/bootstrap/bootstrap.h | 6 ++- src/include/catalog/genbki.h | 2 + 9 files changed, 131 insertions(+), 49 deletions(-) diff --git a/doc/src/sgml/bki.sgml b/doc/src/sgml/bki.sgml index aaf500ad08..af6d8d1d2a 100644 --- a/doc/src/sgml/bki.sgml +++ b/doc/src/sgml/bki.sgml @@ -75,9 +75,12 @@ without_oids rowtype_oid oid (name1 = - type1 , - name2 = type2, ...) + type1 + FORCE NOT NULL | FORCE NULL , + name2 = + type2 + FORCE NOT NULL | FORCE NULL , + ...) diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y index 56fa1aaa5d..9edd1a0aff 100644 --- a/src/backend/bootstrap/bootparse.y +++ b/src/backend/bootstrap/bootparse.y @@ -107,7 +107,7 @@ static int num_columns_read = 0; %type boot_index_params %type boot_index_param %type boot_const boot_ident -%type optbootstrap optsharedrelation optwithoutoids +%type optbootstrap optsharedrelation optwithoutoids boot_column_nullness %type oidspec optoideq optrowtypeoid %token CONST_P ID @@ -115,6 +115,7 @@ static int num_columns_read = 0; %token XDECLARE INDEX ON USING XBUILD INDICES UNIQUE XTOAST %token COMMA EQUALS LPAREN RPAREN %token OBJ_ID XBOOTSTRAP XSHARED_RELATION XWITHOUT_OIDS XROWTYPE_OID NULLVAL +%token XFORCE XNOT XNULL %start TopLevel @@ -427,14 +428,20 @@ boot_column_list: ; boot_column_def: - boot_ident EQUALS boot_ident + boot_ident EQUALS boot_ident boot_column_nullness { if (++numattr > MAXATTR) elog(FATAL, "too many columns"); - DefineAttr($1, $3, numattr-1); + DefineAttr($1, $3, numattr-1, $4); } ; +boot_column_nullness: + XFORCE XNOT XNULL { $$ = BOOTCOL_NULL_FORCE_NOT_NULL; } + | XFORCE XNULL { $$ = BOOTCOL_NULL_FORCE_NULL; } + | { $$ = BOOTCOL_NULL_AUTO; } + ; + oidspec: boot_ident { $$ = atooid($1); } ; diff --git a/src/backend/bootstrap/bootscanner.l b/src/backend/bootstrap/bootscanner.l index fa4e2ff108..72714f474b 100644 --- a/src/backend/bootstrap/bootscanner.l +++ b/src/backend/bootstrap/bootscanner.l @@ -109,6 +109,9 @@ insert { return(INSERT_TUPLE); } "on" { return(ON); } "using" { return(USING); } "toast" { return(XTOAST); } +"FORCE" { return(XFORCE); } +"NOT" { return(XNOT); } +"NULL" { return(XNULL); } {arrayid} { yylval.str = MapArrayTypeName(yytext); diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c index bc66eac984..ad49964732 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -642,7 +642,7 @@ closerel(char *name) * ---------------- */ void -DefineAttr(char *name, char *type, int attnum) +DefineAttr(char *name, char *type, int attnum, int nullness) { Oid typeoid; @@ -697,30 +697,44 @@ DefineAttr(char *name, char *type, int attnum) attrtypes[attnum]->atttypmod = -1; attrtypes[attnum]->attislocal = true; - /* - * Mark as "not null" if type is fixed-width and prior columns are too. - * This corresponds to case where column can be accessed directly via C - * struct declaration. - * - * oidvector and int2vector are also treated as not-nullable, even though - * they are no longer fixed-width. - */ -#define MARKNOTNULL(att) \ - ((att)->attlen > 0 || \ - (att)->atttypid == OIDVECTOROID || \ - (att)->atttypid == INT2VECTOROID) - - if (MARKNOTNULL(attrtypes[attnum])) + if (nullness == BOOTCOL_NULL_FORCE_NOT_NULL) { - int i; + attrtypes[attnum]->attnotnull = true; + } + else if (nullness == BOOTCOL_NULL_FORCE_NULL) + { + attrtypes[attnum]->attnotnull = false; + } + else + { + Assert(nullness == BOOTCOL_NULL_AUTO); - for (i = 0; i < attnum; i++) + /* + * Mark as "not null" if type is fixed-width and prior columns are + * too. This corresponds to case where column can be accessed + * directly via C struct declaration. + * + * oidvector and int2vector are also treated as not-nullable, even + * though they are no longer fixed-width. + */ +#define MARKNOTNULL(att) \ + ((att)->attlen > 0 || \ + (att)->atttypid == OIDVECTOROID || \ + (att)->atttypid == INT2VECTOROID) + + if (MARKNOTNULL(attrtypes[attnum])) { - if (!MARKNOTNULL(attrtypes[i])) - break; + int i; + + /* check earlier attributes */ + for (i = 0; i < attnum; i++) + { + if (!attrtypes[i]->attnotnull) + break; + } + if (i == attnum) + attrtypes[attnum]->attnotnull = true; } - if (i == attnum) - attrtypes[attnum]->attnotnull = true; } } diff --git a/src/backend/catalog/Catalog.pm b/src/backend/catalog/Catalog.pm index c773eca809..c7b1c1785e 100644 --- a/src/backend/catalog/Catalog.pm +++ b/src/backend/catalog/Catalog.pm @@ -161,7 +161,8 @@ sub Catalogs } else { - my ($atttype, $attname) = split /\s+/, $_; + my %row; + my ($atttype, $attname, $attopt) = split /\s+/, $_; die "parse error ($input_file)" unless $attname; if (exists $RENAME_ATTTYPE{$atttype}) { @@ -172,7 +173,26 @@ sub Catalogs $attname = $1; $atttype .= '[]'; # variable-length only } - push @{ $catalog{columns} }, { $attname => $atttype }; + + $row{'type'} = $atttype; + $row{'name'} = $attname; + + if (defined $attopt) + { + if ($attopt eq 'PG_FORCE_NULL') + { + $row{'forcenull'} = 1; + } + elsif ($attopt eq 'BKI_FORCE_NOT_NULL') + { + $row{'forcenotnull'} = 1; + } + else + { + die "unknown column option $attopt on column $attname" + } + } + push @{ $catalog{columns} }, \%row; } } } diff --git a/src/backend/catalog/genbki.pl b/src/backend/catalog/genbki.pl index e1c7fe5bf0..a5c78eed49 100644 --- a/src/backend/catalog/genbki.pl +++ b/src/backend/catalog/genbki.pl @@ -118,17 +118,36 @@ foreach my $catname (@{ $catalogs->{names} }) my %bki_attr; my @attnames; + my $first = 1; + + print BKI " (\n"; foreach my $column (@{ $catalog->{columns} }) { - my ($attname, $atttype) = %$column; - $bki_attr{$attname} = $atttype; + my $attname = $column->{name}; + my $atttype = $column->{type}; + $bki_attr{$attname} = $column; push @attnames, $attname; + + 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"; + } } - print BKI " (\n"; - print BKI join " ,\n", map(" $_ = $bki_attr{$_}", @attnames); print BKI "\n )\n"; - # open it, unless bootstrap case (create bootstrap does this automatically) + # open it, unless bootstrap case (create bootstrap does this automatically) if ($catalog->{bootstrap} eq '') { print BKI "open $catname\n"; @@ -210,7 +229,7 @@ foreach my $catname (@{ $catalogs->{names} }) # Store schemapg entries for later. $row = emit_schemapg_row($row, - grep { $bki_attr{$_} eq 'bool' } @attnames); + grep { $bki_attr{$_}{type} eq 'bool' } @attnames); push @{ $schemapg_entries{$table_name} }, '{ ' . join( ', ', grep { defined $_ } @@ -223,13 +242,13 @@ foreach my $catname (@{ $catalogs->{names} }) { $attnum = 0; my @SYS_ATTRS = ( - { ctid => 'tid' }, - { oid => 'oid' }, - { xmin => 'xid' }, - { cmin => 'cid' }, - { xmax => 'xid' }, - { cmax => 'cid' }, - { tableoid => 'oid' }); + { name => 'ctid', type => 'tid' }, + { name => 'oid', type => 'oid' }, + { 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--; @@ -326,7 +345,8 @@ exit 0; sub emit_pgattr_row { my ($table_name, $attr, $priornotnull) = @_; - my ($attname, $atttype) = %$attr; + my $attname = $attr->{name}; + my $atttype = $attr->{type}; my %row; $row{attrelid} = $catalogs->{$table_name}->{relation_oid}; @@ -354,11 +374,20 @@ sub emit_pgattr_row $row{attndims} = $type->{typcategory} eq 'A' ? '1' : '0'; $row{attcollation} = $type->{typcollation}; - # attnotnull must be set true if the type is fixed-width and - # prior columns are too --- compare DefineAttr in bootstrap.c. - # oidvector and int2vector are also treated as not-nullable. - if ($priornotnull) + 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' diff --git a/src/backend/utils/Gen_fmgrtab.pl b/src/backend/utils/Gen_fmgrtab.pl index 8b7186419e..f5cc2655f3 100644 --- a/src/backend/utils/Gen_fmgrtab.pl +++ b/src/backend/utils/Gen_fmgrtab.pl @@ -52,7 +52,7 @@ my @fmgr = (); my @attnames; foreach my $column (@{ $catalogs->{pg_proc}->{columns} }) { - push @attnames, keys %$column; + push @attnames, $column->{name}; } my $data = $catalogs->{pg_proc}->{data}; diff --git a/src/include/bootstrap/bootstrap.h b/src/include/bootstrap/bootstrap.h index be4430adff..f9cbc137e7 100644 --- a/src/include/bootstrap/bootstrap.h +++ b/src/include/bootstrap/bootstrap.h @@ -23,6 +23,10 @@ */ #define MAXATTR 40 +#define BOOTCOL_NULL_AUTO 1 +#define BOOTCOL_NULL_FORCE_NULL 2 +#define BOOTCOL_NULL_FORCE_NOT_NULL 3 + extern Relation boot_reldesc; extern Form_pg_attribute attrtypes[MAXATTR]; extern int numattr; @@ -35,7 +39,7 @@ extern void err_out(void); extern void closerel(char *name); extern void boot_openrel(char *name); -extern void DefineAttr(char *name, char *type, int attnum); +extern void DefineAttr(char *name, char *type, int attnum, int nullness); extern void InsertOneTuple(Oid objectid); extern void InsertOneValue(char *value, int i); extern void InsertOneNull(int i); diff --git a/src/include/catalog/genbki.h b/src/include/catalog/genbki.h index 5d6039db83..cebf51d509 100644 --- a/src/include/catalog/genbki.h +++ b/src/include/catalog/genbki.h @@ -28,6 +28,8 @@ #define BKI_WITHOUT_OIDS #define BKI_ROWTYPE_OID(oid) #define BKI_SCHEMA_MACRO +#define BKI_FORCE_NULL +#define BKI_FORCE_NOT_NULL /* * This is never defined; it's here only for documentation.