docs: protected void parseTableConstraint( DdlTokenStream tokens,
                                           AstNode tableNode,
                                           boolean isAlterTable ) throws ParsingException {
          assert tokens != null;
          assert tableNode != null;
  
          String mixinType = isAlterTable ? TYPE_ADD_TABLE_CONSTRAINT_DEFINITION : TYPE_TABLE_CONSTRAINT;
  
          /*
          <table constraint definition> ::=
              [ <constraint name definition> ]
              <table constraint> [ <constraint attributes> ]
          
          <table constraint> ::=
                <unique constraint definition>
              | <referential constraint definition>
              | <check constraint definition>
              
          <constraint attributes> ::=
                <constraint check time> [ [ NOT ] DEFERRABLE ]
              | [ NOT ] DEFERRABLE [ <constraint check time> ]
          
          <unique constraint definition> ::=
                      <unique specification> even in SQL3)
              <unique specification>
                <left paren> <unique column list> <right paren>
          
          <unique column list> ::= <column name list>
          
          <referential constraint definition> ::=
              FOREIGN KEY
                  <left paren> <referencing columns> <right paren>
                <references specification>
          
          <referencing columns> ::=
              <reference column list>
              
          <constraint attributes> ::=
                <constraint check time> [ [ NOT ] DEFERRABLE ]
              | [ NOT ] DEFERRABLE [ <constraint check time> ]
          
          <constraint check time> ::=
                INITIALLY DEFERRED
              | INITIALLY IMMEDIATE
              
          <check constraint definition> ::=
              CHECK
                  <left paren> <search condition> <right paren>
           */
          consumeComment(tokens);
  
          if ((tokens.matches("PRIMARY", "KEY")) || (tokens.matches("FOREIGN", "KEY")) || (tokens.matches("UNIQUE"))) {
  
              // This is the case where the PK/FK/UK is NOT NAMED
              if (tokens.matches("UNIQUE")) {
                  String uc_name = "UC_1"; // UNIQUE CONSTRAINT NAME
                  tokens.consume(); // UNIQUE
  
                  AstNode constraintNode = nodeFactory().node(uc_name, tableNode, mixinType);
                  constraintNode.setProperty(CONSTRAINT_TYPE, UNIQUE);
  
                  // CONSUME COLUMNS
                  parseColumnNameList(tokens, constraintNode, TYPE_COLUMN_REFERENCE);
  
                  parseConstraintAttributes(tokens, constraintNode);
  
                  consumeComment(tokens);
              } else if (tokens.matches("PRIMARY", "KEY")) {
                  String pk_name = "PK_1"; // PRIMARY KEY NAME
                  tokens.consume("PRIMARY", "KEY"); // PRIMARY KEY
  
                  AstNode constraintNode = nodeFactory().node(pk_name, tableNode, mixinType);
                  constraintNode.setProperty(CONSTRAINT_TYPE, PRIMARY_KEY);
  
                  // CONSUME COLUMNS
                  parseColumnNameList(tokens, constraintNode, TYPE_COLUMN_REFERENCE);
  
                  parseConstraintAttributes(tokens, constraintNode);
  
                  consumeComment(tokens);
              } else if (tokens.matches("FOREIGN", "KEY")) {
                  String fk_name = "FK_1"; // FOREIGN KEY NAME
                  tokens.consume("FOREIGN", "KEY"); // FOREIGN KEY
  
                  if (!tokens.matches(L_PAREN)) {
                      // Assume the FK is Named here
                      fk_name = tokens.consume();
                  }
  
                  AstNode constraintNode = nodeFactory().node(fk_name, tableNode, mixinType);
                  constraintNode.setProperty(CONSTRAINT_TYPE, FOREIGN_KEY);
  
                  // CONSUME COLUMNS
                  parseColumnNameList(tokens, constraintNode, TYPE_COLUMN_REFERENCE);
  
                  // Parse the references to table and columns
                  parseReferences(tokens, constraintNode);
  
                  parseConstraintAttributes(tokens, constraintNode);
  
                  consumeComment(tokens);
              }
          } else if (tokens.matches(CONSTRAINT, TokenStream.ANY_VALUE, "UNIQUE")) {
              // CONSTRAINT P_KEY_2a UNIQUE (PERMISSIONUID)
              tokens.consume(); // CONSTRAINT
              String uc_name = parseName(tokens); // UNIQUE CONSTRAINT NAME
              tokens.consume("UNIQUE"); // UNIQUE
  
              AstNode constraintNode = nodeFactory().node(uc_name, tableNode, mixinType);
              constraintNode.setProperty(CONSTRAINT_TYPE, UNIQUE);
  
              // CONSUME COLUMNS
              parseColumnNameList(tokens, constraintNode, TYPE_COLUMN_REFERENCE);
  
              parseConstraintAttributes(tokens, constraintNode);
  
              consumeComment(tokens);
          } else if (tokens.matches(CONSTRAINT, TokenStream.ANY_VALUE, "PRIMARY", "KEY")) {
              // CONSTRAINT U_KEY_2a PRIMARY KEY (PERMISSIONUID)
              tokens.consume(CONSTRAINT); // CONSTRAINT
              String pk_name = parseName(tokens); // PRIMARY KEY NAME
              tokens.consume("PRIMARY", "KEY"); // PRIMARY KEY
  
              AstNode constraintNode = nodeFactory().node(pk_name, tableNode, mixinType);
              constraintNode.setProperty(CONSTRAINT_TYPE, PRIMARY_KEY);
  
              // CONSUME COLUMNS
              parseColumnNameList(tokens, constraintNode, TYPE_COLUMN_REFERENCE);
  
              parseConstraintAttributes(tokens, constraintNode);
  
              consumeComment(tokens);
  
          } else if (tokens.matches(CONSTRAINT, TokenStream.ANY_VALUE, "FOREIGN", "KEY")) {
              // CONSTRAINT F_KEY_2a FOREIGN KEY (PERMISSIONUID)
              tokens.consume(CONSTRAINT); // CONSTRAINT
              String fk_name = parseName(tokens); // FOREIGN KEY NAME
              tokens.consume("FOREIGN", "KEY"); // FOREIGN KEY
  
              AstNode constraintNode = nodeFactory().node(fk_name, tableNode, mixinType);
  
              constraintNode.setProperty(CONSTRAINT_TYPE, FOREIGN_KEY);
  
              // CONSUME COLUMNS
              parseColumnNameList(tokens, constraintNode, TYPE_COLUMN_REFERENCE);
  
              // Parse the references to table and columns
              parseReferences(tokens, constraintNode);
  
              parseConstraintAttributes(tokens, constraintNode);
  
              consumeComment(tokens);
  
          } else if (tokens.matches(CONSTRAINT, TokenStream.ANY_VALUE, CHECK)) {
              // CONSTRAINT zipchk CHECK (char_length(zipcode) = 5);
              tokens.consume(CONSTRAINT); // CONSTRAINT
              String ck_name = parseName(tokens); // NAME
              parseCheckConstraint(tokens, tableNode, mixinType, ck_name);
          }  else if (tokens.matches(CHECK)) {
               parseCheckConstraint(tokens, tableNode, mixinType, UNNAMED_CHECK_NODE_NAME);
          }
      }