Hệ thống SQLASTVisitor trong Druid

Hệ thống SQLASTVisitor hoàn chỉnh

I. Kiến trúc cấp cao nhất (Druid 1.2.x)

SQLASTVisitor (interface định nghĩa tất cả phương thức visit)
   ↑ Implements
SQLASTVisitorAdapter (adapter trừu tượng: empty implementation, class cha quan trọng nhất)
   ↑ Extends
├─ SQLASTOutputVisitor        → Xuất SQL string
│   ├─ SQLFormatVisitor        → Định dạng SQL
│   └─ SQLParameterizedOutputVisitor → Thay thế bằng tham số
│
├─ SchemaStatVisitor           → Trích xuất bảng/cột/điều kiện/hàm (được dùng nhiều nhất)
│   ├─ MySqlSchemaStatVisitor
│   ├─ OracleSchemaStatVisitor
│   └─ ...các dialect khác
│
├─ SQLWallVisitor              → Phòng chống SQL injection, kiểm tra bảo mật
│   └─ MySqlWallVisitor etc.
│
└─ Visitor tùy chỉnh của bạn (kế thừa SQLASTVisitorAdapter)

1. SQLASTVisitor (Interface)

Package: com.alibaba.druid.sql.visitor.SQLASTVisitor

Công dụng: Định nghĩa tất cả các phương thức để duyệt qua AST:

  • visit(SQLSelectQueryBlock)
  • visit(SQLExprTableSource)
  • visit(SQLJoinTableSource)
  • visit(SQLSelectItem)
  • visit(SQLWhereClause)
  • visit(SQLBinaryOpExpr)
  • ... hàng trăm phương thức

Bạn sẽ không bao giờ implement trực tiếp interface này!

2. SQLASTVisitorAdapter (Class quan trọng nhất!)

Package: com.alibaba.druid.sql.visitor.SQLASTVisitorAdapter

Vị trí: Class cha thực sự của tất cả Visitor = Adapter với empty implementation = Bắt buộc phải extend khi viết Visitor tùy chỉnh

Đặc điểm:

  • Implements SQLASTVisitor
  • Tất cả phương thức default return true (tiếp tục duyệt các node con)
  • Bạn chỉ cần override những node quan tâm

II. Các class con quan trọng

1. SchemaStatVisitor (Được dùng nhiều nhất)

Công dụng

Trích xuất từ SQL:

  • Tất cả bảng
  • Tất cả cột
  • Tất cả điều kiện
  • Tất cả hàm
  • Data lineage

Tên class thực tế

SchemaStatVisitor
   ↑
MySqlSchemaStatVisitor (được sử dụng thực tế)

Thuộc tính quan trọng

Map<String, TableStat> tables;       // Bảng
Set<Column> columns;                 // Cột
List<Condition> conditions;          // Điều kiện
Set<String> functions;               // Hàm
Set<String> variables;               // Biến

Phương thức quan trọng

visitor.getTables()        // Lấy tất cả bảng
visitor.getColumns()       // Lấy tất cả cột
visitor.getConditions()    // Lấy điều kiện WHERE/JOIN
visitor.getFunctions()     // Lấy hàm

Ví dụ hoàn chỉnh

import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlSchemaStatVisitor;
import com.alibaba.druid.stat.TableStat;
import com.alibaba.druid.util.JdbcConstants;

public class StatVisitorDemo {
    public static void main(String[] args) {
        String sql = "SELECT u.id, u.username FROM users u JOIN department d ON u.dept_id=d.id WHERE u.age>20";

        SQLStatement stmt = SQLUtils.parseSingleStatement(sql, JdbcConstants.MYSQL);
        MySqlSchemaStatVisitor visitor = new MySqlSchemaStatVisitor();
        stmt.accept(visitor);

        // Bảng
        System.out.println("=== Tables ===");
        for (TableStat.Name name : visitor.getTables().keySet()) {
            System.out.println(name);
        }

        // Cột
        System.out.println("\n=== Columns ===");
        visitor.getColumns().forEach(System.out::println);

        // Điều kiện
        System.out.println("\n=== Conditions ===");
        visitor.getConditions().forEach(System.out::println);
    }
}

2. SQLASTOutputVisitor (Xuất SQL)

Công dụng

Chuyển AST thành chuỗi SQL = Implementation bên dưới của SQLUtils.toSQLString()

Class cha

SQLASTVisitorAdapter
   ↑
SQLASTOutputVisitor

Thuộc tính quan trọng

protected StringBuilder appender;
protected String dbType;
protected boolean prettyFormat;

Phương thức quan trọng

visit(...)           // Duyệt và xuất
print(String)        // Xuất chuỗi
println()            // Xuống dòng

Ví dụ

String sql = "select * from employees";
SQLStatement stmt = SQLUtils.parseSingleStatement(sql, JdbcConstants.MYSQL);

StringBuilder out = new StringBuilder();
SQLASTOutputVisitor visitor = new SQLASTOutputVisitor(out, JdbcConstants.MYSQL);

stmt.accept(visitor);
System.out.println(out);
// SELECT * FROM employees

3. SQLFormatVisitor (Định dạng SQL)

Công dụng

SQL美化, thụt lề, viết hoa keyword

Kế thừa

SQLASTOutputVisitor
   ↑
SQLFormatVisitor

Phương thức quan trọng

setUpperCase(boolean)     // Viết hoa keyword
setIndent(int)            // Thiết lập thụt lề

Ví dụ

String sql = "select id,name from employees where salary>5000";
SQLStatement stmt = SQLUtils.parseSingleStatement(sql, JdbcConstants.MYSQL);

StringBuilder sb = new StringBuilder();
SQLFormatVisitor visitor = new SQLFormatVisitor(sb, JdbcConstants.MYSQL);
visitor.setUpperCase(true);
visitor.setIndent(4);

stmt.accept(visitor);
System.out.println(sb);

Kết quả:

SELECT
    id,
    name
FROM employees
WHERE salary > 5000

4. SQLParameterizedOutputVisitor (Tham số hóa)

Công dụng

Chuyển giá trị thành ?

salary>5000 → salary>?

Ví dụ

String sql = "SELECT * FROM employees WHERE age>25 AND department_id=3";
SQLStatement stmt = SQLUtils.parseSingleStatement(sql, JdbcConstants.MYSQL);

StringBuilder sb = new StringBuilder();
SQLParameterizedOutputVisitor visitor = new SQLParameterizedOutputVisitor(sb, JdbcConstants.MYSQL);

stmt.accept(visitor);
System.out.println(sb);

Kết quả:

SELECT * FROM employees WHERE age>? AND department_id=?

5. SQLWallVisitor (Bảo mật SQL)

Công dụng

Kiểm tra SQL có chứa:

  • DROP
  • TRUNCATE
  • SQL injection độc hại
  • Vi phạm quyền

Ví dụ

SQLWallVisitor visitor = new MySqlWallVisitor();
stmt.accept(visitor);

if (visitor.getViolations().isEmpty()) {
    System.out.println("An toàn");
} else {
    System.out.println("Vi phạm: " + visitor.getViolations());
}

III. Tạo Visitor tùy chỉnh (Kế thừa SQLASTVisitorAdapter)

Bạn có thể lắng nghe:

  • Bảng
  • Cột
  • WHERE
  • JOIN
  • Hàm
  • ORDER BY
  • LIMIT
  • Subquery

Ví dụ: In tất cả bảng

import com.alibaba.druid.sql.ast.statement.SQLExprTableSource;
import com.alibaba.druid.sql.visitor.SQLASTVisitorAdapter;

public class TableLoggerVisitor extends SQLASTVisitorAdapter {
    @Override
    public boolean visit(SQLExprTableSource x) {
        System.out.println("Bảng: " + x.getExpr() + " | Alias: " + x.getAlias());
        return true;
    }
}

Sử dụng:

stmt.accept(new TableLoggerVisitor());

IV. Các node thường được lắng nghe nhất

// Bảng
@Override
public boolean visit(SQLExprTableSource x) { }

// JOIN
@Override
public boolean visit(SQLJoinTableSource x) { }

// Subquery
@Override
public boolean visit(SQLSubqueryTableSource x) { }

// Trường trong SELECT
@Override
public boolean visit(SQLSelectItem x) { }

// WHERE
@Override
public boolean visit(SQLWhereClause x) { }

// Biểu thức so sánh a=b, a>b
@Override
public boolean visit(SQLBinaryOpExpr x) { }

// Hàm
@Override
public boolean visit(SQLMethodInvokeExpr x) { }

// ORDER BY
@Override
public boolean visit(SQLOrderBy x) { }

// LIMIT
@Override
public boolean visit(SQLLimit x) { }

V. Tổng kết

1. Kiến trúc thực tế

SQLASTVisitor (interface)
   ↑
SQLASTVisitorAdapter (adapter bạn phải kế thừa)
   ↑
├─ SchemaStatVisitor       Trích xuất bảng/cột/điều kiện
├─ SQLASTOutputVisitor     Xuất SQL
├─ SQLFormatVisitor        Định dạng
├─ SQLParameterizedVisitor Tham số hóa
└─ SQLWallVisitor          Chống injection

2. Sử dụng trong 90% cases

  • Trích xuất bảng/cộtSchemaStatVisitor
  • Duyệt/rewrite tùy chỉnh → Kế thừa SQLASTVisitorAdapter
  • Xuất SQLSQLASTOutputVisitor
  • Định dạngSQLFormatVisitor
  • Chống injectionSQLWallVisitor

Thẻ: Druid sql visitor AST Database

Đăng vào ngày 9 tháng 6 lúc 02:16