forked from prestodb/presto
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
333 additions
and
0 deletions.
There are no files selected for viewing
144 changes: 144 additions & 0 deletions
144
src/main/java/com/facebook/presto/sql/ExpressionFormatter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
package com.facebook.presto.sql; | ||
|
||
import com.facebook.presto.sql.tree.AliasedExpression; | ||
import com.facebook.presto.sql.tree.AllColumns; | ||
import com.facebook.presto.sql.tree.ArithmeticExpression; | ||
import com.facebook.presto.sql.tree.AstVisitor; | ||
import com.facebook.presto.sql.tree.ComparisonExpression; | ||
import com.facebook.presto.sql.tree.DoubleLiteral; | ||
import com.facebook.presto.sql.tree.Expression; | ||
import com.facebook.presto.sql.tree.FunctionCall; | ||
import com.facebook.presto.sql.tree.LikePredicate; | ||
import com.facebook.presto.sql.tree.LogicalBinaryExpression; | ||
import com.facebook.presto.sql.tree.LongLiteral; | ||
import com.facebook.presto.sql.tree.Node; | ||
import com.facebook.presto.sql.tree.QualifiedNameReference; | ||
import com.facebook.presto.sql.tree.StringLiteral; | ||
import com.google.common.base.Function; | ||
import com.google.common.base.Joiner; | ||
import com.google.common.collect.Iterables; | ||
|
||
public class ExpressionFormatter | ||
{ | ||
public static String toString(Expression expression) | ||
{ | ||
return new Formatter().process(expression, null); | ||
} | ||
|
||
public static Function<Expression, String> expressionFormatterFunction() | ||
{ | ||
return new Function<Expression, String>() | ||
{ | ||
@Override | ||
public String apply(Expression input) | ||
{ | ||
return ExpressionFormatter.toString(input); | ||
} | ||
}; | ||
} | ||
|
||
private static class Formatter | ||
extends AstVisitor<String, Void> | ||
{ | ||
@Override | ||
protected String visitNode(Node node, Void context) | ||
{ | ||
throw new UnsupportedOperationException(); | ||
} | ||
|
||
@Override | ||
protected String visitExpression(Expression node, Void context) | ||
{ | ||
throw new UnsupportedOperationException("not yet implemented"); | ||
} | ||
|
||
@Override | ||
protected String visitStringLiteral(StringLiteral node, Void context) | ||
{ | ||
return "'" + node.getValue() + "'"; | ||
} | ||
|
||
@Override | ||
protected String visitLongLiteral(LongLiteral node, Void context) | ||
{ | ||
return node.getValue(); | ||
} | ||
|
||
@Override | ||
protected String visitDoubleLiteral(DoubleLiteral node, Void context) | ||
{ | ||
return node.getValue(); | ||
} | ||
|
||
@Override | ||
protected String visitQualifiedNameReference(QualifiedNameReference node, Void context) | ||
{ | ||
return node.getName().toString(); | ||
} | ||
|
||
@Override | ||
protected String visitAliasedExpression(AliasedExpression node, Void context) | ||
{ | ||
return ExpressionFormatter.toString(node.getExpression()) + ' ' + node.getAlias(); | ||
} | ||
|
||
@Override | ||
protected String visitFunctionCall(FunctionCall node, Void context) | ||
{ | ||
return node.getName() + "(" + Joiner.on(", ").join(Iterables.transform(node.getArguments(), expressionFormatterFunction())) + ")"; | ||
} | ||
|
||
@Override | ||
protected String visitLogicalBinaryExpression(LogicalBinaryExpression node, Void context) | ||
{ | ||
return formatBinaryExpression(node.getType().toString(), node.getLeft(), node.getRight()); | ||
} | ||
|
||
@Override | ||
protected String visitComparisonExpression(ComparisonExpression node, Void context) | ||
{ | ||
return formatBinaryExpression(node.getType().getValue(), node.getLeft(), node.getRight()); | ||
} | ||
|
||
@Override | ||
protected String visitArithmeticExpression(ArithmeticExpression node, Void context) | ||
{ | ||
return formatBinaryExpression(node.getType().getValue(), node.getLeft(), node.getRight()); | ||
} | ||
|
||
@Override | ||
protected String visitLikePredicate(LikePredicate node, Void context) | ||
{ | ||
StringBuilder builder = new StringBuilder(); | ||
|
||
builder.append('(') | ||
.append(ExpressionFormatter.toString(node.getValue())) | ||
.append(" LIKE ") | ||
.append(ExpressionFormatter.toString(node.getPattern())); | ||
|
||
if (node.getEscape() != null) { | ||
builder.append(" ESCAPE ") | ||
.append(ExpressionFormatter.toString(node.getEscape())); | ||
} | ||
|
||
builder.append(')'); | ||
|
||
return builder.toString(); | ||
} | ||
|
||
@Override | ||
protected String visitAllColumns(AllColumns node, Void context) | ||
{ | ||
if (node.getPrefix().isPresent()) { | ||
return node.getPrefix().get() + ".*"; | ||
} | ||
|
||
return "*"; | ||
} | ||
|
||
private String formatBinaryExpression(String operator, Expression left, Expression right) | ||
{ | ||
return '(' + ExpressionFormatter.toString(left) + ' ' + operator + ' ' + ExpressionFormatter.toString(right) + ')'; | ||
} | ||
} | ||
} |
189 changes: 189 additions & 0 deletions
189
src/main/java/com/facebook/presto/sql/SqlFormatter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,189 @@ | ||
package com.facebook.presto.sql; | ||
|
||
import com.facebook.presto.sql.tree.AliasedRelation; | ||
import com.facebook.presto.sql.tree.DefaultTraversalVisitor; | ||
import com.facebook.presto.sql.tree.Node; | ||
import com.facebook.presto.sql.tree.Query; | ||
import com.facebook.presto.sql.tree.Relation; | ||
import com.facebook.presto.sql.tree.Select; | ||
import com.facebook.presto.sql.tree.SortItem; | ||
import com.facebook.presto.sql.tree.Subquery; | ||
import com.facebook.presto.sql.tree.Table; | ||
import com.google.common.base.Function; | ||
import com.google.common.base.Joiner; | ||
import com.google.common.base.Strings; | ||
import com.google.common.collect.Iterables; | ||
|
||
import java.util.Iterator; | ||
|
||
import static com.facebook.presto.sql.ExpressionFormatter.expressionFormatterFunction; | ||
|
||
public class SqlFormatter | ||
{ | ||
private static final String INDENT = " "; | ||
|
||
public static String toString(Node root) | ||
{ | ||
StringBuilder builder = new StringBuilder(); | ||
new Formatter(builder).process(root, 0); | ||
return builder.toString(); | ||
} | ||
|
||
private static class Formatter | ||
extends DefaultTraversalVisitor<Void, Integer> | ||
{ | ||
private final StringBuilder builder; | ||
|
||
public Formatter(StringBuilder builder) | ||
{ | ||
this.builder = builder; | ||
} | ||
|
||
@Override | ||
protected Void visitNode(Node node, Integer indent) | ||
{ | ||
throw new UnsupportedOperationException("not yet implemented: " + node); | ||
} | ||
|
||
@Override | ||
protected Void visitQuery(Query node, Integer indent) | ||
{ | ||
process(node.getSelect(), indent); | ||
|
||
append(indent, "FROM "); | ||
if (node.getFrom().size() > 1) { | ||
builder.append('\n'); | ||
Iterator<Relation> relations = node.getFrom().iterator(); | ||
while (relations.hasNext()) { | ||
process(relations.next(), indent); | ||
if (relations.hasNext()) { | ||
builder.append(", "); | ||
} | ||
} | ||
} | ||
else { | ||
process(Iterables.getOnlyElement(node.getFrom()), indent); | ||
} | ||
|
||
builder.append('\n'); | ||
|
||
if (node.getWhere() != null) { | ||
append(indent, "WHERE " + ExpressionFormatter.toString(node.getWhere())) | ||
.append('\n'); | ||
} | ||
|
||
if (!node.getGroupBy().isEmpty()) { | ||
append(indent, "GROUP BY " + Joiner.on(", ").join(Iterables.transform(node.getGroupBy(), expressionFormatterFunction()))) | ||
.append('\n'); | ||
} | ||
|
||
if (node.getHaving() != null) { | ||
append(indent, "HAVING " + ExpressionFormatter.toString(node.getHaving())) | ||
.append('\n'); | ||
} | ||
|
||
if (!node.getOrderBy().isEmpty()) { | ||
append(indent, "ORDER BY " + Joiner.on(", ").join(Iterables.transform(node.getOrderBy(), orderByFormatterFunction()))); | ||
} | ||
|
||
if (node.getLimit() != null) { | ||
append(indent, "LIMIT " + node.getLimit()); | ||
} | ||
|
||
return null; | ||
} | ||
|
||
@Override | ||
protected Void visitSelect(Select node, Integer indent) | ||
{ | ||
append(indent, "SELECT ") | ||
.append(Joiner.on(", ").join(Iterables.transform(node.getSelectItems(), expressionFormatterFunction()))) | ||
.append('\n'); | ||
|
||
return null; | ||
} | ||
|
||
@Override | ||
protected Void visitTable(Table node, Integer indent) | ||
{ | ||
builder.append(node.getName().toString()); | ||
return null; | ||
} | ||
|
||
@Override | ||
protected Void visitAliasedRelation(AliasedRelation node, Integer indent) | ||
{ | ||
if (node.getColumnNames() != null && !node.getColumnNames().isEmpty()) { | ||
throw new UnsupportedOperationException("not yet implemented: relation alias with column mappings"); // TODO | ||
} | ||
|
||
process(node.getRelation(), indent); | ||
|
||
builder.append(' ') | ||
.append(node.getAlias()); | ||
|
||
return null; | ||
} | ||
|
||
@Override | ||
protected Void visitSubquery(Subquery node, Integer indent) | ||
{ | ||
builder.append('(') | ||
.append('\n'); | ||
|
||
process(node.getQuery(), indent + 1); | ||
|
||
append(indent, ")"); | ||
|
||
return null; | ||
} | ||
|
||
private StringBuilder append(int indent, String value) | ||
{ | ||
return builder.append(Strings.repeat(INDENT, indent)) | ||
.append(value); | ||
} | ||
} | ||
|
||
private static Function<SortItem, String> orderByFormatterFunction() | ||
{ | ||
return new Function<SortItem, String>() | ||
{ | ||
@Override | ||
public String apply(SortItem input) | ||
{ | ||
StringBuilder builder = new StringBuilder(); | ||
|
||
builder.append(ExpressionFormatter.toString(input.getSortKey())) | ||
.append(' '); | ||
|
||
switch (input.getOrdering()) { | ||
case ASCENDING: | ||
builder.append("ASC"); | ||
break; | ||
case DESCENDING: | ||
builder.append("DESC"); | ||
break; | ||
default: | ||
throw new UnsupportedOperationException("unknown ordering: " + input.getOrdering()); | ||
} | ||
|
||
switch (input.getNullOrdering()) { | ||
case FIRST: | ||
builder.append(" NULLS FIRST"); | ||
break; | ||
case LAST: | ||
builder.append(" NULLS LAST"); | ||
break; | ||
case UNDEFINED: | ||
// no op | ||
break; | ||
default: | ||
throw new UnsupportedOperationException("unknown null ordering: " + input.getNullOrdering()); | ||
} | ||
|
||
return builder.toString(); | ||
} | ||
}; | ||
} | ||
} |