001package gudusoft.gsqlparser.stmt.powerquery; 002 003import gudusoft.gsqlparser.EDbVendor; 004import gudusoft.gsqlparser.ESqlStatementType; 005import gudusoft.gsqlparser.TCustomSqlStatement; 006import gudusoft.gsqlparser.TSourceToken; 007import gudusoft.gsqlparser.TSourceTokenList; 008import gudusoft.gsqlparser.nodes.TParseTreeVisitor; 009import gudusoft.gsqlparser.nodes.powerquery.TPowerQueryLetExpr; 010import gudusoft.gsqlparser.parser.powerquery.PowerQueryDocumentParser; 011 012import java.util.ArrayList; 013import java.util.List; 014 015/** 016 * A parsed Power Query M document. 017 * 018 * <p>After parsing, {@link #getLetExpression()} returns the root step graph. 019 * Lineage extraction (inner SQL dispatch for {@code Value.NativeQuery}, 020 * navigation chain resolution) happens later in 021 * {@code TPowerQueryAnalyzer}, not here. This class is deliberately 022 * lightweight — it runs the M-language recognizer over the token list and 023 * stores the result. 024 * 025 * <p>Diagnostics for unsupported constructs land in {@link #getWarnings()}. 026 * Following the plan's "never silent wrong lineage" rule, parse errors on 027 * individual steps are recorded but do not fail the whole document. 028 */ 029public class TPowerQueryDocumentStmt extends TPowerQueryStmt { 030 031 private TPowerQueryLetExpr letExpression; 032 private final List<String> warnings = new ArrayList<>(); 033 034 public TPowerQueryDocumentStmt(EDbVendor dbvendor) { 035 super(dbvendor); 036 sqlstatementtype = ESqlStatementType.sstpowerquerydocument; 037 } 038 039 public TPowerQueryLetExpr getLetExpression() { 040 return letExpression; 041 } 042 043 public void setLetExpression(TPowerQueryLetExpr letExpression) { 044 this.letExpression = letExpression; 045 } 046 047 public List<String> getWarnings() { 048 return warnings; 049 } 050 051 public void addWarning(String warning) { 052 if (warning != null) warnings.add(warning); 053 } 054 055 @Override 056 public int doParseStatement(TCustomSqlStatement psql) { 057 super.doParseStatement(psql); 058 059 TSourceTokenList allTokens = this.sourcetokenlist; 060 if (allTokens == null || allTokens.size() == 0) { 061 return 0; 062 } 063 064 List<TSourceToken> stmtTokens = collectStatementTokens(); 065 if (stmtTokens.isEmpty()) { 066 return 0; 067 } 068 069 PowerQueryDocumentParser documentParser = new PowerQueryDocumentParser(stmtTokens); 070 this.letExpression = documentParser.parse(); 071 for (String w : documentParser.getWarnings()) { 072 addWarning(w); 073 } 074 return 0; 075 } 076 077 @Override 078 public void accept(TParseTreeVisitor v) { 079 v.preVisit(this); 080 v.postVisit(this); 081 } 082 083 @Override 084 public void acceptChildren(TParseTreeVisitor v) { 085 v.preVisit(this); 086 if (letExpression != null) letExpression.acceptChildren(v); 087 v.postVisit(this); 088 } 089 090 private List<TSourceToken> collectStatementTokens() { 091 List<TSourceToken> out = new ArrayList<>(); 092 if (sourcetokenlist == null) return out; 093 TSourceToken start = this.getStartToken(); 094 TSourceToken end = this.getEndToken(); 095 int startPos = start != null ? start.posinlist : 0; 096 int endPos = end != null ? end.posinlist : sourcetokenlist.size() - 1; 097 if (endPos < startPos) endPos = sourcetokenlist.size() - 1; 098 for (int i = startPos; i <= endPos && i < sourcetokenlist.size(); i++) { 099 TSourceToken t = sourcetokenlist.get(i); 100 if (t == null) continue; 101 out.add(t); 102 } 103 return out; 104 } 105}