docs: protected void parseColumnsAndConstraints( DdlTokenStream tokens,
                                                 AstNode tableNode ) throws ParsingException {
          assert tokens != null;
          assert tableNode != null;
  
          if (!tokens.matches(L_PAREN)) {
              return;
          }
  
          String tableElementString = getTableElementsString(tokens, false);
  
          DdlTokenStream localTokens = new DdlTokenStream(tableElementString, DdlTokenStream.ddlTokenizer(false), false);
  
          localTokens.start();
  
          StringBuilder unusedTokensSB = new StringBuilder();
          do {
              if (isTableConstraint(localTokens)) {
                  parseTableConstraint(localTokens, tableNode, false);
              } else if (isColumnDefinitionStart(localTokens)) {
                  parseColumnDefinition(localTokens, tableNode, false);
              } else {
                  unusedTokensSB.append(SPACE).append(localTokens.consume());
              }
          } while (localTokens.canConsume(COMMA));
  
          if (unusedTokensSB.length() > 0) {
              String msg = DdlSequencerI18n.unusedTokensParsingColumnsAndConstraints.text(tableNode.getName());
              DdlParserProblem problem = new DdlParserProblem(DdlConstants.Problems.WARNING, Position.EMPTY_CONTENT_POSITION, msg);
              problem.setUnusedSource(unusedTokensSB.toString());
              addProblem(problem, tableNode);
          }
  
      }