package com.chenyang.druid.sql.dialect.greenplum.parser;

import com.chenyang.druid.sql.ast.SQLCommentHint;
import com.chenyang.druid.sql.ast.SQLExpr;
import com.chenyang.druid.sql.ast.SQLLimit;
import com.chenyang.druid.sql.ast.SQLName;
import com.chenyang.druid.sql.ast.SQLObject;
import com.chenyang.druid.sql.ast.SQLOrderBy;
import com.chenyang.druid.sql.ast.SQLOver;
import com.chenyang.druid.sql.ast.SQLParameter;
import com.chenyang.druid.sql.ast.SQLWindow;
import com.chenyang.druid.sql.ast.expr.SQLAllColumnExpr;
import com.chenyang.druid.sql.ast.expr.SQLBinaryOpExpr;
import com.chenyang.druid.sql.ast.expr.SQLIdentifierExpr;
import com.chenyang.druid.sql.ast.expr.SQLListExpr;
import com.chenyang.druid.sql.ast.expr.SQLMethodInvokeExpr;
import com.chenyang.druid.sql.ast.statement.SQLExprTableSource;
import com.chenyang.druid.sql.ast.statement.SQLSelect;
import com.chenyang.druid.sql.ast.statement.SQLSelectItem;
import com.chenyang.druid.sql.ast.statement.SQLSelectQuery;
import com.chenyang.druid.sql.ast.statement.SQLSelectQueryBlock;
import com.chenyang.druid.sql.ast.statement.SQLSubqueryTableSource;
import com.chenyang.druid.sql.ast.statement.SQLTableSource;
import com.chenyang.druid.sql.ast.statement.SQLUnionOperator;
import com.chenyang.druid.sql.ast.statement.SQLUnionQuery;
import com.chenyang.druid.sql.ast.statement.SQLUnionQueryTableSource;
import com.chenyang.druid.sql.ast.statement.SQLValuesTableSource;
import com.chenyang.druid.sql.dialect.greenplum.ast.expr.GPColumnSimpleDefinitionExpr;
import com.chenyang.druid.sql.dialect.greenplum.ast.expr.tablesource.GPExprTableSource;
import com.chenyang.druid.sql.dialect.greenplum.ast.expr.tablesource.GPFunctionTableSource;
import com.chenyang.druid.sql.dialect.greenplum.ast.expr.tablesource.GPMethodInvokeTableSource;
import com.chenyang.druid.sql.dialect.greenplum.ast.expr.tablesource.GPRowsFromTableSource;
import com.chenyang.druid.sql.dialect.greenplum.ast.expr.tablesource.GPSubqueryTableSource;
import com.chenyang.druid.sql.dialect.greenplum.ast.expr.tablesource.GPTableSource;
import com.chenyang.druid.sql.dialect.greenplum.ast.expr.tablesource.GPValuesTableSource;
import com.chenyang.druid.sql.dialect.greenplum.ast.stmt.GPSelectQueryBlock;
import com.chenyang.druid.sql.parser.Lexer;
import com.chenyang.druid.sql.parser.ParserException;
import com.chenyang.druid.sql.parser.SQLExprParser;
import com.chenyang.druid.sql.parser.SQLParserFeature;
import com.chenyang.druid.sql.parser.SQLSelectListCache;
import com.chenyang.druid.sql.parser.SQLSelectParser;
import com.chenyang.druid.sql.parser.Token;
import com.chenyang.druid.util.FnvHash;
import java.util.List;

public class GPSelectParser extends SQLSelectParser {
   public GPSelectParser(SQLExprParser exprParser) {
      super(exprParser);
   }

   public GPSelectParser(SQLExprParser exprParser, SQLSelectListCache selectListCache) {
      super(exprParser, selectListCache);
   }

   public GPSelectParser(String sql) {
      this((SQLExprParser)(new GPExprParser(sql)));
   }

   protected SQLExprParser createExprParser() {
      return new GPExprParser(this.lexer);
   }

   public SQLSelectQuery queryRest(SQLSelectQuery selectQuery, boolean acceptUnion) {
      if (!(selectQuery instanceof GPSelectQueryBlock)) {
         new GPSelectQueryBlock();
      }

      if (!acceptUnion) {
         return selectQuery;
      } else if (this.lexer.token() != Token.UNION) {
         if (this.lexer.token() == Token.EXCEPT) {
            this.lexer.nextToken();
            SQLUnionQuery union = new SQLUnionQuery();
            union.setLeft(selectQuery);
            if (this.lexer.token() == Token.ALL) {
               this.lexer.nextToken();
               union.setOperator(SQLUnionOperator.EXCEPT_ALL);
            } else if (this.lexer.token() == Token.DISTINCT) {
               this.lexer.nextToken();
               union.setOperator(SQLUnionOperator.EXCEPT_DISTINCT);
            } else {
               union.setOperator(SQLUnionOperator.EXCEPT);
            }

            boolean paren = this.lexer.token() == Token.LPAREN;
            SQLSelectQuery right = this.query(union, false);
            union.setRight(right);
            if (!paren) {
               if (right instanceof SQLSelectQueryBlock) {
                  SQLSelectQueryBlock rightQuery = (SQLSelectQueryBlock)right;
                  SQLOrderBy orderBy = rightQuery.getOrderBy();
                  if (orderBy != null) {
                     union.setOrderBy(orderBy);
                     rightQuery.setOrderBy((SQLOrderBy)null);
                  }

                  SQLLimit limit = rightQuery.getLimit();
                  if (limit != null) {
                     union.setLimit(limit);
                     rightQuery.setLimit((SQLLimit)null);
                  }
               } else if (right instanceof SQLUnionQuery) {
                  SQLUnionQuery rightUnion = (SQLUnionQuery)right;
                  SQLOrderBy orderBy = rightUnion.getOrderBy();
                  if (orderBy != null) {
                     union.setOrderBy(orderBy);
                     rightUnion.setOrderBy((SQLOrderBy)null);
                  }

                  SQLLimit limit = rightUnion.getLimit();
                  if (limit != null) {
                     union.setLimit(limit);
                     rightUnion.setLimit((SQLLimit)null);
                  }
               }
            }

            return this.queryRest(union, true);
         } else if (this.lexer.token() == Token.INTERSECT) {
            this.lexer.nextToken();
            SQLUnionQuery union = new SQLUnionQuery();
            union.setLeft(selectQuery);
            if (this.lexer.token() == Token.DISTINCT) {
               this.lexer.nextToken();
               union.setOperator(SQLUnionOperator.INTERSECT_DISTINCT);
            } else if (this.lexer.token() == Token.ALL) {
               this.lexer.nextToken();
               union.setOperator(SQLUnionOperator.INTERSECT_ALL);
            } else {
               union.setOperator(SQLUnionOperator.INTERSECT);
            }

            boolean paren = this.lexer.token() == Token.LPAREN;
            SQLSelectQuery right = this.query(union, false);
            union.setRight(right);
            if (!paren) {
               if (right instanceof SQLSelectQueryBlock) {
                  SQLSelectQueryBlock rightQuery = (SQLSelectQueryBlock)right;
                  SQLOrderBy orderBy = rightQuery.getOrderBy();
                  if (orderBy != null) {
                     union.setOrderBy(orderBy);
                     rightQuery.setOrderBy((SQLOrderBy)null);
                  }

                  SQLLimit limit = rightQuery.getLimit();
                  if (limit != null) {
                     union.setLimit(limit);
                     rightQuery.setLimit((SQLLimit)null);
                  }
               } else if (right instanceof SQLUnionQuery) {
                  SQLUnionQuery rightUnion = (SQLUnionQuery)right;
                  SQLOrderBy orderBy = rightUnion.getOrderBy();
                  if (orderBy != null) {
                     union.setOrderBy(orderBy);
                     rightUnion.setOrderBy((SQLOrderBy)null);
                  }

                  SQLLimit limit = rightUnion.getLimit();
                  if (limit != null) {
                     union.setLimit(limit);
                     rightUnion.setLimit((SQLLimit)null);
                  }
               }
            }

            return this.queryRest(union, true);
         } else if (acceptUnion && this.lexer.token() == Token.MINUS) {
            this.lexer.nextToken();
            SQLUnionQuery union = new SQLUnionQuery();
            union.setLeft(selectQuery);
            if (this.lexer.token() == Token.ALL) {
               this.lexer.nextToken();
               union.setOperator(SQLUnionOperator.MINUS_ALL);
            } else if (this.lexer.token() == Token.DISTINCT) {
               this.lexer.nextToken();
               union.setOperator(SQLUnionOperator.MINUS_DISTINCT);
            } else {
               union.setOperator(SQLUnionOperator.MINUS);
            }

            SQLSelectQuery right = this.query(union, false);
            union.setRight(right);
            return this.queryRest(union, true);
         } else {
            return selectQuery;
         }
      } else {
         SQLUnionQuery union;
         do {
            this.lexer.nextToken();
            union = this.createSQLUnionQuery();
            if (union.getRelations().isEmpty()) {
               union.setLeft(selectQuery);
            }

            if (this.lexer.token() == Token.ALL) {
               union.setOperator(SQLUnionOperator.UNION_ALL);
               this.lexer.nextToken();
            } else if (this.lexer.token() == Token.DISTINCT) {
               union.setOperator(SQLUnionOperator.DISTINCT);
               this.lexer.nextToken();
            }

            boolean paren = this.lexer.token() == Token.LPAREN;
            SQLSelectQuery right = this.query(paren ? null : union, false);
            union.setRight(right);

            while(this.lexer.isEnabled(SQLParserFeature.EnableMultiUnion) && this.lexer.token() == Token.UNION) {
               Lexer.SavePoint mark = this.lexer.mark();
               this.lexer.nextToken();
               if (this.lexer.token() == Token.ALL) {
                  if (union.getOperator() != SQLUnionOperator.UNION_ALL) {
                     this.lexer.reset(mark);
                     break;
                  }

                  this.lexer.nextToken();
               } else if (this.lexer.token() == Token.DISTINCT) {
                  if (union.getOperator() != SQLUnionOperator.DISTINCT) {
                     this.lexer.reset(mark);
                     break;
                  }

                  this.lexer.nextToken();
               } else if (union.getOperator() != SQLUnionOperator.UNION) {
                  this.lexer.reset(mark);
                  break;
               }

               paren = this.lexer.token() == Token.LPAREN;
               SQLSelectQuery r = this.query(paren ? null : union, false);
               union.addRelation(r);
               right = r;
            }

            if (!paren) {
               if (right instanceof SQLSelectQueryBlock) {
                  SQLSelectQueryBlock rightQuery = (SQLSelectQueryBlock)right;
                  SQLOrderBy orderBy = rightQuery.getOrderBy();
                  if (orderBy != null) {
                     union.setOrderBy(orderBy);
                     rightQuery.setOrderBy((SQLOrderBy)null);
                  }

                  SQLLimit limit = rightQuery.getLimit();
                  if (limit != null) {
                     union.setLimit(limit);
                     rightQuery.setLimit((SQLLimit)null);
                  }
               } else if (right instanceof SQLUnionQuery) {
                  SQLUnionQuery rightUnion = (SQLUnionQuery)right;
                  SQLOrderBy orderBy = rightUnion.getOrderBy();
                  if (orderBy != null) {
                     union.setOrderBy(orderBy);
                     rightUnion.setOrderBy((SQLOrderBy)null);
                  }

                  SQLLimit limit = rightUnion.getLimit();
                  if (limit != null) {
                     union.setLimit(limit);
                     rightUnion.setLimit((SQLLimit)null);
                  }
               }
            }

            union = this.unionRest(union);
            selectQuery = union;
         } while(this.lexer.token() == Token.UNION);

         selectQuery = this.queryRest(union, true);
         return selectQuery;
      }
   }

   public SQLSelectQuery query(SQLObject parent, boolean acceptUnion) {
      if (this.lexer.token() == Token.VALUES) {
         return this.valuesQuery(acceptUnion);
      } else if (this.lexer.token() == Token.LPAREN) {
         this.lexer.nextToken();
         SQLSelectQuery select = this.query();
         if (select instanceof SQLSelectQueryBlock) {
            ((SQLSelectQueryBlock)select).setParenthesized(true);
         }

         this.accept(Token.RPAREN);
         return this.queryRest(select, acceptUnion);
      } else {
         GPSelectQueryBlock queryBlock = new GPSelectQueryBlock();
         if (this.lexer.hasComment() && this.lexer.isKeepComments()) {
            queryBlock.addBeforeComment(this.lexer.readAndResetComments());
         }

         if (this.lexer.token() == Token.SELECT) {
            this.lexer.nextToken();
            if (this.lexer.token() == Token.COMMENT) {
               this.lexer.nextToken();
            }

            if (this.lexer.token() == Token.DISTINCT) {
               queryBlock.setDistionOption(2);
               this.lexer.nextToken();
               if (this.lexer.token() == Token.ON) {
                  this.lexer.nextToken();

                  while(true) {
                     this.lexer.nextToken();
                     SQLExpr expr = this.exprParser.expr();
                     this.lexer.nextToken();
                     queryBlock.getDistinctOn().add(expr);
                     if (this.lexer.token() != Token.COMMA) {
                        break;
                     }

                     this.lexer.nextToken();
                  }
               }
            } else if (this.lexer.token() == Token.ALL) {
               queryBlock.setDistionOption(1);
               this.lexer.nextToken();
            }

            this.parseSelectList(queryBlock);
            if (this.lexer.token() == Token.INTO) {
               this.lexer.nextToken();
               if (this.lexer.token() == Token.TEMPORARY) {
                  this.lexer.nextToken();
                  queryBlock.setIntoOption(GPSelectQueryBlock.IntoOption.TEMPORARY);
               } else if (this.lexer.token() == Token.TEMP) {
                  this.lexer.nextToken();
                  queryBlock.setIntoOption(GPSelectQueryBlock.IntoOption.TEMP);
               } else if (this.lexer.token() == Token.UNLOGGED) {
                  this.lexer.nextToken();
                  queryBlock.setIntoOption(GPSelectQueryBlock.IntoOption.UNLOGGED);
               }

               if (this.lexer.token() == Token.TABLE) {
                  this.lexer.nextToken();
               }

               SQLExpr name = this.createExprParser().name();
               GPExprTableSource ts = new GPExprTableSource();
               ts.setExpr(name);
               queryBlock.setInto(ts);
            }
         }

         if (this.lexer.token() == Token.TABLE) {
            this.lexer.nextToken();
            queryBlock.putAttribute("$table-query", true);
         }

         this.parseFrom(queryBlock);
         this.parseWhere(queryBlock);
         this.parseGroupBy(queryBlock);
         if (this.lexer.token() == Token.WINDOW) {
            this.parseWindow(queryBlock);
         }

         queryBlock.setOrderBy(this.createExprParser().parseOrderBy());

         while(true) {
            while(this.lexer.token() != Token.LIMIT) {
               if (this.lexer.token() == Token.OFFSET) {
                  SQLLimit limit = queryBlock.getLimit();
                  if (limit == null) {
                     limit = new SQLLimit();
                     queryBlock.setLimit(limit);
                  }

                  limit.setType(SQLLimit.Type.LIMIT_OFFSET);
                  this.lexer.nextToken();
                  SQLExpr offset = this.expr();
                  limit.setOffset(offset);
                  if (this.lexer.token() == Token.ROW || this.lexer.token() == Token.ROWS) {
                     this.lexer.nextToken();
                  }
               } else {
                  if (this.lexer.token() != Token.USING) {
                     if (this.lexer.token() == Token.FETCH) {
                        this.lexer.nextToken();
                        GPSelectQueryBlock.FetchClause fetch = new GPSelectQueryBlock.FetchClause();
                        if (this.lexer.token() == Token.FIRST) {
                           fetch.setOption(GPSelectQueryBlock.FetchClause.Option.FIRST);
                        } else {
                           if (this.lexer.token() != Token.NEXT) {
                              throw new ParserException("expect 'FIRST' or 'NEXT'. " + this.lexer.info());
                           }

                           fetch.setOption(GPSelectQueryBlock.FetchClause.Option.NEXT);
                        }

                        this.lexer.nextToken();
                        SQLExpr count = this.expr();
                        fetch.setCount(count);
                        if (this.lexer.token() != Token.ROW && this.lexer.token() != Token.ROWS) {
                           throw new ParserException("expect 'ROW' or 'ROWS'. " + this.lexer.info());
                        }

                        this.lexer.nextToken();
                        if (this.lexer.token() != Token.ONLY) {
                           throw new ParserException("expect 'ONLY'. " + this.lexer.info());
                        }

                        this.lexer.nextToken();
                        queryBlock.setFetch(fetch);
                     }

                     GPSelectQueryBlock.ForClause forClause;
                     for(GPSelectQueryBlock.ForClauses forClauses = null; this.lexer.token() == Token.FOR; forClauses.addForClause(forClause)) {
                        if (forClauses == null) {
                           forClauses = new GPSelectQueryBlock.ForClauses();
                           queryBlock.setForClause(forClauses);
                        }

                        this.lexer.nextToken();
                        forClause = new GPSelectQueryBlock.ForClause();
                        if (this.lexer.token() == Token.UPDATE) {
                           forClause.setOption(GPSelectQueryBlock.ForClause.Option.UPDATE);
                           this.lexer.nextToken();
                        } else if (this.lexer.token() == Token.SHARE) {
                           forClause.setOption(GPSelectQueryBlock.ForClause.Option.SHARE);
                           this.lexer.nextToken();
                        } else if (this.lexer.identifierEquals("NO")) {
                           this.lexer.nextToken();
                           this.accept(Token.KEY);
                           this.accept(Token.UPDATE);
                           forClause.setOption(GPSelectQueryBlock.ForClause.Option.NO_KEY_UPDATE);
                        } else {
                           if (this.lexer.token() != Token.KEY) {
                              throw new ParserException("expect 'FIRST' or 'NEXT'. " + this.lexer.info());
                           }

                           this.lexer.nextToken();
                           this.accept(Token.SHARE);
                           forClause.setOption(GPSelectQueryBlock.ForClause.Option.KEY_SHARE);
                        }

                        if (this.lexer.token() == Token.OF) {
                           this.lexer.nextToken();

                           while(true) {
                              SQLExpr expr = this.exprParser.expr();
                              forClause.getOf().add(expr);
                              if (this.lexer.token() != Token.COMMA) {
                                 break;
                              }

                              this.lexer.nextToken();
                           }
                        }

                        if (this.lexer.token() == Token.NOWAIT) {
                           this.lexer.nextToken();
                           forClause.setNoWait(true);
                        } else if (this.lexer.identifierEquals(FnvHash.Constants.SKIP)) {
                           this.lexer.nextToken();
                           this.acceptIdentifier("LOCKED");
                           forClause.setSkipLocked(true);
                        }
                     }

                     return this.queryRest(queryBlock, acceptUnion);
                  }

                  queryBlock.setUsing(true);
                  this.lexer.nextToken();
                  queryBlock.setUsingOperator(new SQLIdentifierExpr(this.lexer.token().name));
                  this.lexer.nextToken();
               }
            }

            SQLLimit limit = new SQLLimit();
            limit.setType(SQLLimit.Type.LIMIT);
            this.lexer.nextToken();
            if (this.lexer.token() == Token.ALL) {
               limit.setRowCount(new SQLIdentifierExpr("ALL"));
               this.lexer.nextToken();
            } else {
               limit.setRowCount(this.expr());
               if (this.lexer.token() == Token.OFFSET) {
                  this.lexer.nextToken();
                  limit.setOffset(this.expr());
               }
            }

            queryBlock.setLimit(limit);
         }
      }
   }

   public void parseFrom(SQLSelectQueryBlock queryBlock) {
      if (this.lexer.token() != Token.EOF && this.lexer.token() != Token.SEMI && this.lexer.token() != Token.ORDER && this.lexer.token() != Token.RPAREN && this.lexer.token() != Token.UNION) {
         Boolean tableQuery = (Boolean)queryBlock.getAttribute("$table-query");
         if (this.lexer.token() != Token.FROM && tableQuery != null && tableQuery && tableQuery != null && tableQuery) {
            SQLSelectItem item = new SQLSelectItem();
            SQLAllColumnExpr expr = new SQLAllColumnExpr();
            item.setExpr(expr);
            queryBlock.getSelectList().add(item);
         }

         if (this.lexer.token() != Token.FROM) {
            if (this.lexer.token() == Token.ONLY) {
               this.lexer.nextToken();
            }

            SQLTableSource from = this.parseTableSource();
            if (from instanceof GPExprTableSource) {
               GPExprTableSource gpFrom = (GPExprTableSource)from;
               if (gpFrom.getExpr() == null) {
                  return;
               }
            }

            queryBlock.setFrom(from);
         } else if (this.lexer.token() == Token.FROM) {
            this.lexer.nextToken();
            queryBlock.setFrom(this.parseTableSource());
         }
      }
   }

   public SQLTableSource parseTableSource() {
      if (this.lexer.token() == Token.LPAREN) {
         this.lexer.nextToken();
         SQLTableSource tableSource;
         if (this.lexer.token() != Token.SELECT && this.lexer.token() != Token.WITH && this.lexer.token() != Token.SEL) {
            if (this.lexer.token() == Token.VALUES) {
               tableSource = new GPValuesTableSource();
               GPValuesTableSource ts = (GPValuesTableSource)tableSource;
               this.lexer.nextToken();

               do {
                  if (this.lexer.token() == Token.COMMA) {
                     this.lexer.nextToken();
                  }

                  if (this.lexer.token() == Token.LPAREN) {
                     this.lexer.nextToken();
                  }

                  GPValuesTableSource.ValueRow row = new GPValuesTableSource.ValueRow();

                  do {
                     if (this.lexer.token() == Token.COMMA) {
                        this.lexer.nextToken();
                     }

                     SQLExpr expr = this.exprParser.expr();
                     row.getVals().add(expr);
                  } while(this.lexer.token() != Token.RPAREN);

                  this.accept(Token.RPAREN);
                  ts.getRows().add(row);
               } while(this.lexer.token() == Token.COMMA);

               this.accept(Token.RPAREN);
               tableSource.setAlias(this.lexer.stringVal());
               this.lexer.nextToken();
            } else if (this.lexer.token() == Token.LPAREN) {
               tableSource = this.parseTableSource();
               this.accept(Token.RPAREN);
            } else {
               tableSource = this.parseTableSource();
               this.accept(Token.RPAREN);
            }
         } else {
            SQLSelect select = this.select();
            this.accept(Token.RPAREN);
            if (select.getQuery() instanceof SQLSelectQueryBlock) {
               ((SQLSelectQueryBlock)select.getQuery()).setParenthesized(true);
            }

            SQLSelectQuery query = this.queryRest(select.getQuery(), true);
            if (query instanceof SQLUnionQuery) {
               tableSource = new SQLUnionQueryTableSource((SQLUnionQuery)query);
            } else {
               tableSource = new SQLSubqueryTableSource(select);
            }
         }

         if (this.lexer.token() == Token.AS) {
            this.lexer.nextToken();
            String alias = this.tableAlias(true);
            tableSource.setAlias(alias);
            if (tableSource instanceof SQLValuesTableSource && ((SQLValuesTableSource)tableSource).getColumns().size() == 0) {
               SQLValuesTableSource values = (SQLValuesTableSource)tableSource;
               this.accept(Token.LPAREN);
               this.exprParser.names(values.getColumns(), values);
               this.accept(Token.RPAREN);
            } else if (tableSource instanceof SQLSubqueryTableSource) {
               SQLSubqueryTableSource values = (SQLSubqueryTableSource)tableSource;
               if (this.lexer.token() == Token.LPAREN) {
                  this.lexer.nextToken();
                  this.exprParser.names(values.getColumns(), values);
                  this.accept(Token.RPAREN);
               }
            }
         }

         return this.parseTableSourceRest(tableSource);
      } else if (this.lexer.token() != Token.VALUES) {
         if (this.lexer.token() == Token.SELECT) {
            throw new ParserException("TODO " + this.lexer.info());
         } else {
            SQLExprTableSource tableReference = new SQLExprTableSource();
            this.parseTableSourceQueryTableExpr(tableReference);
            SQLTableSource tableSrc = this.parseTableSourceRest(tableReference);
            if (this.lexer.hasComment() && this.lexer.isKeepComments()) {
               tableSrc.addAfterComment(this.lexer.readAndResetComments());
            }

            return tableSrc;
         }
      } else {
         this.lexer.nextToken();
         SQLValuesTableSource tableSource = new SQLValuesTableSource();

         while(true) {
            this.accept(Token.LPAREN);
            SQLListExpr listExpr = new SQLListExpr();
            this.exprParser.exprList(listExpr.getItems(), listExpr);
            this.accept(Token.RPAREN);
            listExpr.setParent(tableSource);
            tableSource.getValues().add(listExpr);
            if (this.lexer.token() != Token.COMMA) {
               if (this.lexer.token() == Token.RPAREN) {
                  return tableSource;
               } else {
                  String alias = this.tableAlias();
                  if (alias != null) {
                     tableSource.setAlias(alias);
                  }

                  this.accept(Token.LPAREN);
                  this.exprParser.names(tableSource.getColumns(), tableSource);
                  this.accept(Token.RPAREN);
                  return tableSource;
               }
            }

            this.lexer.nextToken();
         }
      }
   }

   protected void parseTableSourceQueryTableExpr(SQLExprTableSource tableReference) {
      if (this.lexer.token() == Token.ONLY) {
         tableReference.putAttribute("table-only", true);
         this.lexer.nextToken();
      }

      if (this.lexer.token() == Token.ROWS) {
         this.lexer.nextToken();
         tableReference.putAttribute("ts-rows-from", true);
         this.accept(Token.FROM);
         this.accept(Token.LPAREN);
      }

      if (this.lexer.token() != Token.LITERAL_ALIAS && !this.lexer.identifierEquals(FnvHash.Constants.IDENTIFIED) && this.lexer.token() != Token.LITERAL_CHARS) {
         if (this.lexer.token() == Token.HINT) {
            SQLCommentHint hint = this.exprParser.parseHint();
            tableReference.setHint(hint);
         }

         SQLExpr expr = null;
         if (this.lexer.token() == Token.IDENTIFIER) {
            this.lexer.mark();
            SQLName name = new SQLIdentifierExpr(this.lexer.stringVal());
            this.lexer.nextToken();
            if (this.lexer.token() != Token.LPAREN) {
               if (this.lexer.token() == Token.STAR) {
                  this.lexer.nextToken();
                  expr = name;
               } else {
                  this.lexer.reset();
                  if (this.lexer.token() != Token.SET) {
                     expr = this.expr();
                     if (this.lexer.token() == Token.STAR) {
                        this.lexer.nextToken();
                     }
                  } else {
                     expr = name;
                  }
               }
            } else {
               this.lexer.reset();
               if (this.lexer.token() != Token.SET) {
                  expr = this.expr();
               } else {
                  expr = name;
               }
            }
         }

         if (expr instanceof SQLBinaryOpExpr) {
            throw new ParserException("Invalid from clause : " + expr.toString().replace("\n", " "));
         } else {
            if (this.lexer.token() == Token.MONKEYS_AT) {
               this.lexer.nextToken();
               SQLExpr linkExpr = this.expr().clone();
               linkExpr.setParent(tableReference);
            }

            tableReference.setExpr(expr);
         }
      } else {
         tableReference.setExpr((SQLExpr)this.exprParser.name());
      }
   }

   public SQLTableSource parseTableSourceRest(SQLTableSource tableSource) {
      Boolean only = (Boolean)tableSource.getAttribute("table-only");
      Boolean withOrdinality = (Boolean)tableSource.getAttribute("table-with-ordinality");
      Boolean rowFrom = (Boolean)tableSource.getAttribute("ts-rows-from");
      if (tableSource instanceof SQLExprTableSource && !(tableSource instanceof GPTableSource)) {
         SQLExprTableSource exprTableSource = (SQLExprTableSource)tableSource;
         SQLTableSource gts = this.parseExprTableSource(exprTableSource);
         if (gts instanceof SQLExprTableSource) {
            gts = this.parseMethodDataSource((SQLExprTableSource)gts);
         }

         return !(gts instanceof GPRowsFromTableSource) && !(gts instanceof GPMethodInvokeTableSource) ? super.parseTableSourceRest(gts) : gts;
      } else if (tableSource instanceof SQLSubqueryTableSource && !(tableSource instanceof GPTableSource)) {
         GPSubqueryTableSource ts = new GPSubqueryTableSource();
         SQLSubqueryTableSource subquery = (SQLSubqueryTableSource)tableSource;
         subquery.cloneTo(ts);
         if (only != null) {
            ts.setOnly(only);
         }

         return super.parseTableSourceRest(ts);
      } else {
         if (this.lexer.token() == Token.IDENTITY && tableSource instanceof SQLExprTableSource) {
            this.lexer.nextToken();
            String alias = null;
            if (this.lexer.token() == Token.IDENTIFIER) {
               alias = this.lexer.stringVal();
               this.lexer.nextToken();
            }

            if (this.lexer.token() == Token.LPAREN) {
               SQLExprTableSource exprTableSource = (SQLExprTableSource)tableSource;
               GPFunctionTableSource functionTableSource = new GPFunctionTableSource(exprTableSource.getExpr());
               if (alias != null) {
                  functionTableSource.setAlias(alias);
               }

               this.lexer.nextToken();
               this.parserParameters(functionTableSource.getParameters());
               this.accept(Token.RPAREN);
               return super.parseTableSourceRest(functionTableSource);
            }

            if (alias != null) {
               tableSource.setAlias(alias);
               return super.parseTableSourceRest(tableSource);
            }
         }

         if (!tableSource.getClass().equals(GPValuesTableSource.class)) {
            return super.parseTableSourceRest(tableSource);
         } else {
            if (this.lexer.token() == Token.LPAREN) {
               GPValuesTableSource ts = (GPValuesTableSource)tableSource;

               while(this.lexer.token() != Token.RPAREN) {
                  this.lexer.nextToken();
                  SQLExpr expr = this.exprParser.expr();
                  ts.addColumnAlias((SQLName)expr);
               }

               this.lexer.nextToken();
            }

            return tableSource;
         }
      }
   }

   private SQLTableSource parseExprTableSource(SQLExprTableSource tableSource) {
      if (tableSource instanceof GPTableSource) {
         return tableSource;
      } else if (!(tableSource instanceof SQLExprTableSource)) {
         return tableSource;
      } else {
         Boolean only = (Boolean)tableSource.getAttribute("table-only");
         SQLExpr expr = tableSource.getExpr();
         if (expr instanceof SQLMethodInvokeExpr) {
            return tableSource;
         } else {
            GPExprTableSource ts = new GPExprTableSource();
            tableSource.cloneTo(ts);
            if (only != null) {
               ts.setOnly(only);
            }

            if (this.lexer.token() == Token.AS) {
               this.lexer.nextToken();
            }

            String alias = null;
            if (!this.lexer.identifierEquals("NATURAL") && !this.lexer.identifierEquals("CROSS") && this.lexer.token() == Token.IDENTIFIER) {
               alias = this.lexer.stringVal();
               ts.setAlias(alias);
               this.lexer.nextToken();
            }

            this.parseColumns(ts);
            return this.parseTableSourceRest(ts);
         }
      }
   }

   private SQLTableSource parseMethodDataSource(SQLExprTableSource tableSource) {
      SQLExpr expr = tableSource.getExpr();
      if (!(expr instanceof SQLMethodInvokeExpr)) {
         return tableSource;
      } else {
         GPMethodInvokeTableSource ts = new GPMethodInvokeTableSource();
         SQLMethodInvokeExpr method = (SQLMethodInvokeExpr)expr;
         ts.setExpr(method.getMethodName());

         for(SQLExpr parameter : method.getParameters()) {
            SQLExpr p = parameter.clone();
            ts.getParameters().add(p);
            p.setParent(ts);
         }

         GPTableSource gts = null;
         GPRowsFromTableSource rts = null;
         Boolean rowFrom = (Boolean)tableSource.getAttribute("ts-rows-from");
         if (rowFrom != null && rowFrom) {
            rts = new GPRowsFromTableSource();
            rts.addTableSource(ts);
            gts = rts;
         } else {
            gts = ts;
         }

         if (rowFrom != null) {
            ts.setRowsFrom(rowFrom);

            do {
               if (this.lexer.token() == Token.AS) {
                  this.lexer.nextToken();
               }

               this.parseColumns(ts);
               if (this.lexer.token() == Token.COMMA) {
                  this.lexer.nextToken();
                  SQLTableSource nts = this.parseTableSource();
                  ts = (GPMethodInvokeTableSource)nts;
                  rts.addTableSource(ts);
               }
            } while(this.lexer.token() == Token.COMMA);

            this.accept(Token.RPAREN);
         }

         if (this.lexer.token() == Token.WITH) {
            this.lexer.nextToken();
            this.lexer.nextToken();
            ts.setWithOrdinality(true);
            if (rts != null) {
               ts.setWithOrdinality(true);
            }
         }

         if (this.lexer.token() == Token.AS) {
            this.lexer.nextToken();
         }

         String alias = null;
         if (this.lexer.token() == Token.IDENTIFIER) {
            alias = this.lexer.stringVal();
            gts.setAlias(alias);
            this.lexer.nextToken();
         }

         this.parseColumns(gts);
         return (SQLTableSource)(gts != null ? gts : ts);
      }
   }

   private void parseColumns(GPTableSource gts) {
      if (this.lexer.token() == Token.LPAREN) {
         this.lexer.nextToken();

         while(this.lexer.token() != Token.RPAREN) {
            SQLName name = this.exprParser.name();
            if (this.lexer.token() != Token.COMMA && this.lexer.token() != Token.RPAREN) {
               SQLExpr type = new SQLIdentifierExpr(this.lexer.stringVal());
               GPColumnSimpleDefinitionExpr def = new GPColumnSimpleDefinitionExpr();
               def.setName(name);
               def.setType(type);
               if (gts instanceof GPMethodInvokeTableSource) {
                  GPMethodInvokeTableSource mits = (GPMethodInvokeTableSource)gts;
                  mits.addColumnAlias(def);
               } else if (gts instanceof GPRowsFromTableSource) {
                  GPRowsFromTableSource mits = (GPRowsFromTableSource)gts;
                  mits.addColumnAlias(def);
               }

               this.lexer.nextToken();
            } else {
               gts.addColumnAlias(name);
            }

            if (this.lexer.token() == Token.COMMA) {
               this.lexer.nextToken();
            }
         }

         this.accept(Token.RPAREN);
      }

   }

   private void parserParameters(List<SQLParameter> parameters) {
      do {
         SQLParameter parameter = new SQLParameter();
         parameter.setName(this.exprParser.name());
         parameter.setDataType(this.exprParser.parseDataType());
         parameters.add(parameter);
         if (this.lexer.token() == Token.COMMA || this.lexer.token() == Token.SEMI) {
            this.lexer.nextToken();
         }
      } while(this.lexer.token() != Token.BEGIN && this.lexer.token() != Token.RPAREN);

   }

   protected void parseWindow(SQLSelectQueryBlock queryBlock) {
      if (this.lexer.identifierEquals(FnvHash.Constants.WINDOW) || this.lexer.token() == Token.WINDOW) {
         this.lexer.nextToken();

         while(true) {
            SQLName name = this.exprParser.name();
            this.accept(Token.AS);
            SQLOver over = new SQLOver();
            this.exprParser.over(over);
            queryBlock.addWindow(new SQLWindow(name, over));
            if (this.lexer.token() != Token.COMMA) {
               return;
            }

            this.lexer.nextToken();
         }
      }
   }
}
