001package gudusoft.gsqlparser.stmt;
002
003import gudusoft.gsqlparser.*;
004import gudusoft.gsqlparser.compiler.TFrame;
005import gudusoft.gsqlparser.compiler.TGlobalScope;
006import gudusoft.gsqlparser.compiler.TVariable;
007import gudusoft.gsqlparser.nodes.*;
008
009import java.util.ArrayList;
010import java.util.HashMap;
011import java.util.Stack;
012
013/**
014 * The EXECUTE IMMEDIATE statement builds and executes a dynamic SQL statement in
015 * a single operation.
016 */
017public class TExecImmeStmt extends TBlockSqlStatement {
018
019    private TExpression dynamicStringExpr = null;
020    private TBindArgumentList bindArguments = null;
021    private TExpressionList intoVariables = null;
022    private TObjectNameList returnNames = null;
023    private String dynamicSQL = null;
024    private TStatementList dynamicStatements = null;
025
026    public ArrayList<String> getEvaluatedDynamicSQLs() {
027        return evaluatedDynamicSQLs;
028    }
029
030    private ArrayList <String> evaluatedDynamicSQLs = new ArrayList<String>();
031
032    // Static parser instance for performance - reused across all dynamic SQL parsing
033    private static TGSqlParser sqlparser = null;
034    static {
035        sqlparser = new TGSqlParser(EDbVendor.dbvoracle);
036    }
037
038    /**
039     *
040     * @return sql statement instance that generated dynamically based on {@link #dynamicSQL}
041     */
042    public synchronized TStatementList getDynamicStatements() {
043        if (this.dynamicStatements != null) return this.dynamicStatements;
044        if (this.getDynamicSQL() == null) return null;
045
046        // Prepare parser for reuse by clearing cached state
047        // This resets vendorParser, sqlEnv, sqlfilename, and parsing options
048        sqlparser.prepareForReuse();
049        // Parse the dynamic SQL fragment as-is (no leading blank-line padding).
050        // The fragment's tokens are produced with coordinates relative to the
051        // fragment itself (first character at line 1). After parsing we shift
052        // every coordinate back to the position the fragment occupies in the
053        // original source file (see shiftDynamicCoordinates).
054        //
055        // Historically this method prepended (lineNo - 1) blank lines via
056        // TBaseType.stringBlock() so that the re-parsed tokens lined up with the
057        // file. That padding was capped at 1000 blank lines to avoid OOM, which
058        // produced wrong line numbers for any dynamic SQL located beyond line
059        // 1000 in the file (a fragment at file line 1709 was reported at 1005).
060        // Shifting coordinates after the fact is exact and allocates no padding.
061        sqlparser.sqltext = this.getDynamicSQL();
062        // Propagate the enclosing routine's PL/SQL scope (parameters and local
063        // variables) into the dynamic-SQL parse, mirroring how the function /
064        // procedure body re-parse does it (TCreateFunctionStmt /
065        // TCreateProcedureStmt call newParser.setFrameStack(getFrameStack())).
066        // Without this, an identifier inside the dynamic SQL that is actually a
067        // routine parameter/variable (e.g. WHERE col = P_MANAGER_CODE) cannot be
068        // located by linkColumnToTable -> locateVariableOrParameter and is wrongly
069        // reported as an orphan column. With the scope available it resolves to
070        // the variable instead.
071        //
072        // This statement's own frame stack is usually empty here (a routine
073        // declared inside a package body does not push its parameter scope onto
074        // the frame stack), so when it is empty we rebuild one from the
075        // parameters / local variables of every enclosing routine.
076        // Always install a definite frame stack: sqlparser is a reused static
077        // parser and prepareForReuse() does not clear the frame stack, so a
078        // previous routine's scope must not leak into a later no-scope parse.
079        Stack<TFrame> dynFrameStack = getFrameStack();
080        if (dynFrameStack == null || dynFrameStack.isEmpty()) {
081            Stack<TFrame> built = buildEnclosingRoutineFrameStack();
082            dynFrameStack = (built != null) ? built : new Stack<TFrame>();
083        }
084        sqlparser.setFrameStack(dynFrameStack);
085        int ret = sqlparser.parse();
086
087        if(this.getDynamicStringExpr().getPlainTextLineNo() != -1){
088            int deltaLine = (int) this.getDynamicStringExpr().getPlainTextLineNo() - 1;
089            int deltaColumn = (int) this.getDynamicStringExpr().getPlainTextColumnNo();
090            TReparseCoordinateShifter.shift(sqlparser, deltaLine, deltaColumn);
091        }
092
093        if ( ret != 0){
094            for(int j=0;j<sqlparser.getErrorCount();j++){
095                this.parseerrormessagehandle(sqlparser.getSyntaxErrors().get(j));
096            }
097
098            return null;
099        }
100
101        this.dynamicStatements = new TStatementList();
102        for(int i=0;i<sqlparser.sqlstatements.size();i++){
103            if (this.getParentStmt() == null){
104                sqlparser.sqlstatements.get(i).setParentStmt(this);
105            }else{
106                sqlparser.sqlstatements.get(i).setParentStmt(this.getParentStmt());
107            }
108
109            this.dynamicStatements.add(sqlparser.sqlstatements.get(i));
110        }
111        return dynamicStatements;
112    }
113
114    /**
115     * Build a PL/SQL variable scope (frame stack) from the parameters and local
116     * variable declarations of every routine enclosing this EXECUTE IMMEDIATE,
117     * found by walking the parent-statement chain.
118     * <p>
119     * When a function / procedure is declared inside a package body its
120     * parameter scope is not pushed onto the frame stack, so this statement's
121     * own {@link #getFrameStack()} is empty and the dynamic-SQL parse would
122     * report routine parameters/variables referenced in the dynamic SQL (e.g.
123     * {@code WHERE col = P_MANAGER_CODE}) as orphan columns. Re-creating the
124     * scope here lets {@code locateVariableOrParameter} resolve those names to
125     * variables instead. Returns {@code null} when no enclosing routine
126     * contributes any name (e.g. a top-level EXECUTE IMMEDIATE).
127     */
128    private Stack<TFrame> buildEnclosingRoutineFrameStack() {
129        TGlobalScope scope = null;
130        TCustomSqlStatement cur = this;
131        while (cur != null) {
132            if (cur instanceof TStoredProcedureSqlStatement) {
133                TStoredProcedureSqlStatement routine = (TStoredProcedureSqlStatement) cur;
134
135                TParameterDeclarationList params = routine.getParameterDeclarations();
136                if (params != null) {
137                    for (int i = 0; i < params.size(); i++) {
138                        TParameterDeclaration pd = params.getParameterDeclarationItem(i);
139                        if (pd != null && pd.getParameterName() != null) {
140                            if (scope == null) scope = new TGlobalScope();
141                            scope.addSymbol(new TVariable(pd.getParameterName(), pd));
142                        }
143                    }
144                }
145
146                TStatementList decls = routine.getDeclareStatements();
147                if (decls != null) {
148                    for (int i = 0; i < decls.size(); i++) {
149                        TCustomSqlStatement d = decls.get(i);
150                        if (d instanceof TVarDeclStmt && ((TVarDeclStmt) d).getElementName() != null) {
151                            if (scope == null) scope = new TGlobalScope();
152                            scope.addSymbol(new TVariable(((TVarDeclStmt) d).getElementName(), d));
153                        }
154                    }
155                }
156            }
157            cur = cur.getParentStmt();
158        }
159
160        if (scope == null) return null;
161        Stack<TFrame> frameStack = new Stack<TFrame>();
162        new TFrame(scope).pushMeToStack(frameStack);
163        return frameStack;
164    }
165
166    /**
167     *
168     * @return String representation of dynamic sql statement. if there is a variable in
169     * {@link #dynamicStringExpr},  value of this variable will be returned.
170     */
171
172   public String getDynamicSQL() {
173       if (!this.getEvaluatedDynamicSQLs().isEmpty()){
174           // 使用 TASTEvaluator 计算动态 SQL 后,将结果保存在 evaluatedDynamicSQLs 中,如果有值,采用这个更精确的 SQL 值
175           StringBuilder concatenatedSQL = new StringBuilder();
176
177           for (int i = 0; i < this.getEvaluatedDynamicSQLs().size(); i++) {
178               String sql = this.getEvaluatedDynamicSQLs().get(i);
179               concatenatedSQL.append(sql);
180               if (i < this.getEvaluatedDynamicSQLs().size() - 1) {
181                   concatenatedSQL.append(";");
182                   concatenatedSQL.append(TBaseType.newline);
183               }
184           }
185
186           this.dynamicSQL = concatenatedSQL.toString();
187       }
188           return this.dynamicSQL;
189
190//        if (this.dynamicSQL != null) return this.dynamicSQL;
191//
192//        if (dynamicStringExpr.getExpressionType() == EExpressionType.simple_constant_t)
193//        {
194//            this.dynamicSQL = TBaseType.getStringInsideLiteral(this.dynamicStringExpr.toString());
195//        }else if (dynamicStringExpr.getExpressionType() == EExpressionType.simple_object_name_t){
196//           // this is a variable, we have to search
197//            String lcvar = this.getDynamicStringExpr().toString();
198//            TCustomSqlStatement topsql = this.getTopStatement();
199//            if (topsql instanceof TBlockSqlStatement){
200//                TCustomSqlStatement stmt = null;
201//                for(int i=0;i< ((TBlockSqlStatement)topsql).getBodyStatements().size();i++){
202//                   stmt = ((TBlockSqlStatement)topsql).getBodyStatements().get(i);
203//                   if (stmt instanceof TAssignStmt){
204//                       if ( ((TAssignStmt)stmt).getLeft().toString().compareToIgnoreCase(lcvar) == 0 )
205//                       {
206//                           if (((TAssignStmt)stmt).getExpression().getExpressionType() == EExpressionType.simple_constant_t){
207//                             this.dynamicSQL  = ((TAssignStmt)stmt).getExpression().toString().substring(1,((TAssignStmt)stmt).getExpression().toString().length() - 1);
208//                           }else{
209//                               this.dynamicSQL  = null;
210//                           }
211//                           break;
212//                       }
213//                   }
214//                }
215//            }
216//           if ((this.getParentStmt() instanceof TBlockSqlStatement)&&(this.dynamicSQL  == null)){
217//               TBlockSqlStatement blockSqlStatement = (TBlockSqlStatement)this.getParentStmt();
218//               for(int i=0;i< blockSqlStatement.getBodyStatements().size();i++){
219//                   TCustomSqlStatement stmt = blockSqlStatement.getBodyStatements().get(i);
220//                   if (stmt instanceof TAssignStmt){
221//                       if ( ((TAssignStmt)stmt).getLeft().toString().compareToIgnoreCase(lcvar) == 0 )
222//                       {
223//                           if (((TAssignStmt)stmt).getExpression().getExpressionType() == EExpressionType.simple_constant_t){
224//                               this.dynamicSQL  = ((TAssignStmt)stmt).getExpression().toString().substring(1,((TAssignStmt)stmt).getExpression().toString().length() - 1);
225//                           }else{
226//                               this.dynamicSQL  = null;
227//                           }
228//                           break;
229//                       }
230//                   }
231//               }
232//           }
233//
234//        }else{
235//            calculateExprVisitor cv = new calculateExprVisitor();
236//            this.dynamicStringExpr.postOrderTraverse(cv);
237//            //this.dynamicStringExpr.evaluate();
238//
239//            this.dynamicSQL  = this.dynamicStringExpr.getPlainText();
240////            if (this.dynamicSQL.charAt(0) == '\''){
241////                this.dynamicSQL  = this.dynamicSQL.substring(1,this.dynamicSQL.toString().length() - 2);
242////            }
243//        }
244//        return this.dynamicSQL ;
245    }
246
247    /**
248     * bind arguments
249     * @return bind arguments in using clause.
250     */    
251    public TBindArgumentList getBindArguments() {
252        return bindArguments;
253    }
254
255    /**
256     * String expr
257     *
258     * @return   A string literal, string variable, or string expression that represents any SQL statement.
259     * this is the original string of dynamic sql statement.
260     */
261
262    public TExpression getDynamicStringExpr() {
263        return dynamicStringExpr;
264    }
265
266    /**
267     * Into variable
268     *
269     * @return variable names in the into clause.
270     */    
271    public TExpressionList getIntoVariables() {
272        return intoVariables;
273    }
274
275    /**
276     *
277     * Used if and only if dynamic_sql_stmt has a RETURNING INTO clause, this clause
278     * returns the column values of the rows affected by dynamic_sql_stmt, in either
279     * individual variables or records
280     *
281     * @return
282     */
283    
284    public TObjectNameList getReturnNames() {
285        return returnNames;
286    }
287
288    public TExecImmeStmt(EDbVendor dbvendor){
289        super(dbvendor);
290        sqlstatementtype = ESqlStatementType.sstplsql_execimmestmt ;
291        }
292
293    void buildsql() {
294    }
295
296    void clear() {
297    }
298
299    String getasprettytext() {
300        return "";
301    }
302
303    void iterate(TVisitorAbs pvisitor) {
304    }
305
306    public int doParseStatement(TCustomSqlStatement psql) {
307        if (rootNode == null) return -1;
308        super.doParseStatement(psql);
309
310        TExecImmeNode execImmeNode = (TExecImmeNode)rootNode;
311        this.bindArguments = execImmeNode.getBindArguments();
312        this.intoVariables = execImmeNode.getIntoVariables();
313        this.dynamicStringExpr = execImmeNode.getDynamicStringExpr();
314        this.returnNames = execImmeNode.getReturnNames();
315
316        this.dynamicStringExpr.evaluate(this.getFrameStack(),this);
317        this.dynamicSQL = this.dynamicStringExpr.getPlainText();
318
319        //TODO parse the dynamicSQL, 先不在这里解析动态 SQL, 考虑和 TASTEvaluator 结合
320        // getDynamicStatements();
321
322        return 0;
323    }
324
325    public void accept(TParseTreeVisitor v){
326        v.preVisit(this);
327        v.postVisit(this);
328    }
329
330    public void acceptChildren(TParseTreeVisitor v){
331        v.preVisit(this);
332        this.dynamicStringExpr.acceptChildren(v);
333        v.postVisit(this);
334    }
335
336    public void setDynamicStringExpr(TExpression dynamicStringExpr) {
337        this.dynamicStringExpr = dynamicStringExpr;
338    }
339
340    public void setBindArguments(TBindArgumentList bindArguments) {
341        this.bindArguments = bindArguments;
342    }
343
344    public void setIntoVariables(TExpressionList intoVariables) {
345        this.intoVariables = intoVariables;
346    }
347
348    public void setReturnNames(TObjectNameList returnNames) {
349        this.returnNames = returnNames;
350    }
351
352    public void setDynamicSQL(String dynamicSQL) {
353        this.dynamicSQL = dynamicSQL;
354    }
355
356    public void setDynamicStatements(TStatementList dynamicStatements) {
357        this.dynamicStatements = dynamicStatements;
358    }
359
360//    private String EvaluateExpr(){
361//        if (this.dynamicStringExpr == null) return "";
362//        return this.dynamicStringExpr.getPlainText();
363//    }
364}
365
366//class calculateExprVisitor implements IExpressionVisitor {
367//    Stack<TExpression> expressionStack = new Stack<>();
368//
369//    public boolean exprVisit(TParseTreeNode pNode,boolean isLeafNode){
370//        if (isLeafNode){
371//            expressionStack.push((TExpression)pNode);
372//        }
373//
374//        TExpression expr = (TExpression)pNode;
375//        switch (expr.getExpressionType()){
376//            case concatenate_t:
377//                TExpression expr1 = expressionStack.pop();
378//                TExpression expr2 = expressionStack.pop();
379//
380//                String expr1Str = expr1.getPlainText();
381//                String expr2Str = expr2.getPlainText();
382//                switch (expr1.getExpressionType()){
383//                    case simple_constant_t:
384//                        expr1Str = TBaseType.getStringInsideLiteral(expr1Str);
385//                        break;
386//                    case function_t:
387//                        expr1Str = expr1.getFunctionCall().getFunctionName().toString();
388//                        break;
389//                    case simple_object_name_t:
390//                        expr1Str = expr1Str.replace(".","_");
391//                        break;
392//                    default:
393//                        break;
394//                }
395//
396//                switch (expr2.getExpressionType()){
397//                    case simple_constant_t:
398//                        expr2Str = TBaseType.getStringInsideLiteral(expr2Str);
399//                        break;
400//                    case function_t:
401//                        expr2Str = expr2.getFunctionCall().getFunctionName().toString();
402//                        break;
403//                    case simple_object_name_t:
404//                        expr2Str = expr2Str.replace(".","_");
405//                        break;
406//                    default:
407//                        break;
408//                }
409//
410//
411//                //TExpression expr3 = expressionStack.peek();
412//                ((TExpression)pNode).setPlainText(expr2Str+expr1Str);
413//
414//                expressionStack.push((TExpression)pNode);
415//
416//                break;
417//        }
418//        return true;
419//    };
420//
421//}
422