[feature] (sql-digest) support sql digest (#8919)
This commit is contained in:
@ -838,6 +838,36 @@ public class AnalyticExpr extends Expr {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toDigestImpl() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(fnCall.toDigest()).append(" OVER (");
|
||||
boolean needsSpace = false;
|
||||
if (!partitionExprs.isEmpty()) {
|
||||
sb.append("PARTITION BY ").append(exprListToDigest(partitionExprs));
|
||||
needsSpace = true;
|
||||
}
|
||||
if (!orderByElements.isEmpty()) {
|
||||
List<String> orderByStrings = Lists.newArrayList();
|
||||
for (OrderByElement e : orderByElements) {
|
||||
orderByStrings.add(e.toDigest());
|
||||
}
|
||||
if (needsSpace) {
|
||||
sb.append(" ");
|
||||
}
|
||||
sb.append("ORDER BY ").append(Joiner.on(", ").join(orderByStrings));
|
||||
needsSpace = true;
|
||||
}
|
||||
if (window != null) {
|
||||
if (needsSpace) {
|
||||
sb.append(" ");
|
||||
}
|
||||
sb.append(window.toDigest());
|
||||
}
|
||||
sb.append(")");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String exprListToSql(List<? extends Expr> exprs) {
|
||||
if (exprs == null || exprs.isEmpty())
|
||||
return "";
|
||||
@ -847,4 +877,15 @@ public class AnalyticExpr extends Expr {
|
||||
}
|
||||
return Joiner.on(", ").join(strings);
|
||||
}
|
||||
|
||||
private String exprListToDigest(List<? extends Expr> exprs) {
|
||||
if (exprs == null || exprs.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
List<String> strings = Lists.newArrayList();
|
||||
for (Expr expr : exprs) {
|
||||
strings.add(expr.toDigest());
|
||||
}
|
||||
return Joiner.on(", ").join(strings);
|
||||
}
|
||||
}
|
||||
|
||||
@ -167,6 +167,17 @@ public class AnalyticWindow {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public String toDigest() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if (expr != null) {
|
||||
sb.append(expr.toDigest()).append(" ");
|
||||
}
|
||||
|
||||
sb.append(type.toString());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public TAnalyticWindowBoundary toThrift(Type windowType) {
|
||||
TAnalyticWindowBoundary result = new TAnalyticWindowBoundary(type.toThrift());
|
||||
|
||||
@ -296,6 +307,21 @@ public class AnalyticWindow {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public String toDigest() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(type_.toString()).append(" ");
|
||||
|
||||
if (rightBoundary_ == null) {
|
||||
sb.append(leftBoundary_.toDigest());
|
||||
} else {
|
||||
sb.append("BETWEEN ").append(leftBoundary_.toDigest()).append(" AND ");
|
||||
sb.append(rightBoundary_.toDigest());
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
public TAnalyticWindow toThrift() {
|
||||
TAnalyticWindow result = new TAnalyticWindow(type_.toThrift());
|
||||
|
||||
|
||||
@ -221,6 +221,15 @@ public class ArithmeticExpr extends Expr {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toDigestImpl() {
|
||||
if (children.size() == 1) {
|
||||
return op.toString() + " " + getChild(0).toDigest();
|
||||
} else {
|
||||
return getChild(0).toDigest() + " " + op.toString() + " " + getChild(1).toDigest();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void toThrift(TExprNode msg) {
|
||||
msg.node_type = TExprNodeType.ARITHMETIC_EXPR;
|
||||
|
||||
@ -72,6 +72,14 @@ public class ArrayLiteral extends LiteralExpr {
|
||||
return "ARRAY(" + StringUtils.join(list, ", ") + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toDigestImpl() {
|
||||
List<String> list = new ArrayList<>(children.size());
|
||||
children.forEach(v -> list.add(v.toDigestImpl()));
|
||||
|
||||
return "ARRAY(" + StringUtils.join(list, ", ") + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStringValue() {
|
||||
List<String> list = new ArrayList<>(children.size());
|
||||
|
||||
@ -96,7 +96,14 @@ public class BetweenPredicate extends Predicate {
|
||||
public String toSqlImpl() {
|
||||
String notStr = (isNotBetween) ? "NOT " : "";
|
||||
return children.get(0).toSql() + " " + notStr + "BETWEEN " +
|
||||
children.get(1).toSql() + " AND " + children.get(2).toSql();
|
||||
children.get(1).toSql() + " AND " + children.get(2).toSql();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toDigestImpl() {
|
||||
String notStr = (isNotBetween) ? "NOT " : "";
|
||||
return children.get(0).toDigest() + " " + notStr + "BETWEEN " +
|
||||
children.get(1).toDigest() + " AND " + children.get(2).toDigest();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -242,6 +242,11 @@ public class BinaryPredicate extends Predicate implements Writable {
|
||||
return getChild(0).toSql() + " " + op.toString() + " " + getChild(1).toSql();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toDigestImpl() {
|
||||
return getChild(0).toDigest() + " " + op.toString() + " " + getChild(1).toDigest();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void toThrift(TExprNode msg) {
|
||||
msg.node_type = TExprNodeType.BINARY_PRED;
|
||||
|
||||
@ -129,6 +129,24 @@ public class CaseExpr extends Expr {
|
||||
return output.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toDigestImpl() {
|
||||
StringBuilder sb = new StringBuilder("CASE");
|
||||
int childIdx = 0;
|
||||
if (hasCaseExpr) {
|
||||
sb.append(" ").append(children.get(childIdx++).toDigest());
|
||||
}
|
||||
while (childIdx + 2 <= children.size()) {
|
||||
sb.append(" WHEN ").append(children.get(childIdx++).toDigest());
|
||||
sb.append(" THEN ").append(children.get(childIdx++).toDigest());
|
||||
}
|
||||
if (hasElseExpr) {
|
||||
sb.append(" ELSE ").append(children.get(children.size() - 1).toDigest());
|
||||
}
|
||||
sb.append(" END");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVectorized() {
|
||||
return false;
|
||||
|
||||
@ -206,6 +206,27 @@ public class CastExpr extends Expr {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toDigestImpl() {
|
||||
boolean isVerbose = ConnectContext.get() != null &&
|
||||
ConnectContext.get().getExecutor() != null &&
|
||||
ConnectContext.get().getExecutor().getParsedStmt() != null &&
|
||||
ConnectContext.get().getExecutor().getParsedStmt().getExplainOptions() != null &&
|
||||
ConnectContext.get().getExecutor().getParsedStmt().getExplainOptions().isVerbose();
|
||||
if (isImplicit && !isVerbose) {
|
||||
return getChild(0).toDigest();
|
||||
}
|
||||
if (isAnalyzed) {
|
||||
if (type.isStringType()) {
|
||||
return "CAST(" + getChild(0).toDigest() + " AS " + "CHARACTER" + ")";
|
||||
} else {
|
||||
return "CAST(" + getChild(0).toDigest() + " AS " + type.toString() + ")";
|
||||
}
|
||||
} else {
|
||||
return "CAST(" + getChild(0).toDigest() + " AS " + targetTypeDef.toString() + ")";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void treeToThriftHelper(TExpr container) {
|
||||
if (noOp) {
|
||||
|
||||
@ -94,6 +94,15 @@ public class CompoundPredicate extends Predicate {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toDigestImpl() {
|
||||
if (children.size() == 1) {
|
||||
return "NOT " + getChild(0).toDigest();
|
||||
} else {
|
||||
return getChild(0).toDigest() + " " + op.toString() + " " + getChild(1).toDigest();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void toThrift(TExprNode msg) {
|
||||
msg.node_type = TExprNodeType.COMPOUND_PRED;
|
||||
|
||||
@ -58,6 +58,7 @@ public class ExistsPredicate extends Predicate {
|
||||
@Override
|
||||
public Expr clone() { return new ExistsPredicate(this); }
|
||||
|
||||
@Override
|
||||
public String toSqlImpl() {
|
||||
StringBuilder strBuilder = new StringBuilder();
|
||||
if (notExists) {
|
||||
@ -69,6 +70,18 @@ public class ExistsPredicate extends Predicate {
|
||||
return strBuilder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toDigestImpl() {
|
||||
StringBuilder strBuilder = new StringBuilder();
|
||||
if (notExists) {
|
||||
strBuilder.append("NOT ");
|
||||
|
||||
}
|
||||
strBuilder.append("EXISTS ");
|
||||
strBuilder.append(getChild(0).toDigest());
|
||||
return strBuilder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 31 * super.hashCode() + Boolean.hashCode(notExists);
|
||||
|
||||
@ -874,12 +874,25 @@ abstract public class Expr extends TreeNode<Expr> implements ParseNode, Cloneabl
|
||||
return (printSqlInParens) ? "(" + toSqlImpl() + ")" : toSqlImpl();
|
||||
}
|
||||
|
||||
public String toDigest() {
|
||||
return (printSqlInParens) ? "(" + toDigestImpl() + ")" : toDigestImpl();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a SQL string representing this expr. Subclasses should override this method
|
||||
* instead of toSql() to ensure that parenthesis are properly added around the toSql().
|
||||
*/
|
||||
protected abstract String toSqlImpl();
|
||||
|
||||
/**
|
||||
* !!!!!! Important !!!!!!
|
||||
* Subclasses should override this method if
|
||||
* sql digest should be represented different from tosqlImpl().
|
||||
*/
|
||||
protected String toDigestImpl() {
|
||||
return toSqlImpl();
|
||||
}
|
||||
|
||||
public String toMySql() {
|
||||
return toSql();
|
||||
}
|
||||
@ -952,6 +965,14 @@ abstract public class Expr extends TreeNode<Expr> implements ParseNode, Cloneabl
|
||||
// }
|
||||
// }
|
||||
|
||||
public List<String> childrenToDigest() {
|
||||
List<String> childrenDigestList = Lists.newArrayList();
|
||||
for (Expr child : children) {
|
||||
childrenDigestList.add(child.toDigest());
|
||||
}
|
||||
return childrenDigestList;
|
||||
}
|
||||
|
||||
public static com.google.common.base.Predicate<Expr> isAggregatePredicate() {
|
||||
return IS_AGGREGATE_PREDICATE;
|
||||
}
|
||||
|
||||
@ -193,6 +193,17 @@ public class FromClause implements ParseNode, Iterable<TableRef> {
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public String toDigest() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
if (!tableRefs_.isEmpty()) {
|
||||
builder.append(" FROM");
|
||||
for (int i = 0; i < tableRefs_.size(); ++i) {
|
||||
builder.append(" " + tableRefs_.get(i).toDigest());
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public boolean isEmpty() { return tableRefs_.isEmpty(); }
|
||||
|
||||
@Override
|
||||
|
||||
@ -292,6 +292,61 @@ public class FunctionCallExpr extends Expr {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String paramsToDigest() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("(");
|
||||
|
||||
if (fnParams.isStar()) {
|
||||
sb.append("*");
|
||||
}
|
||||
if (fnParams.isDistinct()) {
|
||||
sb.append("DISTINCT ");
|
||||
}
|
||||
int len = children.size();
|
||||
List<String> result = Lists.newArrayList();
|
||||
if (fnName.getFunction().equalsIgnoreCase("json_array") ||
|
||||
fnName.getFunction().equalsIgnoreCase("json_object")) {
|
||||
len = len - 1;
|
||||
}
|
||||
if (fnName.getFunction().equalsIgnoreCase("aes_decrypt") ||
|
||||
fnName.getFunction().equalsIgnoreCase("aes_encrypt") ||
|
||||
fnName.getFunction().equalsIgnoreCase("sm4_decrypt") ||
|
||||
fnName.getFunction().equalsIgnoreCase("sm4_encrypt")) {
|
||||
len = len - 1;
|
||||
}
|
||||
for (int i = 0; i < len; ++i) {
|
||||
if (i == 1 && (fnName.getFunction().equalsIgnoreCase("aes_decrypt") ||
|
||||
fnName.getFunction().equalsIgnoreCase("aes_encrypt") ||
|
||||
fnName.getFunction().equalsIgnoreCase("sm4_decrypt") ||
|
||||
fnName.getFunction().equalsIgnoreCase("sm4_encrypt"))) {
|
||||
result.add("\'***\'");
|
||||
} else {
|
||||
result.add(children.get(i).toDigest());
|
||||
}
|
||||
}
|
||||
sb.append(Joiner.on(", ").join(result)).append(")");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toDigestImpl() {
|
||||
Expr expr;
|
||||
if (originStmtFnExpr != null) {
|
||||
expr = originStmtFnExpr;
|
||||
} else {
|
||||
expr = this;
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(((FunctionCallExpr) expr).fnName);
|
||||
sb.append(paramsToDigest());
|
||||
if (fnName.getFunction().equalsIgnoreCase("json_quote") ||
|
||||
fnName.getFunction().equalsIgnoreCase("json_array") ||
|
||||
fnName.getFunction().equalsIgnoreCase("json_object")) {
|
||||
return forJSON(sb.toString());
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String debugString() {
|
||||
return MoreObjects.toStringHelper(this)/*.add("op", aggOp)*/.add("name", fnName).add("isStar",
|
||||
|
||||
@ -269,6 +269,19 @@ public class InPredicate extends Predicate {
|
||||
return strBuilder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toDigestImpl() {
|
||||
StringBuilder strBuilder = new StringBuilder();
|
||||
String notStr = (isNotIn) ? "NOT " : "";
|
||||
strBuilder.append(getChild(0).toDigest() + " " + notStr + "IN (");
|
||||
for (int i = 1; i < children.size(); ++i) {
|
||||
strBuilder.append(getChild(i).toDigest());
|
||||
strBuilder.append((i + 1 != children.size()) ? ", " : "");
|
||||
}
|
||||
strBuilder.append(")");
|
||||
return strBuilder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return toSql();
|
||||
|
||||
@ -463,4 +463,24 @@ public class InlineViewRef extends TableRef {
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String tableRefToDigest() {
|
||||
String aliasSql = null;
|
||||
String alias = getExplicitAlias();
|
||||
if (alias != null) {
|
||||
aliasSql = ToSqlUtils.getIdentSql(alias);
|
||||
}
|
||||
if (view != null) {
|
||||
return name.toSql() + (aliasSql == null ? "" : " " + aliasSql);
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder()
|
||||
.append("(")
|
||||
.append(queryStmt.toDigest())
|
||||
.append(") ")
|
||||
.append(aliasSql);
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@ -97,6 +97,11 @@ public class IsNullPredicate extends Predicate {
|
||||
return getChild(0).toSql() + (isNotNull ? " IS NOT NULL" : " IS NULL");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toDigestImpl() {
|
||||
return getChild(0).toDigest() + (isNotNull ? " IS NOT NULL" : " IS NULL");
|
||||
}
|
||||
|
||||
public boolean isSlotRefChildren() {
|
||||
return (children.get(0) instanceof SlotRef);
|
||||
}
|
||||
|
||||
@ -108,6 +108,12 @@ public class LikePredicate extends Predicate {
|
||||
return getChild(0).toSql() + " " + op.toString() + " " + getChild(1).toSql();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toDigestImpl() {
|
||||
return getChild(0).toDigest() + " " + op.toString() + " " + getChild(1).toDigest();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void toThrift(TExprNode msg) {
|
||||
msg.node_type = TExprNodeType.FUNCTION_CALL;
|
||||
|
||||
@ -96,6 +96,18 @@ public class LimitElement {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public String toDigest() {
|
||||
if (limit == -1) {
|
||||
return "";
|
||||
}
|
||||
StringBuilder sb = new StringBuilder(" LIMIT ");
|
||||
if (offset != 0) {
|
||||
sb.append(offset + "?, ");
|
||||
}
|
||||
sb.append("" + " ? ");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public void analyze(Analyzer analyzer) {
|
||||
if (limit == 0) analyzer.setHasEmptyResultSet();
|
||||
}
|
||||
|
||||
@ -198,6 +198,11 @@ public abstract class LiteralExpr extends Expr implements Comparable<LiteralExpr
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toDigestImpl() {
|
||||
return " ? ";
|
||||
}
|
||||
|
||||
// Swaps the sign of numeric literals.
|
||||
// Throws for non-numeric literals.
|
||||
public void swapSign() throws NotImplementedException {
|
||||
|
||||
@ -133,6 +133,22 @@ public class OrderByElement {
|
||||
return strBuilder.toString();
|
||||
}
|
||||
|
||||
public String toDigest() {
|
||||
StringBuilder strBuilder = new StringBuilder();
|
||||
strBuilder.append(expr.toDigest());
|
||||
strBuilder.append(isAsc ? " ASC" : " DESC");
|
||||
if (nullsFirstParam != null) {
|
||||
if (isAsc && nullsFirstParam) {
|
||||
// If ascending, nulls are last by default, so only add if nulls first.
|
||||
strBuilder.append(" NULLS FIRST");
|
||||
} else if (!isAsc && !nullsFirstParam) {
|
||||
// If descending, nulls are first by default, so only add if nulls last.
|
||||
strBuilder.append(" NULLS LAST");
|
||||
}
|
||||
}
|
||||
return strBuilder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return toSql();
|
||||
|
||||
@ -529,6 +529,15 @@ public class OutFileClause {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public String toDigest() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(" INTO OUTFILE '").append(" ? ").append(" FORMAT AS ").append(" ? ");
|
||||
if (properties != null && !properties.isEmpty()) {
|
||||
sb.append(" PROPERTIES(").append(" ? ").append(")");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public TResultFileSinkOptions toSinkOptions() {
|
||||
TResultFileSinkOptions sinkOptions = new TResultFileSinkOptions(filePath, fileFormatType);
|
||||
if (isCsvFormat()) {
|
||||
|
||||
@ -706,6 +706,10 @@ public abstract class QueryStmt extends StatementBase {
|
||||
return outFileClause != null ? outFileClause.clone() : null;
|
||||
}
|
||||
|
||||
public String toDigest() {
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* C'tor for cloning.
|
||||
*/
|
||||
|
||||
@ -97,6 +97,20 @@ public class SelectListItem {
|
||||
}
|
||||
}
|
||||
|
||||
public String toDigest() {
|
||||
if (!isStar) {
|
||||
Preconditions.checkNotNull(expr);
|
||||
String aliasSql = null;
|
||||
if (alias != null) {
|
||||
aliasSql = "`" + alias + "`";
|
||||
}
|
||||
return expr.toDigest() + ((aliasSql == null) ? "" : " " + aliasSql);
|
||||
} else if (tblName != null) {
|
||||
return tblName.toString() + ".*";
|
||||
} else {
|
||||
return "*";
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Return a column label for the select list item.
|
||||
*/
|
||||
|
||||
@ -1710,6 +1710,83 @@ public class SelectStmt extends QueryStmt {
|
||||
return strBuilder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toDigest() {
|
||||
StringBuilder strBuilder = new StringBuilder();
|
||||
if (withClause_ != null) {
|
||||
strBuilder.append(withClause_.toDigest());
|
||||
strBuilder.append(" ");
|
||||
}
|
||||
|
||||
// Select list
|
||||
strBuilder.append("SELECT ");
|
||||
if (selectList.isDistinct()) {
|
||||
strBuilder.append("DISTINCT ");
|
||||
}
|
||||
|
||||
if (originalExpr == null) {
|
||||
originalExpr = Expr.cloneList(resultExprs);
|
||||
}
|
||||
|
||||
if (resultExprs.isEmpty()) {
|
||||
for (int i = 0; i < selectList.getItems().size(); ++i) {
|
||||
if (i != 0) {
|
||||
strBuilder.append(", ");
|
||||
}
|
||||
strBuilder.append(selectList.getItems().get(i).toDigest());
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < originalExpr.size(); ++i) {
|
||||
if (i != 0) {
|
||||
strBuilder.append(", ");
|
||||
}
|
||||
strBuilder.append(originalExpr.get(i).toDigest());
|
||||
strBuilder.append(" AS ").append(SqlUtils.getIdentSql(colLabels.get(i)));
|
||||
}
|
||||
}
|
||||
|
||||
// From clause
|
||||
if (!fromClause_.isEmpty()) {
|
||||
strBuilder.append(fromClause_.toDigest());
|
||||
}
|
||||
|
||||
// Where clause
|
||||
if (whereClause != null) {
|
||||
strBuilder.append(" WHERE ");
|
||||
strBuilder.append(whereClause.toDigest());
|
||||
}
|
||||
// Group By clause
|
||||
if (groupByClause != null) {
|
||||
strBuilder.append(" GROUP BY ");
|
||||
strBuilder.append(groupByClause.toSql());
|
||||
}
|
||||
// Having clause
|
||||
if (havingClause != null) {
|
||||
strBuilder.append(" HAVING ");
|
||||
strBuilder.append(havingClause.toDigest());
|
||||
}
|
||||
// Order By clause
|
||||
if (orderByElements != null) {
|
||||
strBuilder.append(" ORDER BY ");
|
||||
for (int i = 0; i < orderByElements.size(); ++i) {
|
||||
strBuilder.append(orderByElements.get(i).getExpr().toDigest());
|
||||
if (sortInfo != null) {
|
||||
strBuilder.append((sortInfo.getIsAscOrder().get(i)) ? " ASC" : " DESC");
|
||||
}
|
||||
strBuilder.append((i + 1 != orderByElements.size()) ? ", " : "");
|
||||
}
|
||||
}
|
||||
// Limit clause.
|
||||
if (hasLimitClause()) {
|
||||
strBuilder.append(limitElement.toDigest());
|
||||
}
|
||||
|
||||
if (hasOutFileClause()) {
|
||||
strBuilder.append(outFileClause.toDigest());
|
||||
}
|
||||
return strBuilder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* If the select statement has a sort/top that is evaluated, then the sort tuple
|
||||
* is materialized. Else, if there is aggregation then the aggregate tuple id is
|
||||
|
||||
@ -659,6 +659,57 @@ public class SetOperationStmt extends QueryStmt {
|
||||
return strBuilder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toDigest() {
|
||||
StringBuilder strBuilder = new StringBuilder();
|
||||
if (withClause_ != null) {
|
||||
strBuilder.append(withClause_.toDigest());
|
||||
strBuilder.append(" ");
|
||||
}
|
||||
|
||||
strBuilder.append(operands.get(0).getQueryStmt().toDigest());
|
||||
for (int i = 1; i < operands.size() - 1; ++i) {
|
||||
strBuilder.append(
|
||||
" " + operands.get(i).getOperation().toString() + " "
|
||||
+ ((operands.get(i).getQualifier() == Qualifier.ALL) ? "ALL " : ""));
|
||||
if (operands.get(i).getQueryStmt() instanceof SetOperationStmt) {
|
||||
strBuilder.append("(");
|
||||
}
|
||||
strBuilder.append(operands.get(i).getQueryStmt().toDigest());
|
||||
if (operands.get(i).getQueryStmt() instanceof SetOperationStmt) {
|
||||
strBuilder.append(")");
|
||||
}
|
||||
}
|
||||
// Determine whether we need parenthesis around the last Set operand.
|
||||
SetOperand lastOperand = operands.get(operands.size() - 1);
|
||||
QueryStmt lastQueryStmt = lastOperand.getQueryStmt();
|
||||
strBuilder.append(" " + lastOperand.getOperation().toString() + " "
|
||||
+ ((lastOperand.getQualifier() == Qualifier.ALL) ? "ALL " : ""));
|
||||
if (lastQueryStmt instanceof SetOperationStmt || ((hasOrderByClause() || hasLimitClause()) &&
|
||||
!lastQueryStmt.hasLimitClause() &&
|
||||
!lastQueryStmt.hasOrderByClause())) {
|
||||
strBuilder.append("(");
|
||||
strBuilder.append(lastQueryStmt.toDigest());
|
||||
strBuilder.append(")");
|
||||
} else {
|
||||
strBuilder.append(lastQueryStmt.toDigest());
|
||||
}
|
||||
// Order By clause
|
||||
if (hasOrderByClause()) {
|
||||
strBuilder.append(" ORDER BY ");
|
||||
for (int i = 0; i < orderByElements.size(); ++i) {
|
||||
strBuilder.append(orderByElements.get(i).getExpr().toDigest());
|
||||
strBuilder.append(orderByElements.get(i).getIsAsc() ? " ASC" : " DESC");
|
||||
strBuilder.append((i + 1 != orderByElements.size()) ? ", " : "");
|
||||
}
|
||||
}
|
||||
// Limit clause.
|
||||
if (hasLimitClause()) {
|
||||
strBuilder.append(limitElement.toDigest());
|
||||
}
|
||||
return strBuilder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayList<String> getColLabels() {
|
||||
Preconditions.checkState(operands.size() > 0);
|
||||
|
||||
@ -56,6 +56,11 @@ public class Subquery extends Expr {
|
||||
@Override
|
||||
public String toSqlImpl() { return "(" + stmt.toSql() + ")"; }
|
||||
|
||||
@Override
|
||||
public String toDigestImpl() {
|
||||
return "(" + stmt.toDigest() + ")";
|
||||
}
|
||||
|
||||
/**
|
||||
* C'tor that initializes a Subquery from a QueryStmt.
|
||||
*/
|
||||
|
||||
@ -649,6 +649,10 @@ public class TableRef implements ParseNode, Writable {
|
||||
return tblName;
|
||||
}
|
||||
|
||||
public String tableRefToDigest() {
|
||||
return tableRefToSql();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toSql() {
|
||||
if (joinOp == null) {
|
||||
@ -670,6 +674,26 @@ public class TableRef implements ParseNode, Writable {
|
||||
return output.toString();
|
||||
}
|
||||
|
||||
public String toDigest() {
|
||||
if (joinOp == null) {
|
||||
// prepend "," if we're part of a sequence of table refs w/o an
|
||||
// explicit JOIN clause
|
||||
return (leftTblRef != null ? ", " : "") + tableRefToDigest();
|
||||
}
|
||||
|
||||
StringBuilder output = new StringBuilder(" " + joinOpToSql() + " ");
|
||||
if (joinHints != null && !joinHints.isEmpty()) {
|
||||
output.append("[").append(Joiner.on(", ").join(joinHints)).append("] ");
|
||||
}
|
||||
output.append(tableRefToDigest()).append(" ");
|
||||
if (usingColNames != null) {
|
||||
output.append("USING (").append(Joiner.on(", ").join(usingColNames)).append(")");
|
||||
} else if (onClause != null) {
|
||||
output.append("ON ").append(onClause.toDigest());
|
||||
}
|
||||
return output.toString();
|
||||
}
|
||||
|
||||
public String getAlias() {
|
||||
if (!hasExplicitAlias()) {
|
||||
return name.toString();
|
||||
|
||||
@ -325,6 +325,44 @@ public class TimestampArithmeticExpr extends Expr {
|
||||
return strBuilder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toDigestImpl() {
|
||||
StringBuilder strBuilder = new StringBuilder();
|
||||
if (funcName != null) {
|
||||
if (funcName.equalsIgnoreCase("TIMESTAMPDIFF") || funcName.equalsIgnoreCase("TIMESTAMPADD")) {
|
||||
strBuilder.append(funcName).append("(");
|
||||
strBuilder.append(timeUnitIdent).append(", ");
|
||||
strBuilder.append(getChild(1).toDigest()).append(", ");
|
||||
strBuilder.append(getChild(0).toDigest()).append(")");
|
||||
return strBuilder.toString();
|
||||
}
|
||||
// Function-call like version.
|
||||
strBuilder.append(funcName).append("(");
|
||||
strBuilder.append(getChild(0).toDigest()).append(", ");
|
||||
strBuilder.append("INTERVAL ");
|
||||
strBuilder.append(getChild(1).toDigest());
|
||||
strBuilder.append(" ").append(timeUnitIdent);
|
||||
strBuilder.append(")");
|
||||
return strBuilder.toString();
|
||||
}
|
||||
if (intervalFirst) {
|
||||
// Non-function-call like version with interval as first operand.
|
||||
strBuilder.append("INTERVAL ");
|
||||
strBuilder.append(getChild(1).toDigest() + " ");
|
||||
strBuilder.append(timeUnitIdent);
|
||||
strBuilder.append(" ").append(op.toString()).append(" ");
|
||||
strBuilder.append(getChild(0).toDigest());
|
||||
} else {
|
||||
// Non-function-call like version with interval as second operand.
|
||||
strBuilder.append(getChild(0).toDigest());
|
||||
strBuilder.append(" " + op.toString() + " ");
|
||||
strBuilder.append("INTERVAL ");
|
||||
strBuilder.append(getChild(1).toDigest() + " ");
|
||||
strBuilder.append(timeUnitIdent);
|
||||
}
|
||||
return strBuilder.toString();
|
||||
}
|
||||
|
||||
// Time units supported in timestamp arithmetic.
|
||||
public enum TimeUnit {
|
||||
YEAR("YEAR"), // YEARS
|
||||
|
||||
@ -143,5 +143,20 @@ public class WithClause implements ParseNode {
|
||||
return "WITH " + Joiner.on(",").join(viewStrings);
|
||||
}
|
||||
|
||||
public String toDigest() {
|
||||
List<String> viewStrings = Lists.newArrayList();
|
||||
for (View view : views_) {
|
||||
// Enclose the view alias and explicit labels in quotes if Hive cannot parse it
|
||||
// without quotes. This is needed for view compatibility between Impala and Hive.
|
||||
String aliasSql = ToSqlUtils.getIdentSql(view.getName());
|
||||
if (view.hasColLabels()) {
|
||||
aliasSql += "(" + Joiner.on(", ").join(
|
||||
ToSqlUtils.getIdentSqlList(view.getOriginalColLabels())) + ")";
|
||||
}
|
||||
viewStrings.add(aliasSql + " AS (" + view.getQueryStmt().toDigest() + ")");
|
||||
}
|
||||
return "WITH " + Joiner.on(",").join(viewStrings);
|
||||
}
|
||||
|
||||
public List<View> getViews() { return views_; }
|
||||
}
|
||||
|
||||
@ -84,6 +84,8 @@ public class AuditEvent {
|
||||
public String sqlHash = "";
|
||||
@AuditField(value = "peakMemoryBytes")
|
||||
public long peakMemoryBytes = -1;
|
||||
@AuditField(value = "SqlDigest")
|
||||
public String sqlDigest = "";
|
||||
|
||||
public static class AuditEventBuilder {
|
||||
|
||||
@ -186,6 +188,11 @@ public class AuditEvent {
|
||||
return this;
|
||||
}
|
||||
|
||||
public AuditEventBuilder setSqlDigest(String sqlDigest) {
|
||||
auditEvent.sqlDigest = sqlDigest;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AuditEvent build() {
|
||||
return this.auditEvent;
|
||||
}
|
||||
|
||||
@ -19,6 +19,7 @@ package org.apache.doris.qe;
|
||||
|
||||
import org.apache.doris.analysis.InsertStmt;
|
||||
import org.apache.doris.analysis.KillStmt;
|
||||
import org.apache.doris.analysis.QueryStmt;
|
||||
import org.apache.doris.analysis.SqlParser;
|
||||
import org.apache.doris.analysis.SqlScanner;
|
||||
import org.apache.doris.analysis.StatementBase;
|
||||
@ -29,6 +30,7 @@ import org.apache.doris.catalog.Database;
|
||||
import org.apache.doris.catalog.Table;
|
||||
import org.apache.doris.cluster.ClusterNamespace;
|
||||
import org.apache.doris.common.AnalysisException;
|
||||
import org.apache.doris.common.Config;
|
||||
import org.apache.doris.common.DdlException;
|
||||
import org.apache.doris.common.ErrorCode;
|
||||
import org.apache.doris.common.ErrorReport;
|
||||
@ -133,6 +135,10 @@ public class ConnectProcessor {
|
||||
} else {
|
||||
// ok query
|
||||
MetricRepo.HISTO_QUERY_LATENCY.update(elapseMs);
|
||||
if (elapseMs > Config.qe_slow_log_ms) {
|
||||
String sqlDigest = DigestUtils.md5Hex(((QueryStmt) parsedStmt).toDigest());
|
||||
ctx.getAuditEventBuilder().setSqlDigest(sqlDigest);
|
||||
}
|
||||
}
|
||||
ctx.getAuditEventBuilder().setIsQuery(true);
|
||||
ctx.getQueryDetail().setEventTime(endTime);
|
||||
@ -183,7 +189,6 @@ public class ConnectProcessor {
|
||||
}
|
||||
String sqlHash = DigestUtils.md5Hex(originStmt);
|
||||
ctx.setSqlHash(sqlHash);
|
||||
|
||||
ctx.getAuditEventBuilder().reset();
|
||||
ctx.getAuditEventBuilder()
|
||||
.setTimestamp(System.currentTimeMillis())
|
||||
|
||||
@ -0,0 +1,142 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you 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.apache.doris.planner;
|
||||
|
||||
import org.apache.doris.analysis.CreateDbStmt;
|
||||
import org.apache.doris.analysis.CreateTableStmt;
|
||||
import org.apache.doris.catalog.Catalog;
|
||||
import org.apache.doris.qe.ConnectContext;
|
||||
import org.apache.doris.utframe.UtFrameUtils;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.UUID;
|
||||
|
||||
public class SqlDigestTest {
|
||||
|
||||
private static String runningDir = "fe/mocked/SqlDigestTest/" + UUID.randomUUID().toString() + "/";
|
||||
|
||||
private static ConnectContext connectContext;
|
||||
|
||||
@BeforeClass
|
||||
public static void setUp() throws Exception {
|
||||
UtFrameUtils.createDorisCluster(runningDir);
|
||||
connectContext = UtFrameUtils.createDefaultCtx();
|
||||
|
||||
String createDbStmtStr = "create database db1;";
|
||||
CreateDbStmt createDbStmt = (CreateDbStmt) UtFrameUtils.parseAndAnalyzeStmt(createDbStmtStr, connectContext);
|
||||
Catalog.getCurrentCatalog().createDb(createDbStmt);
|
||||
// 3. create table tbl1
|
||||
String createTblStmtStr = "create table db1.tbl1(k1 varchar(32), k2 varchar(32), k3 varchar(32), k4 int) "
|
||||
+ "AGGREGATE KEY(k1, k2,k3,k4) distributed by hash(k1) buckets 3 properties('replication_num' = '1');";
|
||||
CreateTableStmt createTableStmt = (CreateTableStmt) UtFrameUtils.parseAndAnalyzeStmt(createTblStmtStr, connectContext);
|
||||
Catalog.getCurrentCatalog().createTable(createTableStmt);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDown() {
|
||||
File file = new File(runningDir);
|
||||
file.delete();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWhere() throws Exception {
|
||||
String sql1 = "select k4 from tbl1 where k1 > 1";
|
||||
String sql2 = "select k4 from tbl1 where k1 > 5";
|
||||
String digest1 = UtFrameUtils.getStmtDigest(connectContext, sql1);
|
||||
String digest2 = UtFrameUtils.getStmtDigest(connectContext, sql2);
|
||||
Assert.assertEquals(digest1, digest2);
|
||||
|
||||
sql1 = "select k4 from tbl1 where k1 like 'xxx' ";
|
||||
sql2 = "select k4 from tbl1 where k1 like 'kkskkkkkkkkk' ";
|
||||
digest1 = UtFrameUtils.getStmtDigest(connectContext, sql1);
|
||||
digest2 = UtFrameUtils.getStmtDigest(connectContext, sql2);
|
||||
Assert.assertEquals(digest1, digest2);
|
||||
|
||||
|
||||
sql1 = "select k4 from tb1 where k1 < 2 and k2 > -1 ";
|
||||
sql2 = "select k4 from tb1 where k1 < 1000 and k2 > 100000 ";
|
||||
digest1 = UtFrameUtils.getStmtDigest(connectContext, sql1);
|
||||
digest2 = UtFrameUtils.getStmtDigest(connectContext, sql2);
|
||||
Assert.assertEquals(digest1, digest2);
|
||||
|
||||
sql1 = "select k4 from tb1 where k1 < 2 or k2 > -1 ";
|
||||
sql2 = "select k4 from tb1 where k1 < 3 or k2 > 100000 ";
|
||||
digest1 = UtFrameUtils.getStmtDigest(connectContext, sql1);
|
||||
digest2 = UtFrameUtils.getStmtDigest(connectContext, sql2);
|
||||
Assert.assertEquals(digest1, digest2);
|
||||
|
||||
sql1 = "select k4 from tb1 where not k1 < 2 ";
|
||||
sql2 = "select k4 from tb1 where not k1 < 3";
|
||||
digest1 = UtFrameUtils.getStmtDigest(connectContext, sql1);
|
||||
digest2 = UtFrameUtils.getStmtDigest(connectContext, sql2);
|
||||
Assert.assertEquals(digest1, digest2);
|
||||
|
||||
sql1 = "select k4 from tb1 where not k1 < 2 ";
|
||||
sql2 = "select k4 from tb1 where not k1 > 3";
|
||||
digest1 = UtFrameUtils.getStmtDigest(connectContext, sql1);
|
||||
digest2 = UtFrameUtils.getStmtDigest(connectContext, sql2);
|
||||
Assert.assertNotEquals(digest1, digest2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLimit() throws Exception {
|
||||
String sql1 = "select k4 from tb1 where k1 > 1 limit 1";
|
||||
String sql2 = "select k4 from tb1 where k1 > 5 limit 20";
|
||||
String digest1 = UtFrameUtils.getStmtDigest(connectContext, sql1);
|
||||
String digest2 = UtFrameUtils.getStmtDigest(connectContext, sql2);
|
||||
Assert.assertEquals(digest1, digest2);
|
||||
|
||||
sql1 = "select k4 from tb1 where k1 > 1 order by k1 limit 1";
|
||||
sql2 = "select k4 from tb1 where k1 > 5 order by k1 limit 20";
|
||||
digest1 = UtFrameUtils.getStmtDigest(connectContext, sql1);
|
||||
digest2 = UtFrameUtils.getStmtDigest(connectContext, sql2);
|
||||
Assert.assertEquals(digest1, digest2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFunction() throws Exception {
|
||||
String sql1 = "select substr(k4, 1, 2) from tb1 where k1 > 1 limit 1";
|
||||
String sql2 = "select substr(k4, 1, 5) from tb1 where k1 > 1 limit 1";
|
||||
String digest1 = UtFrameUtils.getStmtDigest(connectContext, sql1);
|
||||
String digest2 = UtFrameUtils.getStmtDigest(connectContext, sql2);
|
||||
Assert.assertEquals(digest1, digest2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testArithmetic() throws Exception {
|
||||
String sql1 = "select k1 + 1 from tb1";
|
||||
String sql2 = "select k1 + 2 from tb1";
|
||||
String digest1 = UtFrameUtils.getStmtDigest(connectContext, sql1);
|
||||
String digest2 = UtFrameUtils.getStmtDigest(connectContext, sql2);
|
||||
Assert.assertEquals(digest1, digest2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCaseWhen() throws Exception {
|
||||
String sql1 = "select k1+20, case k2 when k3 then 1 else 0 end from tbl1 where k4 is null";
|
||||
String sql2 = "select k1+20, case k2 when k3 then 1000 else 9999999 end from tbl1 where k4 is null";
|
||||
String digest1 = UtFrameUtils.getStmtDigest(connectContext, sql1);
|
||||
String digest2 = UtFrameUtils.getStmtDigest(connectContext, sql2);
|
||||
Assert.assertEquals(digest1, digest2);
|
||||
}
|
||||
}
|
||||
@ -19,6 +19,7 @@ package org.apache.doris.utframe;
|
||||
|
||||
import org.apache.doris.analysis.Analyzer;
|
||||
import org.apache.doris.analysis.ExplainOptions;
|
||||
import org.apache.doris.analysis.QueryStmt;
|
||||
import org.apache.doris.analysis.SqlParser;
|
||||
import org.apache.doris.analysis.SqlScanner;
|
||||
import org.apache.doris.analysis.StatementBase;
|
||||
@ -45,9 +46,12 @@ import org.apache.doris.utframe.MockedFrontend.EnvVarNotSetException;
|
||||
import org.apache.doris.utframe.MockedFrontend.FeStartException;
|
||||
import org.apache.doris.utframe.MockedFrontend.NotInitException;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
|
||||
import java.io.File;
|
||||
@ -297,5 +301,16 @@ public class UtFrameUtils {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static String getStmtDigest(ConnectContext connectContext, String originStmt) throws Exception {
|
||||
SqlScanner input =
|
||||
new SqlScanner(new StringReader(originStmt), connectContext.getSessionVariable().getSqlMode());
|
||||
SqlParser parser = new SqlParser(input);
|
||||
StatementBase statementBase = SqlParserUtils.getFirstStmt(parser);
|
||||
Preconditions.checkState(statementBase instanceof QueryStmt);
|
||||
QueryStmt queryStmt = (QueryStmt) statementBase;
|
||||
String digest = queryStmt.toDigest();
|
||||
return DigestUtils.md5Hex(digest);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user