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(","));
}
}