|
| 1 | +********************************* |
| 2 | +Migration to 5.0 |
| 3 | +********************************* |
| 4 | + |
| 5 | +The new JSQLParser 5 introduces API-breaking changes: |
| 6 | + |
| 7 | +1. **Dependency on Java 11 or newer** |
| 8 | + |
| 9 | +2. **Reworked AST Visitors** |
| 10 | + |
| 11 | + The AST Visitors have been reworked to pass a Generic Context from the Root Node down to the Leaves. |
| 12 | + |
| 13 | + .. code-block:: java |
| 14 | + :caption: Generic Interface |
| 15 | +
|
| 16 | + public interface SelectVisitor<T> { |
| 17 | +
|
| 18 | + <S> T visit(PlainSelect plainSelect, S context); |
| 19 | +
|
| 20 | + default void visit(PlainSelect plainSelect) { |
| 21 | + this.visit(plainSelect, null); |
| 22 | + } |
| 23 | +
|
| 24 | + } |
| 25 | +
|
| 26 | + .. code-block:: java |
| 27 | + :caption: Sample Implementation |
| 28 | +
|
| 29 | + public class JSQLColumnResolver |
| 30 | + implements SelectVisitor<JdbcResultSetMetaData>, FromItemVisitor<JdbcResultSetMetaData> { |
| 31 | +
|
| 32 | + @Override |
| 33 | + public <S> JdbcResultSetMetaData visit(PlainSelect select, S context) { |
| 34 | + if (context instanceof JdbcMetaData) { |
| 35 | + return visit(select, (JdbcMetaData) context); |
| 36 | + } |
| 37 | + return null; |
| 38 | + } |
| 39 | +
|
| 40 | + public JdbcResultSetMetaData visit(PlainSelect select, JdbcMetaData metaData) { |
| 41 | + JdbcResultSetMetaData resultSetMetaData = new JdbcResultSetMetaData(); |
| 42 | +
|
| 43 | + // Logic to retrieve the column information |
| 44 | + resultSetMetaData = getColumn(metaData, select.getFromItem(), select.getJoins()); |
| 45 | +
|
| 46 | + return resultSetMetaData; |
| 47 | + } |
| 48 | + } |
| 49 | +
|
| 50 | +3. **Generic Result from Leaves to Root** |
| 51 | + |
| 52 | + Node objects now return a Generic Result from the Leaves up to the Root. |
| 53 | + |
| 54 | + .. code-block:: java |
| 55 | + :caption: AST Node |
| 56 | +
|
| 57 | + public class PlainSelect extends Select { |
| 58 | + @Override |
| 59 | + public <T, S> T accept(SelectVisitor<T> selectVisitor, S context) { |
| 60 | + return selectVisitor.visit(this, context); |
| 61 | + } |
| 62 | + } |
| 63 | +
|
| 64 | +How is this useful? Consider resolving the `AllColumns` ``*`` or `AllTableColumns` ``t.*`` expressions to retrieve the actual column names. This process depends on the database's physical metadata and the context of the current scope, including virtual data frames (like sub-selects and with-clauses). |
| 65 | + |
| 66 | +Therefore, every branch of the AST must receive scoped metadata from its parent node. Each AST node must receive the resolved columns from its child nodes. A global result object (like the `StringBuilder` in the `DepParser` implementations) is inadequate. |
| 67 | + |
| 68 | +Alternatively, consider substituting `TimeValueKey` (``CURRENT_DATE``, ``CURRENT_TIME``, etc.) with actual date or time values. You can push a simple `Map` of key/value pairs down to the Expression Visitor: |
| 69 | + |
| 70 | + .. code-block:: java |
| 71 | + :caption: Expression Visitor |
| 72 | +
|
| 73 | + @Override |
| 74 | + public <S> StringBuilder visit(TimeKeyExpression expression, S context) { |
| 75 | + if (context instanceof Map) { |
| 76 | + return visit(expression, (Map<String, Object>) substitutions); |
| 77 | + } else { |
| 78 | + return expression.toString(); |
| 79 | + } |
| 80 | + } |
| 81 | +
|
| 82 | + public StringBuilder visit(TimeKeyExpression expression, Map<String, Object> substitutions) { |
| 83 | + // Remove possible trailing brackets "()" |
| 84 | + String value = expression.getStringValue().toUpperCase().replaceAll("[()]", ""); |
| 85 | +
|
| 86 | + if (substitutions.containsKey(value)) { |
| 87 | + // @todo: Cast Date/Time types |
| 88 | + return castDateTime(substitutions.get(value).toString()).accept(this, null); |
| 89 | + } else { |
| 90 | + return super.visit(expression, null); |
| 91 | + } |
| 92 | + } |
| 93 | +
|
| 94 | +Another advantage is parallel processing: Without relying on a global result object, the AST can be traversed in parallel (whereas it currently must be traversed strictly in serial). |
| 95 | + |
| 96 | +Finally, any child node can now know its parent and identify who called it. |
0 commit comments