@Override protected Type visitSubqueryExpression(SubqueryExpression node, StackableAstVisitorContext<AnalysisContext> context) { StatementAnalyzer analyzer = statementAnalyzerFactory.apply(node); RelationType descriptor = analyzer.process(node.getQuery(), context.getContext()); // Subquery should only produce one column if (descriptor.getVisibleFieldCount() != 1) { throw new SemanticException(MULTIPLE_FIELDS_FROM_SUBQUERY, node, "Multiple columns returned by subquery are not yet supported. Found %s", descriptor.getVisibleFieldCount()); } Optional<Node> previousNode = context.getPreviousNode(); if (previousNode.isPresent() && previousNode.get() instanceof InPredicate && ((InPredicate) previousNode.get()).getValue() != node) { subqueryInPredicates.add((InPredicate) previousNode.get()); } else { scalarSubqueries.add(node); } Type type = Iterables.getOnlyElement(descriptor.getVisibleFields()).getType(); expressionTypes.put(node, type); return type; }
public void analyzeWhere(Node node, RelationType tupleDescriptor, AnalysisContext context, Expression predicate) { Analyzer.verifyNoAggregatesOrWindowFunctions(metadata, predicate, "WHERE"); ExpressionAnalysis expressionAnalysis = analyzeExpression(predicate, tupleDescriptor, context); analysis.recordSubqueries(node, expressionAnalysis); Type predicateType = expressionAnalysis.getType(predicate); if (!predicateType.equals(BOOLEAN)) { if (!predicateType.equals(UNKNOWN)) { throw new SemanticException(TYPE_MISMATCH, predicate, "WHERE clause must evaluate to a boolean: actual type %s", predicateType); } // coerce null to boolean analysis.addCoercion(predicate, BOOLEAN); } analysis.setWhere(node, predicate); }
@Override public Node visitSetOperation(SqlBaseParser.SetOperationContext context) { QueryBody left = (QueryBody) visit(context.left); QueryBody right = (QueryBody) visit(context.right); boolean distinct = context.setQuantifier() == null || context.setQuantifier().DISTINCT() != null; switch (context.operator.getType()) { case SqlBaseLexer.UNION: return new Union(getLocation(context.UNION()), ImmutableList.of(left, right), distinct); case SqlBaseLexer.INTERSECT: return new Intersect(getLocation(context.INTERSECT()), ImmutableList.of(left, right), distinct); case SqlBaseLexer.EXCEPT: return new Except(getLocation(context.EXCEPT()), left, right, distinct); } throw new IllegalArgumentException("Unsupported set operation: " + context.operator.getText()); }
public String formatSql(Node root) { StringBuilder builder = new StringBuilder(); if (root instanceof Statement) { setOutputPassFunc(false); setOutputDivideByZeroGuard(true); } else { // Expression setOutputPassFunc(true); setOutputDivideByZeroGuard(true); } new Formatter(builder).process(root, 0); return builder.toString(); }
public String formatSql(Node root) { StringBuilder builder = new StringBuilder(); if (root instanceof Statement) { setOutputPassFunc(false); setOutputDivideByZeroGuard(false); } else { // Expression setOutputPassFunc(true); setOutputDivideByZeroGuard(false); } new Formatter(builder).process(root, 0); return builder.toString(); }
public static QualifiedObjectName createQualifiedObjectName(Session session, Node node, QualifiedName name) { requireNonNull(session, "session is null"); requireNonNull(name, "name is null"); checkArgument(name.getParts().size() <= 3, "Too many dots in table name: %s", name); List<String> parts = Lists.reverse(name.getParts()); String objectName = parts.get(0); String schemaName = (parts.size() > 1) ? parts.get(1) : session.getSchema().orElseThrow(() -> new SemanticException(CATALOG_NOT_SPECIFIED, node, "Catalog must be specified when session catalog is not set")); String catalogName = (parts.size() > 2) ? parts.get(2) : session.getCatalog().orElseThrow(() -> new SemanticException(SCHEMA_NOT_SPECIFIED, node, "Schema must be specified when session schema is not set")); return new QualifiedObjectName(catalogName, schemaName, objectName); }
private static String formatMessage(String formatString, Node node, Object[] args) { if (node.getLocation().isPresent()) { NodeLocation nodeLocation = node.getLocation().get(); return format("line %s:%s: %s", nodeLocation.getLineNumber(), nodeLocation.getColumnNumber(), format(formatString, args)); } return format(formatString, args); }
public static void assertFormattedSql(SqlParser sqlParser, Node expected) { String formatted = formatSql(expected); // verify round-trip of formatting already-formatted SQL Statement actual = parseFormatted(sqlParser, formatted, expected); assertEquals(formatSql(actual), formatted); // compare parsed tree with parsed tree of formatted SQL if (!actual.equals(expected)) { // simplify finding the non-equal part of the tree assertListEquals(linearizeTree(actual), linearizeTree(expected)); } assertEquals(actual, expected); }
@Override public Node visitWindowFrame(SqlBaseParser.WindowFrameContext context) { return new WindowFrame( getLocation(context), getFrameType(context.frameType), (FrameBound) visit(context.start), visitIfPresent(context.end, FrameBound.class)); }
@SuppressWarnings("SuspiciousMethodCalls") @Override public Type process(Node node, @Nullable StackableAstVisitorContext<AnalysisContext> context) { // don't double process a node Type type = expressionTypes.get(node); if (type != null) { return type; } return super.process(node, context); }
private Type coerceToSingleType(StackableAstVisitorContext<AnalysisContext> context, Node node, String message, Expression first, Expression second) { Type firstType = null; if (first != null) { firstType = process(first, context); } Type secondType = null; if (second != null) { secondType = process(second, context); } if (firstType == null) { return secondType; } if (secondType == null) { return firstType; } if (firstType.equals(secondType)) { return firstType; } // coerce types if possible if (canCoerce(firstType, secondType)) { expressionCoercions.put(first, secondType); return secondType; } if (canCoerce(secondType, firstType)) { expressionCoercions.put(second, firstType); return firstType; } throw new SemanticException(TYPE_MISMATCH, node, message, firstType, secondType); }
private static List<Node> linearizeTree(Node tree) { ImmutableList.Builder<Node> nodes = ImmutableList.builder(); new DefaultTraversalVisitor<Node, Void>() { @Override public Node process(Node node, @Nullable Void context) { Node result = super.process(node, context); nodes.add(node); return result; } }.process(tree, null); return nodes.build(); }
@Override public Node visitSortItem(SqlBaseParser.SortItemContext context) { return new SortItem( getLocation(context), (Expression) visit(context.expression()), Optional.ofNullable(context.ordering) .map(AstBuilder::getOrderingType) .orElse(SortItem.Ordering.ASCENDING), Optional.ofNullable(context.nullOrdering) .map(AstBuilder::getNullOrderingType) .orElse(SortItem.NullOrdering.UNDEFINED)); }
@Override protected Node aggregateResult(Node aggregate, Node nextResult) { if (nextResult == null) { throw new UnsupportedOperationException("not yet implemented"); } if (aggregate == null) { return nextResult; } throw new UnsupportedOperationException("not yet implemented"); }
private Node invokeParser(String name, String sql, Function<SqlBaseParser, ParserRuleContext> parseFunction) { try { SqlBaseLexer lexer = new SqlBaseLexer(new CaseInsensitiveStream(new ANTLRInputStream(sql))); CommonTokenStream tokenStream = new CommonTokenStream(lexer); SqlBaseParser parser = new SqlBaseParser(tokenStream); parser.addParseListener(new PostProcessor()); lexer.removeErrorListeners(); lexer.addErrorListener(ERROR_LISTENER); parser.removeErrorListeners(); parser.addErrorListener(ERROR_LISTENER); ParserRuleContext tree; try { // first, try parsing with potentially faster SLL mode parser.getInterpreter().setPredictionMode(PredictionMode.SLL); tree = parseFunction.apply(parser); } catch (ParseCancellationException ex) { // if we fail, parse with LL mode tokenStream.reset(); // rewind input stream parser.reset(); parser.getInterpreter().setPredictionMode(PredictionMode.LL); tree = parseFunction.apply(parser); } return new AstBuilder().visit(tree); } catch (StackOverflowError e) { throw new ParsingException(name + " is too large (stack overflow while parsing)"); } }
@Override public Node visitSearchedCase(SqlBaseParser.SearchedCaseContext context) { return new SearchedCaseExpression( getLocation(context), visit(context.whenClause(), WhenClause.class), visitIfPresent(context.elseExpression, Expression.class)); }
@Override public Node visitInsertInto(SqlBaseParser.InsertIntoContext context) { return new Insert( getQualifiedName(context.qualifiedName()), Optional.ofNullable(getColumnAliases(context.columnAliases())), (Query) visit(context.query())); }
@Override public Node visitTypeConstructor(SqlBaseParser.TypeConstructorContext context) { String type = context.identifier().getText(); String value = unquote(context.STRING().getText()); if (type.equalsIgnoreCase("time")) { return new TimeLiteral(getLocation(context), value); } if (type.equalsIgnoreCase("timestamp")) { return new TimestampLiteral(getLocation(context), value); } return new GenericLiteral(getLocation(context), type, value); }
@Override public Node visitSimpleCase(SqlBaseParser.SimpleCaseContext context) { return new SimpleCaseExpression( getLocation(context), (Expression) visit(context.valueExpression()), visit(context.whenClause(), WhenClause.class), visitIfPresent(context.elseExpression, Expression.class)); }
@Override public Node visitCall(SqlBaseParser.CallContext context) { return new Call( getLocation(context), getQualifiedName(context.qualifiedName()), visit(context.callArgument(), CallArgument.class)); }
@Override public Node visitQuery(SqlBaseParser.QueryContext context) { Query body = (Query) visit(context.queryNoWith()); return new Query( getLocation(context), visitIfPresent(context.with(), With.class), body.getQueryBody(), body.getOrderBy(), body.getLimit(), body.getApproximate()); }
@Override public Node visitRollup(SqlBaseParser.RollupContext context) { return new Rollup(getLocation(context), context.qualifiedName().stream() .map(AstBuilder::getQualifiedName) .collect(toList())); }
@Override public Node visitCube(SqlBaseParser.CubeContext context) { return new Cube(getLocation(context), context.qualifiedName().stream() .map(AstBuilder::getQualifiedName) .collect(toList())); }
@Override public Node visitLambda(SqlBaseParser.LambdaContext context) { List<String> arguments = context.identifier().stream() .map(SqlBaseParser.IdentifierContext::getText) .collect(toList()); Expression body = (Expression) visit(context.expression()); return new LambdaExpression(arguments, body); }
@Override public Node visitExplainFormat(SqlBaseParser.ExplainFormatContext context) { switch (context.value.getType()) { case SqlBaseLexer.GRAPHVIZ: return new ExplainFormat(getLocation(context), ExplainFormat.Type.GRAPHVIZ); case SqlBaseLexer.TEXT: return new ExplainFormat(getLocation(context), ExplainFormat.Type.TEXT); } throw new IllegalArgumentException("Unsupported EXPLAIN format: " + context.value.getText()); }
@Override public Node visitExplainType(SqlBaseParser.ExplainTypeContext context) { switch (context.value.getType()) { case SqlBaseLexer.LOGICAL: return new ExplainType(getLocation(context), ExplainType.Type.LOGICAL); case SqlBaseLexer.DISTRIBUTED: return new ExplainType(getLocation(context), ExplainType.Type.DISTRIBUTED); } throw new IllegalArgumentException("Unsupported EXPLAIN type: " + context.value.getText()); }
@Override public Node visitShowTables(SqlBaseParser.ShowTablesContext context) { return new ShowTables( getLocation(context), Optional.ofNullable(context.qualifiedName()) .map(AstBuilder::getQualifiedName), getTextIfPresent(context.pattern) .map(AstBuilder::unquote)); }