@Test public void testWith() throws Exception { assertStatement("WITH a (t, u) AS (SELECT * FROM x), b AS (SELECT * FROM y) TABLE z", new Query(Optional.of(new With(false, ImmutableList.of( new WithQuery("a", simpleQuery(selectList(new AllColumns()), table(QualifiedName.of("x"))), ImmutableList.of("t", "u")), new WithQuery("b", simpleQuery(selectList(new AllColumns()), table(QualifiedName.of("y"))), null)))), new Table(QualifiedName.of("z")), ImmutableList.of(), Optional.<String>empty(), Optional.<Approximate>empty())); assertStatement("WITH RECURSIVE a AS (SELECT * FROM x) TABLE y", new Query(Optional.of(new With(true, ImmutableList.of( new WithQuery("a", simpleQuery(selectList(new AllColumns()), table(QualifiedName.of("x"))), null)))), new Table(QualifiedName.of("y")), ImmutableList.of(), Optional.<String>empty(), Optional.<Approximate>empty())); }
@Override protected Void visitQuery(Query node, Integer indent) { if (node.getWith().isPresent()) { With with = node.getWith().get(); append(indent, "WITH"); if (with.isRecursive()) { builder.append(" RECURSIVE"); } builder.append("\n "); Iterator<WithQuery> queries = with.getQueries().iterator(); while (queries.hasNext()) { WithQuery query = queries.next(); append(indent, query.getName()); query.getColumnNames().ifPresent(columnNames -> appendAliasColumns(builder, columnNames)); builder.append(" AS "); process(new TableSubquery(query.getQuery()), indent); builder.append('\n'); if (queries.hasNext()) { builder.append(", "); } } } processRelation(node.getQueryBody(), indent); if (node.getOrderBy().isPresent()) { append(indent, "ORDER BY " + formatSortItems(node.getOrderBy().get().getSortItems(), parameters, indent)) .append('\n'); } if (node.getLimit().isPresent()) { append(indent, "LIMIT " + node.getLimit().get()) .append('\n'); } return null; }
private void analyzeWith(Query node, AnalysisContext context) { // analyze WITH clause if (!node.getWith().isPresent()) { return; } With with = node.getWith().get(); if (with.isRecursive()) { throw new SemanticException(NOT_SUPPORTED, with, "Recursive WITH queries are not supported"); } for (WithQuery withQuery : with.getQueries()) { if (withQuery.getColumnNames() != null && !withQuery.getColumnNames().isEmpty()) { throw new SemanticException(NOT_SUPPORTED, withQuery, "Column alias not supported in WITH queries"); } Query query = withQuery.getQuery(); process(query, context); String name = withQuery.getName(); if (context.isNamedQueryDeclared(name)) { throw new SemanticException(DUPLICATE_RELATION, withQuery, "WITH query name '%s' specified more than once", name); } context.addNamedQuery(name, query); } }
@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 visitQueryNoWith(SqlBaseParser.QueryNoWithContext context) { QueryBody term = (QueryBody) visit(context.queryTerm()); if (term instanceof QuerySpecification) { // When we have a simple query specification // followed by order by limit, fold the order by and limit // clauses into the query specification (analyzer/planner // expects this structure to resolve references with respect // to columns defined in the query specification) QuerySpecification query = (QuerySpecification) term; return new Query( getLocation(context), Optional.<With>empty(), new QuerySpecification( getLocation(context), query.getSelect(), query.getFrom(), query.getWhere(), query.getGroupBy(), query.getHaving(), visit(context.sortItem(), SortItem.class), getTextIfPresent(context.limit)), ImmutableList.of(), Optional.<String>empty(), getTextIfPresent(context.confidence) .map(confidence -> new Approximate(getLocation(context), confidence))); } return new Query( getLocation(context), Optional.<With>empty(), term, visit(context.sortItem(), SortItem.class), getTextIfPresent(context.limit), getTextIfPresent(context.confidence) .map(confidence -> new Approximate(getLocation(context), confidence))); }
@Override protected Void visitQuery(Query node, Integer indent) { if (node.getWith().isPresent()) { With with = node.getWith().get(); append(indent, "WITH"); if (with.isRecursive()) { builder.append(" RECURSIVE"); } builder.append("\n "); Iterator<WithQuery> queries = with.getQueries().iterator(); while (queries.hasNext()) { WithQuery query = queries.next(); append(indent, query.getName()); appendAliasColumns(builder, query.getColumnNames()); builder.append(" AS "); process(new TableSubquery(query.getQuery()), indent); builder.append('\n'); if (queries.hasNext()) { builder.append(", "); } } } processRelation(node.getQueryBody(), indent); if (!node.getOrderBy().isEmpty()) { append(indent, "ORDER BY " + formatSortItems(node.getOrderBy())) .append('\n'); } if (node.getLimit().isPresent()) { append(indent, "LIMIT " + node.getLimit().get()) .append('\n'); } if (node.getApproximate().isPresent()) { String confidence = node.getApproximate().get().getConfidence(); append(indent, "APPROXIMATE AT " + confidence + " CONFIDENCE") .append('\n'); } return null; }
@Override public Node visitWith(SqlBaseParser.WithContext context) { return new With(getLocation(context), context.RECURSIVE() != null, visit(context.namedQuery(), WithQuery.class)); }