1. 什么是AST

AST是abstract syntax tree的缩写,也就是抽象语法树。和所有的Parser一样,Druid Parser会生成一个抽象语法树。

2. 在Druid SQL Parser中有哪些AST节点类型

在Druid中,AST节点类型主要包括SQLObject、SQLExpr、SQLStatement三种抽象类型。

  1. package com.alibaba.druid.sql.ast;
  2.  
  3. interface SQLObject {}
  4. interface SQLExpr extends SQLObject {}
  5. interface SQLStatement extends SQLObject {}
  6.  
  7. interface SQLTableSource extends SQLObject {}
  8. class SQLSelect extends SQLObject {}
  9. class SQLSelectQueryBlock extends SQLObject {}

2.1. 常用的SQLExpr有哪些

  1. package com.alibaba.druid.sql.ast.expr;
  2.  
  3. // SQLName是一种的SQLExpr的Expr,包括SQLIdentifierExpr、SQLPropertyExpr等
  4. public interface SQLName extends SQLExpr {}
  5.  
  6. // 例如 ID = 3 这里的ID是一个SQLIdentifierExpr
  7. class SQLIdentifierExpr implements SQLExpr, SQLName {
  8. String name;
  9. }
  10.  
  11. // 例如 A.ID = 3 这里的A.ID是一个SQLPropertyExpr
  12. class SQLPropertyExpr implements SQLExpr, SQLName {
  13. SQLExpr owner;
  14. String name;
  15. }
  16.  
  17. // 例如 ID = 3 这是一个SQLBinaryOpExpr
  18. // left是ID (SQLIdentifierExpr)
  19. // right是3 (SQLIntegerExpr)
  20. class SQLBinaryOpExpr implements SQLExpr {
  21. SQLExpr left;
  22. SQLExpr right;
  23. SQLBinaryOperator operator;
  24. }
  25.  
  26. // 例如 select * from where id = ?,这里的?是一个SQLVariantRefExpr,name是'?'
  27. class SQLVariantRefExpr extends SQLExprImpl {
  28. String name;
  29. }
  30.  
  31. // 例如 ID = 3 这里的3是一个SQLIntegerExpr
  32. public class SQLIntegerExpr extends SQLNumericLiteralExpr implements SQLValuableExpr {
  33. Number number;
  34.  
  35. // 所有实现了SQLValuableExpr接口的SQLExpr都可以直接调用这个方法求值
  36. @Override
  37. public Object getValue() {
  38. return this.number;
  39. }
  40. }
  41.  
  42. // 例如 NAME = 'jobs' 这里的'jobs'是一个SQLCharExpr
  43. public class SQLCharExpr extends SQLTextLiteralExpr implements SQLValuableExpr{
  44. String text;
  45. }

2.2. 常用的SQLStatemment

最常用的Statement当然是SELECT/UPDATE/DELETE/INSERT,他们分别是

  1. package com.alibaba.druid.sql.ast.statement;
  2.  
  3. class SQLSelectStatement implements SQLStatement {
  4. SQLSelect select;
  5. }
  6. class SQLUpdateStatement implements SQLStatement {
  7. SQLExprTableSource tableSource;
  8. List<SQLUpdateSetItem> items;
  9. SQLExpr where;
  10. }
  11. class SQLDeleteStatement implements SQLStatement {
  12. SQLTableSource tableSource;
  13. SQLExpr where;
  14. }
  15. class SQLInsertStatement implements SQLStatement {
  16. SQLExprTableSource tableSource;
  17. List<SQLExpr> columns;
  18. SQLSelect query;
  19. }

2.3. SQLTableSource

常见的SQLTableSource包括SQLExprTableSource、SQLJoinTableSource、SQLSubqueryTableSource、SQLWithSubqueryClause.Entry

  1. class SQLTableSourceImpl extends SQLObjectImpl implements SQLTableSource {
  2. String alias;
  3. }
  4.  
  5. // 例如 select * from emp where i = 3,这里的from emp是一个SQLExprTableSource
  6. // 其中expr是一个name=emp的SQLIdentifierExpr
  7. class SQLExprTableSource extends SQLTableSourceImpl {
  8. SQLExpr expr;
  9. }
  10.  
  11. // 例如 select * from emp e inner join org o on e.org_id = o.id
  12. // 其中left 'emp e' 是一个SQLExprTableSource,right 'org o'也是一个SQLExprTableSource
  13. // condition 'e.org_id = o.id'是一个SQLBinaryOpExpr
  14. class SQLJoinTableSource extends SQLTableSourceImpl {
  15. SQLTableSource left;
  16. SQLTableSource right;
  17. JoinType joinType; // INNER_JOIN/CROSS_JOIN/LEFT_OUTER_JOIN/RIGHT_OUTER_JOIN/...
  18. SQLExpr condition;
  19. }
  20.  
  21. // 例如 select * from (select * from temp) a,这里第一层from(...)是一个SQLSubqueryTableSource
  22. SQLSubqueryTableSource extends SQLTableSourceImpl {
  23. SQLSelect select;
  24. }
  25.  
  26. /*
  27. 例如
  28. WITH RECURSIVE ancestors AS (
  29. SELECT *
  30. FROM org
  31. UNION
  32. SELECT f.*
  33. FROM org f, ancestors a
  34. WHERE f.id = a.parent_id
  35. )
  36. SELECT *
  37. FROM ancestors;
  38.  
  39. 这里的ancestors AS (...) 是一个SQLWithSubqueryClause.Entry
  40. */
  41. class SQLWithSubqueryClause {
  42. static class Entry extends SQLTableSourceImpl {
  43. SQLSelect subQuery;
  44. }
  45. }

2.4. SQLSelect & SQLSelectQuery

SQLSelectStatement包含一个SQLSelect,SQLSelect包含一个SQLSelectQuery,都是组成的关系。SQLSelectQuery有主要的两个派生类,分别是SQLSelectQueryBlock和SQLUnionQuery。

  1. class SQLSelect extends SQLObjectImpl {
  2. SQLWithSubqueryClause withSubQuery;
  3. SQLSelectQuery query;
  4. }
  5.  
  6. interface SQLSelectQuery extends SQLObject {}
  7.  
  8. class SQLSelectQueryBlock implements SQLSelectQuery {
  9. List<SQLSelectItem> selectList;
  10. SQLTableSource from;
  11. SQLExprTableSource into;
  12. SQLExpr where;
  13. SQLSelectGroupByClause groupBy;
  14. SQLOrderBy orderBy;
  15. SQLLimit limit;
  16. }
  17.  
  18. class SQLUnionQuery implements SQLSelectQuery {
  19. SQLSelectQuery left;
  20. SQLSelectQuery right;
  21. SQLUnionOperator operator; // UNION/UNION_ALL/MINUS/INTERSECT
  22. }

2.5. SQLCreateTableStatement

建表语句包含了一系列方法,用于方便各种操作

  1. public class SQLCreateTableStatement extends SQLStatementImpl implements SQLDDLStatement, SQLCreateStatement {
  2. SQLExprTableSource tableSource;
  3. List<SQLTableElement> tableElementList;
  4. Select select;
  5.  
  6. // 忽略大小写的查找SQLCreateTableStatement中的SQLColumnDefinition
  7. public SQLColumnDefinition findColumn(String columName) {}
  8.  
  9. // 忽略大小写的查找SQLCreateTableStatement中的column关联的索引
  10. public SQLTableElement findIndex(String columnName) {}
  11.  
  12. // 是否外键依赖另外一个表
  13. public boolean isReferenced(String tableName) {}
  14. }

3. 怎样产生AST

3.1. 通过SQLUtils产生List<SQLStatement>

  1. import com.alibaba.druid.util.JdbcConstants;
  2.  
  3. String dbType = JdbcConstants.MYSQL;
  4. List<SQLStatement> statementList = SQLUtils.parseStatements(sql, dbType);

3.2. 通过SQLUtils产生SQLExpr

  1. String dbType = JdbcConstants.MYSQL;
  2. SQLExpr expr = SQLUtils.toSQLExpr("id=3", dbType);

4. 怎样打印AST节点

4.1. 通过SQLUtils工具类打印节点

  1. package com.alibaba.druid.sql;
  2.  
  3. public class SQLUtils {
  4. // 可以将SQLExpr/SQLStatement打印为String类型
  5. static String toSQLString(SQLObject sqlObj, String dbType);
  6.  
  7. // 可以将一个&lt;SQLStatement&gt;打印为String类型
  8. static String toSQLString(List<SQLStatement> statementList, String dbType);
  9. }

5. 如何自定义遍历AST节点

所有的AST节点都支持Visitor模式,需要自定义遍历逻辑,可以实现相应的ASTVisitorAdapter派生类,比如 https://github.com/alibaba/druid/wiki/SQL_Parser_Demo_visitor

6. 相关阅读