Allow forcing nullness of columns during bootstrap.
authorAndres Freund <andres@anarazel.de>
Sat, 21 Feb 2015 21:25:49 +0000 (22:25 +0100)
committerAndres Freund <andres@anarazel.de>
Sat, 21 Feb 2015 21:31:54 +0000 (22:31 +0100)
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
src/backend/bootstrap/bootparse.y
src/backend/bootstrap/bootscanner.l
src/backend/bootstrap/bootstrap.c
src/backend/catalog/Catalog.pm
src/backend/catalog/genbki.pl
src/backend/utils/Gen_fmgrtab.pl
src/include/bootstrap/bootstrap.h
src/include/catalog/genbki.h

index aaf500ad082f8bd41c65560a0af62552d88c6cc4..af6d8d1d2a9c0cb41aa4d1303ba379a0904ded7a 100644 (file)
      <optional><literal>without_oids</></optional>
      <optional><literal>rowtype_oid</> <replaceable>oid</></optional>
      (<replaceable class="parameter">name1</replaceable> =
-     <replaceable class="parameter">type1</replaceable> <optional>,
-     <replaceable class="parameter">name2</replaceable> = <replaceable
-     class="parameter">type2</replaceable>, ...</optional>)
+     <replaceable class="parameter">type1</replaceable>
+     <optional>FORCE NOT NULL | FORCE NULL </optional> <optional>,
+     <replaceable class="parameter">name2</replaceable> =
+     <replaceable class="parameter">type2</replaceable>
+     <optional>FORCE NOT NULL | FORCE NULL </optional>,
+     ...</optional>)
     </term>
 
     <listitem>
index 56fa1aaa5db64736a70cd22b60f64e53adecd1d5..9edd1a0aff908c1bbff3d3912e0d777015f0452e 100644 (file)
@@ -107,7 +107,7 @@ static int num_columns_read = 0;
 %type <list>  boot_index_params
 %type <ielem> boot_index_param
 %type <str>   boot_const boot_ident
-%type <ival>  optbootstrap optsharedrelation optwithoutoids
+%type <ival>  optbootstrap optsharedrelation optwithoutoids boot_column_nullness
 %type <oidval> oidspec optoideq optrowtypeoid
 
 %token <str> 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); }
        ;
index fa4e2ff108955545034d11ea1f4907a93f319e77..72714f474bf3f7a48c9f71fadacb3541dee9cb04 100644 (file)
@@ -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);
index bc66eac9848a30d7dd01e102dc8c97c2cee2bf96..ad49964732f8a901a60f9743d2e431a1e7124323 100644 (file)
@@ -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)
+   {
+       attrtypes[attnum]->attnotnull = true;
+   }
+   else if (nullness == BOOTCOL_NULL_FORCE_NULL)
    {
-       int         i;
+       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;
    }
 }
 
index c773eca80925e137f081f82da60ce57d6f0e2e3a..c7b1c1785e456590b15e96f26e0c30151debe198 100644 (file)
@@ -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;
                }
            }
        }
index e1c7fe5bf015795c00e93771faa9761976af87de..a5c78eed4931f68ea52112683b863faa0760b227 100644 (file)
@@ -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'
index 8b7186419e3d6adff45c47f0e9188b64e70cd9a1..f5cc2655f3559dd0a9ca95172ae3d87817c7c5c3 100644 (file)
@@ -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};
index be4430adffdb15037abaf350d7efb0dd8a4c0f12..f9cbc137e72215e50a9e3e1e66e23a96f19ae6ab 100644 (file)
  */
 #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);
index 5d6039db83570ab1cbe592ac8c8adcdddca53efd..cebf51d509d8925c48dd90e1f35633d1fe9fc67c 100644 (file)
@@ -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.