All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jparsec.examples.sql.parser.RelationParser Maven / Gradle / Ivy

The newest version!
/*****************************************************************************
 * Copyright (C) jparsec.org                                                *
 * ------------------------------------------------------------------------- *
 * Licensed under the Apache License, Version 2.0 (the "License");           *
 * you may not use this file except in compliance with the License.          *
 * You may obtain a copy of the License at                                   *
 *                                                                           *
 * http://www.apache.org/licenses/LICENSE-2.0                                *
 *                                                                           *
 * Unless required by applicable law or agreed to in writing, software       *
 * distributed under the License is distributed on an "AS IS" BASIS,         *
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  *
 * See the License for the specific language governing permissions and       *
 * limitations under the License.                                            *
 *****************************************************************************/
package org.jparsec.examples.sql.parser;

import java.util.List;
import java.util.function.UnaryOperator;

import org.jparsec.Parser;
import org.jparsec.Parsers;
import org.jparsec.examples.sql.ast.AliasedRelation;
import org.jparsec.examples.sql.ast.CrossJoinRelation;
import org.jparsec.examples.sql.ast.Expression;
import org.jparsec.examples.sql.ast.GroupBy;
import org.jparsec.examples.sql.ast.JoinRelation;
import org.jparsec.examples.sql.ast.JoinType;
import org.jparsec.examples.sql.ast.OrderBy;
import org.jparsec.examples.sql.ast.Projection;
import org.jparsec.examples.sql.ast.Relation;
import org.jparsec.examples.sql.ast.Select;
import org.jparsec.examples.sql.ast.TableRelation;
import org.jparsec.examples.sql.ast.UnionRelation;

/**
 * Parser for relation.
 * 
 * @author Ben Yu
 */
public final class RelationParser {

  static final Parser ALIAS = TerminalParser.term("as").optional().next(TerminalParser.NAME);
  
  static final Parser FULL_JOIN = joinType(JoinType.FULL, "full join", "full outer join");
  static final Parser RIGHT_JOIN =
      joinType(JoinType.RIGHT, "right join", "right outer join");
  static final Parser LEFT_JOIN = joinType(JoinType.LEFT, "left join", "left outer join");
  static final Parser INNER_JOIN = joinType(JoinType.INNER, "join", "inner join");
  
  static final Parser TABLE = TerminalParser.QUALIFIED_NAME.map(TableRelation::new);
  static final Parser SELECT_CLAUSE = TerminalParser.term("select").next(TerminalParser.term("distinct").succeeds());
  
  static final Parser projection(Parser expr) {
    return Parsers.sequence(expr, ALIAS.optional(), Projection::new);
  }
  
  static final Parser alias(Parser rel) {
    return Parsers.sequence(rel, ALIAS, AliasedRelation::new);
  }
  
  static final Parser aliasable(Parser rel) {
    return alias(rel).or(rel);
  }
  
  static final Parser selectClause() {
    return TerminalParser.term("select").next(TerminalParser.term("distinct").succeeds());
  }
  
  static Parser> fromClause(Parser rel) {
    return TerminalParser.term("from").next(aliasable(rel).sepBy1(TerminalParser.term(",")));
  }
  
  static Parser whereClause(Parser cond) {
    return TerminalParser.term("where").next(cond);
  }
  
  static Parser groupByClause(Parser expr, Parser cond) {
    return Parsers.sequence(
        TerminalParser.phrase("group by").next(list(expr)), TerminalParser.phrase("having").next(cond).optional(),
        GroupBy::new);
  }
  
  static Parser havingClause(Parser cond) {
    return TerminalParser.term("having").next(cond);
  }
  
  static Parser orderByItem(Parser expr) {
    return Parsers.sequence(
        expr, Parsers.or(TerminalParser.term("asc").retn(true), TerminalParser.term("desc").retn(false)).optional(true),
        OrderBy.Item::new);
  }
  
  static Parser orderByClause(Parser expr) {
    return TerminalParser.phrase("order by").next(list(orderByItem(expr))).map(OrderBy::new);
  }
  
  static Parser join(Parser rel, Parser cond) {
    Parser.Reference ref = Parser.newReference();
    Parser lazy = ref.lazy();
    Parser atom = aliasable(ExpressionParser.paren(lazy).or(rel));
    
    // Cannot use regular infix operator because of the "join ... on ..." syntax.
    Parser> crossJoin =
        TerminalParser.phrase("cross join").next(atom).map(r -> l -> new CrossJoinRelation(l, r));
    Parser parser = atom.postfix(Parsers.or(
        joinOn(INNER_JOIN, lazy, cond), 
        joinOn(LEFT_JOIN, lazy, cond),
        joinOn(RIGHT_JOIN, lazy, cond),
        joinOn(FULL_JOIN, lazy, cond),
        crossJoin));
    ref.set(parser);
    return parser;
  }
  
  static Parser select(
      Parser expr, Parser cond, Parser rel) {
    return Parsers.sequence(
        SELECT_CLAUSE, list(projection(expr)),
        fromClause(join(rel, cond)),
        whereClause(cond).optional(),
        groupByClause(expr, cond).optional(),
        orderByClause(expr).optional(),
        Select::new);
  }
  
  static Parser union(Parser rel) {
    Parser.Reference ref = Parser.newReference();
    Parser parser = ExpressionParser.paren(ref.lazy()).or(rel).infixl(
        TerminalParser.term("union").next(TerminalParser.term("all").succeeds())
            .label("relation")
            .map(a -> (l, r) -> new UnionRelation(l, a, r)));
    ref.set(parser);
    return parser;
  }
  
  static Parser query(
      Parser expr, Parser cond, Parser rel) {
    return union(select(expr, cond, rel));
  }
  
  /** The {@link Parser} for a full fledged SQL query. */
  public static Parser query() {
    Parser.Reference relationRef = Parser.newReference();
    Parser subQuery = ExpressionParser.paren(relationRef.lazy());
    Parser.Reference conditionRef = Parser.newReference();
    Parser expr = ExpressionParser.expression(conditionRef.lazy());
    Parser cond = ExpressionParser.condition(expr, subQuery);
    Parser relation = query(expr, cond, subQuery.or(TABLE));
    conditionRef.set(cond);
    relationRef.set(relation);
    return relation;
  }
  
  private static Parser joinType(JoinType joinType, String phrase1, String phrase2) {
    return Parsers.or(TerminalParser.phrase(phrase1), TerminalParser.phrase(phrase2)).retn(joinType);
  }
  
  private static Parser> joinOn(
      Parser joinType, Parser right, Parser cond) {
    return Parsers.sequence(
        joinType, right, TerminalParser.term("on").next(cond),
        (t, r, c) -> l -> new JoinRelation(l, t, r, c));
  }
  
  private static  Parser> list(Parser p) {
    return p.sepBy1(TerminalParser.term(","));
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy