001package gudusoft.gsqlparser;
002
003
004import gudusoft.gsqlparser.compiler.*;
005import gudusoft.gsqlparser.nodes.*;
006import gudusoft.gsqlparser.nodes.dax.TDaxFunction;
007import gudusoft.gsqlparser.nodes.teradata.THashByClause;
008import gudusoft.gsqlparser.sqlenv.TSQLEnv;
009import gudusoft.gsqlparser.sqlenv.TSQLFunction;
010import gudusoft.gsqlparser.stmt.*;
011import gudusoft.gsqlparser.stmt.dax.TDaxStmt;
012import gudusoft.gsqlparser.stmt.oracle.*;
013import gudusoft.gsqlparser.sqlenv.ESQLDataObjectType;
014import gudusoft.gsqlparser.util.OraclePseudoColumnUtil;
015import gudusoft.gsqlparser.util.SQLUtil;
016import gudusoft.gsqlparser.util.functionChecker;
017import gudusoft.gsqlparser.util.keywordChecker;
018
019import java.util.ArrayDeque;
020import java.util.ArrayList;
021import java.util.Deque;
022import java.util.Stack;
023import java.util.TreeMap;
024import java.security.MessageDigest;
025import java.security.NoSuchAlgorithmException;
026import java.nio.charset.StandardCharsets;
027
028/**
029 * TCustomSqlStatement is the root class for all SQL statements.
030 */
031public class TCustomSqlStatement extends TParseTreeNode implements IRelation{
032
033    private String sqlHash;
034
035    public void setSqlHash(String sqlHash) {
036        this.sqlHash = sqlHash;
037    }
038
039    /**
040     * Returns a stable, vendor-aware hash of this statement's SQL text for
041     * lineage grouping and statement identity.
042     * <p>
043     * Purpose:
044     * <ul>
045     *     <li>Provide a deterministic identifier for a statement that is
046     *     insensitive to formatting (whitespace, comments, keyword case).</li>
047     *     <li>Act as the first component of a recommended {@code statementKey}
048     *     for lineage grouping: {@code statementKey = sqlHash + "#" + queryId}.</li>
049     * </ul>
050     * How it works:
051     * <ul>
052     *     <li>Builds a normalized textual representation of the statement by
053     *     iterating the token chain between {@code getStartToken()} and
054     *     {@code getEndToken()}.</li>
055     *     <li>Normalization rules (Profile A by default): remove comments;
056     *     collapse spacing deterministically; uppercase keywords; handle
057     *     identifiers by the current vendor's case-sensitivity
058     *     ({@link gudusoft.gsqlparser.sqlenv.TSQLEnv#columnCollationCaseSensitive}).
059     *     For delimited identifiers (quoted identifiers), the quotes are
060     *     removed via {@link gudusoft.gsqlparser.TBaseType#removeQuoteChar(String)}
061     *     before case normalization. String literals are kept as-is.</li>
062     *     <li>The hash input is prefixed by a normalization version and the
063     *     current vendor, so future evolution of the normalizer will not break
064     *     previously computed values: {@code normVersion + "\n" + vendor + "\n" + normalizedSql}.</li>
065     *     <li>Hash function: SHA-256 (lowercase hex).</li>
066     * </ul>
067     * Usage:
068     * <ul>
069     *     <li>Call {@code getSqlHash(false)} for cached result or lazy
070     *     calculation with Profile A (identity-safe) normalization.</li>
071     *     <li>Call {@code getSqlHash(true)} to force recalculation (e.g., after
072     *     mutating the underlying token chain).</li>
073     *     <li>If you need a grouping-friendly variant (masking certain literal
074     *     classes), use {@link #computeSqlHash(SqlNormalizationProfile, String)}
075     *     with {@link SqlNormalizationProfile#GROUPING_FRIENDLY}.</li>
076     * </ul>
077     *
078     * @param forceReCalculate if true, recompute the hash even if a cached
079     *                         value exists
080     * @return lowercase hex SHA-256 hash of the normalized SQL text
081     */
082    public String getSqlHash(boolean forceReCalculate) {
083        if (sqlHash != null && !forceReCalculate) {
084            return sqlHash;
085        }
086        // Default: Profile A (identity-safe)
087        this.sqlHash = computeSqlHash(SqlNormalizationProfile.IDENTITY_SAFE, DEFAULT_SQLHASH_NORM_VERSION);
088        return this.sqlHash;
089    }
090
091    /**
092     * Backward compatible overload equivalent to {@code getSqlHash(false)}.
093     */
094    public String getSqlHash() {
095        return getSqlHash(false);
096    }
097
098    /**
099     * Normalization profiles for SQL hashing.
100     * <ul>
101     *     <li>IDENTITY_SAFE: Remove comments and normalize spacing/case only.
102     *     Literal values are preserved. Operator synonyms are minimally
103     *     unified (e.g., {@code !=} to {@code <>}).</li>
104     *     <li>GROUPING_FRIENDLY: In addition to IDENTITY_SAFE, date/time
105     *     string literals are normalized to {@code '1970-01-01'} to aid
106     *     grouping across different runs where only timestamps vary.</li>
107     * </ul>
108     */
109    public enum SqlNormalizationProfile {
110        IDENTITY_SAFE,
111        GROUPING_FRIENDLY
112    }
113
114    private static final String DEFAULT_SQLHASH_NORM_VERSION = "sqlHash.norm.v1";
115
116    /**
117     * Compute a SQL hash using the given normalization profile and version.
118     * See {@link #getSqlHash(boolean)} for details.
119     *
120     * @param profile     normalization profile
121     * @param normVersion version tag embedded in the hash input
122     * @return lowercase hex SHA-256 hash
123     */
124    public String computeSqlHash(SqlNormalizationProfile profile, String normVersion) {
125        String normalized = toNormalizedSql(profile);
126        String input = normVersion + "\n" + String.valueOf(this.dbvendor) + "\n" + normalized;
127        return sha256Hex(input);
128    }
129
130    /**
131     * Produce a normalized textual representation of this statement according
132     * to the supplied profile. The method is non-mutating: it does not alter
133     * token texts or statuses permanently.
134     *
135     * Rules applied:
136     * - Remove comments.
137     * - Remove trailing semicolon.
138     * - Deterministic spacing around punctuation/operators.
139     * - Uppercase keywords, keep string literals as-is.
140     * - Identifiers: if the vendor's column collation is case sensitive, keep
141     *   identifier case; otherwise uppercase. For quoted identifiers, remove
142     *   quoting via {@link TBaseType#removeQuoteChar(String)} prior to case
143     *   handling.
144     * - Minimal operator unification: {@code "!="} becomes {@code "<>"}.
145     * - Profile B adds date/time string masking: {@code '1970-01-01'}.
146     *
147     * @param profile normalization profile
148     * @return normalized SQL string
149     */
150    public String toNormalizedSql(SqlNormalizationProfile profile) {
151        TSourceToken start = getStartToken();
152        TSourceToken end = getEndToken();
153        if (start == null || end == null) {
154            return String.valueOf(this.toString());
155        }
156
157        boolean idCaseSensitive = Boolean.TRUE.equals(TSQLEnv.columnCollationCaseSensitive.get(this.dbvendor));
158
159        StringBuilder sb = new StringBuilder(256);
160        TokenClass prevClass = null;
161        char lastAppended = '\0';
162
163        // We may need to skip a single trailing semicolon that is the end token
164        // Detect once to simplify checks in the loop
165        boolean endIsSemicolon = (end.toString().equals(";"));
166
167        TSourceToken cur = start;
168        while (cur != null) {
169            // Skip tokens that should not contribute
170            if (isComment(cur)) {
171                // skip comments
172            } else if (cur == end && endIsSemicolon) {
173                // drop trailing semicolon
174            } else if (cur.tokenstatus == ETokenStatus.tsdeleted || cur.tokenstatus == ETokenStatus.tsignorebyyacc) {
175                // ignore deleted/ignored tokens
176            } else {
177                String text = normalizeTokenText(cur, idCaseSensitive, profile);
178                if (text != null && !text.isEmpty()) {
179                    TokenClass clazz = classify(cur, text);
180                    if (shouldAddSpaceBefore(prevClass, clazz, lastAppended)) {
181                        sb.append(' ');
182                        lastAppended = ' ';
183                    }
184                    sb.append(text);
185                    lastAppended = text.charAt(text.length() - 1);
186                    prevClass = clazz;
187                }
188            }
189
190            if (cur == end) {
191                break;
192            } else {
193                cur = cur.getNextTokenInChain();
194            }
195        }
196
197        // Trim any trailing single space for cleanliness
198        int len = sb.length();
199        if (len > 0 && sb.charAt(len - 1) == ' ') {
200            sb.setLength(len - 1);
201        }
202        return sb.toString();
203    }
204
205    private static boolean isComment(TSourceToken t) {
206        return t.tokentype == ETokenType.ttsimplecomment || t.tokentype == ETokenType.ttbracketedcomment;
207    }
208
209    private enum TokenClass { WORD, OP, DOT, COMMA, PAREN_LEFT, PAREN_RIGHT, SEMICOLON, OTHER }
210
211    private static TokenClass classify(TSourceToken t, String normalizedText) {
212        switch (t.tokentype) {
213            case ttidentifier:
214            case ttdqstring:
215            case ttdbstring:
216            case ttbrstring:
217            case ttnumber:
218            case ttsqstring:
219            case ttkeyword:
220            case ttnonreservedkeyword:
221            case ttbindvar:
222            case ttsqlvar:
223            case ttsubstitutionvar:
224                return TokenClass.WORD;
225            case ttperiod:
226                return TokenClass.DOT;
227            case ttcomma:
228                return TokenClass.COMMA;
229            case ttleftparenthesis:
230                return TokenClass.PAREN_LEFT;
231            case ttrightparenthesis:
232                return TokenClass.PAREN_RIGHT;
233            case ttsemicolon:
234            case ttsemicolon2:
235            case ttsemicolon3:
236                return TokenClass.SEMICOLON;
237            case ttequals:
238            case ttplussign:
239            case ttminussign:
240            case ttasterisk:
241            case ttslash:
242            case ttgreaterthan:
243            case ttlessthan:
244            case ttsinglecharoperator:
245            case ttmulticharoperator:
246            case ttconcatenationop:
247                return TokenClass.OP;
248            default:
249                break;
250        }
251        // Heuristic: treat UNKNOWN single-char punctuation as operator
252        if (normalizedText.length() == 1 && !Character.isLetterOrDigit(normalizedText.charAt(0))) {
253            return TokenClass.OP;
254        }
255        return TokenClass.OTHER;
256    }
257
258    private static boolean shouldAddSpaceBefore(TokenClass prev, TokenClass curr, char lastAppended) {
259        if (prev == null) return false;
260        if (lastAppended == '\0') return false;
261
262        // No space rules
263        if (curr == TokenClass.DOT || curr == TokenClass.COMMA || curr == TokenClass.PAREN_RIGHT) return false;
264        if (prev == TokenClass.DOT || prev == TokenClass.PAREN_LEFT) return false;
265
266        // Space around operators and between words
267        if (prev == TokenClass.OP && (curr == TokenClass.WORD || curr == TokenClass.PAREN_LEFT)) return true;
268        if ((prev == TokenClass.WORD || prev == TokenClass.PAREN_RIGHT) && (curr == TokenClass.OP || curr == TokenClass.WORD)) return true;
269
270        // After comma ensure a space before next word
271        if (prev == TokenClass.COMMA && (curr == TokenClass.WORD || curr == TokenClass.PAREN_LEFT)) return true;
272
273        // Default: no space
274        return false;
275    }
276
277    private String normalizeTokenText(TSourceToken t, boolean idCaseSensitive, SqlNormalizationProfile profile) {
278        String s = t.toString();
279
280        // Minimal operator unification: != => <>
281        if (t.tokentype == ETokenType.ttmulticharoperator || t.tokentype == ETokenType.ttsinglecharoperator ||
282            t.tokentype == ETokenType.ttgreaterthan || t.tokentype == ETokenType.ttlessthan) {
283            if ("!=".equals(s)) {
284                return "<>";
285            }
286            return s;
287        }
288
289        switch (t.tokentype) {
290            case ttsimplecomment:
291            case ttbracketedcomment:
292                return null; // removed
293            case ttkeyword:
294            case ttnonreservedkeyword:
295                return s.toUpperCase();
296            case ttidentifier: {
297                // regular identifier
298                return idCaseSensitive ? s : s.toUpperCase();
299            }
300            case ttdqstring:
301            case ttdbstring:
302            case ttbrstring: {
303                // delimited/quoted identifier -> remove quotes, then apply case rule
304                String unquoted = TBaseType.removeQuoteChar(s);
305                return idCaseSensitive ? unquoted : unquoted.toUpperCase();
306            }
307            case ttsqstring: {
308                if (profile == SqlNormalizationProfile.GROUPING_FRIENDLY && looksLikeDateOrTimestampLiteral(s)) {
309                    return "'1970-01-01'";
310                }
311                return s; // keep string literal as-is
312            }
313            default:
314                return s;
315        }
316    }
317
318    private static boolean looksLikeDateOrTimestampLiteral(String s) {
319        // Very lightweight check for common SQL string date/timestamp formats, e.g. '2025-09-14' or '2025-09-14 12:34:56'
320        // Input includes surrounding quotes per token text.
321        if (s == null || s.length() < 2) return false;
322        if (!(s.charAt(0) == '\'' && s.charAt(s.length() - 1) == '\'')) return false;
323        String inner = s.substring(1, s.length() - 1).trim();
324        // yyyy-mm-dd or yyyy-mm-dd hh:mm[:ss[.fff]]
325        if (inner.matches("\\d{4}-\\d{2}-\\d{2}")) return true;
326        if (inner.matches("\\d{4}-\\d{2}-\\d{2}[ T]\\d{2}:\\d{2}(:\\d{2}(\\.\\d{1,9})?)?")) return true;
327        return false;
328    }
329
330    private static String sha256Hex(String input) {
331        try {
332            MessageDigest md = MessageDigest.getInstance("SHA-256");
333            byte[] out = md.digest(input.getBytes(StandardCharsets.UTF_8));
334            char[] hex = new char[out.length * 2];
335            final char[] digits = "0123456789abcdef".toCharArray();
336            for (int i = 0, j = 0; i < out.length; i++) {
337                int b = out[i] & 0xFF;
338                hex[j++] = digits[(b >>> 4) & 0x0F];
339                hex[j++] = digits[b & 0x0F];
340            }
341            return new String(hex);
342        } catch (NoSuchAlgorithmException e) {
343            // Should never happen on a standard JVM
344            throw new RuntimeException("SHA-256 not available", e);
345        }
346    }
347
348    private String queryId;
349
350    public void setQueryId(String queryId) {
351        this.queryId = queryId;
352    }
353
354    /**
355     * Retrieves the unique and stable identifier for this SQL statement.
356     * <p>
357     * The queryId provides a reliable way to reference any statement, including subqueries,
358     * within a parsed SQL script. It is generated hierarchically based on the statement's
359     * position within the Abstract Syntax Tree (AST), ensuring that the ID is reproducible
360     * across identical SQL inputs.
361     * <p>
362     * <b>ID Format:</b>
363     * <ul>
364     *     <li>A top-level statement has an ID like {@code "stmt_0_select"}, where {@code 0} is the index
365     *         of the statement in the script and {@code select} is the statement type.</li>
366     *     <li>A nested statement will have a path-like ID that includes its parent's ID. For example,
367     *         an {@code INSERT} statement containing a {@code SELECT} subquery might have an ID for the
368     *         subquery like {@code "stmt_0_insert#stmt_1_select"}.</li>
369     * </ul>
370     * This identifier is particularly useful for tasks like data lineage analysis, where tracking
371     * the origin and transformation of data through various statements is required.
372     *
373     * @return The unique query identifier string for this statement, or {@code null} if it has not been set.
374     */
375    public String getQueryId() {
376        return queryId;
377    }
378
379    public void setUsingVariableList(TColumnDefinitionList usingVariableList) {
380        this.usingVariableList = usingVariableList;
381    }
382
383    private TColumnDefinitionList usingVariableList;
384
385    /*
386    * Variables defined in teradata using clause.
387    * */
388    public TColumnDefinitionList getUsingVariableList() {
389        return usingVariableList;
390    }
391
392    protected ArrayList<TAttributeNode> relationAttributes = new ArrayList<>();
393
394    @Override
395    public ArrayList<TAttributeNode> getAttributes(){
396       // if (relationAttributes.size() != 0) return relationAttributes;
397        relationAttributes.clear();
398        for(TTable table:relations){
399            //relationAttributes.addAll(table.getAttributes());
400            TAttributeNode.addAllNodesToList(table.getAttributes(),relationAttributes);
401        }
402
403        return relationAttributes;
404    }
405
406    @Override
407    public String getRelationName(){
408        return null;
409    }
410
411    @Override
412    public int size(){
413        return relationAttributes.size();
414    }
415
416    @Override
417    public String toScript(){
418        String ret = super.toScript();
419        if ((ret == null)||(ret.isEmpty())){
420            ret = this.toString();
421        }else{
422            if ((this.getEndToken() != null) && (this.getEndToken().tokencode == ';')) {
423                if (!ret.endsWith(";")){
424                    ret = ret + ";";
425                }
426            }
427        }
428        return ret;
429    }
430
431    protected TFromClause fromClause;
432
433    public void setFromClause(TFromClause fromClause) {
434        this.fromClause = fromClause;
435    }
436
437    public TFromClause getFromClause() {
438        return fromClause;
439    }
440
441    /**
442     * Relations that used in from clause of select statement.
443     * Or tables of other statements such as insert, update, delete and etc
444     *
445     * Please use this property to get the relations instead of {@link #getTables()} and {@link #getJoins()}after version 2.7.4.0
446     *
447     * when a join is used in from clause, then the table in getRelations() is type of ETableSource.join, and you can
448     * use TTable.getJoinExpr() to get this join.
449     *
450     * @return
451     */
452    public ArrayList<TTable> getRelations() {
453        return relations;
454    }
455
456    private ArrayList<TTable> relations = new ArrayList<>();
457
458    protected TTable fromSourceTable;
459
460    /**
461     * This is table in from clause if only one table is listed in the from clause,
462     * If more than one table is listed in from clause, please check {@link #getFromSourceJoin()} instead.
463     *
464     * @return table in from clause
465     */
466    public TTable getFromSourceTable() {
467        return fromSourceTable;
468    }
469
470    /**
471     * This is a join in from clause, including left and right relation.
472     * If only a single table is listed in from clause, please use {@link #getFromSourceTable()} instead
473     * @return
474     */
475    public TJoinExpr getFromSourceJoin() {
476        return fromSourceJoin;
477    }
478
479    protected TJoinExpr fromSourceJoin;
480
481    private String asCanonicalText = null;
482
483    /**
484     *  this method return a canonical form of a SQL statement in plan text.
485     *  <br>1. remove all comment inside SQL query.
486     *  <br>2. remove redundant parenthesis at the begin/end of a select statement.
487     *  <br>3. replace all number in where clause with 999 constant
488     *  <br>4. replace all string constant in where clause with 'placeholder_str'
489     *  <br>5. all number elements in a list such as (1,2,3,4) will be change to a single element (999)
490     *  <br>6. all string elements in a list such as ('a','b','c','d') will be change to a single element ('placeholder_str')
491     *
492     * @return a canonical form of a SQL statement in plan text.
493     */
494    public String asCanonical(){
495        if (asCanonicalText != null) return asCanonicalText;
496
497        String ret = null;
498        TSourceToken lcStartToken = getStartToken();
499        if (lcStartToken ==  null) return toString();
500        TSourceToken lcEndToken = getEndToken();
501        if (lcEndToken ==  null) return toString();
502
503        // remove the ; token at the end of statement
504        if (lcEndToken.tokencode == ';') lcEndToken.tokenstatus = ETokenStatus.tsdeleted;
505
506
507        // remove ( ) at the begin and end of the statement
508        TSourceToken lcCurrentToken = lcStartToken;
509        while (lcCurrentToken != null){
510            if (lcCurrentToken.tokencode != '(') {
511                break;
512            }else{
513                if (lcCurrentToken.getLinkToken() != null){
514                    lcCurrentToken.tokenstatus  = ETokenStatus.tsdeleted;
515                    lcCurrentToken.getLinkToken().tokenstatus  = ETokenStatus.tsdeleted;
516                }
517            }
518
519            if (lcCurrentToken.equals(lcEndToken)){
520                break;
521            }else{
522                lcCurrentToken = lcCurrentToken.getNextTokenInChain();
523            }
524        }
525
526        // change constant to placeholder, all number change to 999 and string constant change to placeholder_str
527        constantVisitor cv = new constantVisitor();
528        this.acceptChildren(cv);
529
530
531        boolean chainUnchanged = true, includingComment = false;
532        StringBuffer sb = new StringBuffer("");
533        TSourceToken lcPrevSt = null;
534        boolean ignoreNextReturnToken = false, isChainModified = false;
535
536        lcCurrentToken = lcStartToken;
537        while (lcCurrentToken != null){
538            if((lcCurrentToken.tokenstatus == ETokenStatus.tsdeleted)
539                    ||(!includingComment  && ((lcCurrentToken.tokencode == TBaseType.cmtslashstar) ||(lcCurrentToken.tokencode == TBaseType.cmtdoublehyphen)))
540            ){
541                // ignore this token, do nothing
542                //System.out.println("out: ignore deleted token:"+lcCurrentToken.astext);
543            }else{
544                //
545                sb.append(lcCurrentToken.toString());
546                if (lcCurrentToken.isChangedInAsCanonical()){
547                    lcCurrentToken.restoreText();
548                }
549            }
550
551            if (lcCurrentToken.equals(lcEndToken)){
552                break;
553            }else{
554                lcCurrentToken = lcCurrentToken.getNextTokenInChain();
555            }
556
557        }
558        asCanonicalText = sb.toString();
559        return asCanonicalText;
560    }
561
562    private TCTE cteIncludeThisStmt = null;
563
564    public void setCteIncludeThisStmt(TCTE cteIncludeThisStmt) {
565        this.cteIncludeThisStmt = cteIncludeThisStmt;
566    }
567
568    public TCTE getCteIncludeThisStmt() {
569        return cteIncludeThisStmt;
570    }
571
572    private TreeMap<String,TResultColumn> expandedResultColumns = null;
573
574    public TreeMap<String,TResultColumn> getExpandedResultColumns() {
575        if (expandedResultColumns == null){
576            expandedResultColumns = new TreeMap<>();
577        }
578        return expandedResultColumns;
579    }
580
581    public TSQLFunction searchFunctionInSQLEnv(String functionName){
582        if (getSqlEnv() == null) return null;
583        return getSqlEnv().searchFunction(functionName);
584    }
585
586    public TSQLEnv getSqlEnv() {
587        if (getGlobalScope() == null) return null;
588        return getGlobalScope().getSqlEnv();
589    }
590
591    public TGlobalScope getGlobalScope() {
592        TGlobalScope lcResult = null;
593        if (frameStack != null){
594            if (frameStack.get(0) != null){
595                lcResult = (TGlobalScope)frameStack.get(0).getScope();
596            }
597        }
598        return lcResult;
599    }
600
601    private Stack<TFrame> frameStack;
602
603    public void setFrameStack(Stack<TFrame> frameStack) {
604        this.frameStack = frameStack;
605    }
606
607    public Stack<TFrame> getFrameStack() {
608        return frameStack;
609    }
610
611    private TPTNodeList<TColumnWithSortOrder> indexColumns = null;
612
613    public TPTNodeList<TColumnWithSortOrder> getIndexColumns() {
614        return indexColumns;
615    }
616
617    private Stack<TObjectName> variableStack = null;
618
619    public void setVariableStack(Stack<TObjectName> variableStack) {
620        this.variableStack = variableStack;
621    }
622
623    public Stack<TObjectName> getVariableStack() {
624        if (variableStack == null){
625            variableStack = new Stack<TObjectName>();
626        }
627
628        return variableStack;
629    }
630
631    private Stack<TDaxFunction> daxFunctionStack = null;
632
633    public Stack<TDaxFunction> getDaxFunctionStack() {
634        if (daxFunctionStack == null){
635            daxFunctionStack = new Stack<TDaxFunction>();
636        }
637        return daxFunctionStack;
638    }
639
640    private TObjectName labelName;
641
642    public void setLabelName(TObjectName lName) {
643        labelName = lName;
644        if (labelName != null){
645            //labelName.setObjectType(TObjectName.ttobjLabelName);
646            labelName.setDbObjectType(EDbObjectType.label);
647        }
648    }
649
650    /**
651     *
652     * @return label name used in plsql statement.
653     */
654    public TObjectName getLabelName() {
655
656        return labelName;
657    }
658
659
660    private TObjectName endlabelName;
661
662    public void setEndlabelName(TObjectName endlabelName) {
663        this.endlabelName = endlabelName;
664    }
665
666    public TObjectName getEndlabelName() {
667
668        return endlabelName;
669    }
670
671    /**
672     * Type of this statement.
673     */
674    public ESqlStatementType sqlstatementtype;
675    /**
676     * Source tokens included in this statement. only source tokens available when this is a top level statement, otherwise, there is no source token in this statement.
677     * Please check {@link gudusoft.gsqlparser.nodes.TParseTreeNode#getStartToken()}, and {@link gudusoft.gsqlparser.nodes.TParseTreeNode#getEndToken()} of this statement. 
678     */
679    public TSourceTokenList sourcetokenlist;
680
681    public TSourceTokenList getTokenList() {
682        return sourcetokenlist;
683    }
684    /**
685     * Parser used to parse this statement.
686     */
687    public TCustomParser parser;
688    /**
689     * PLSQL parser used to parse this statement.
690     */
691    public TCustomParser plsqlparser;
692    /**
693     * Tag used by parser internally.
694     */
695    public int dummytag;
696
697    /**
698     * target table in the delete/insert/update/create table statement.
699     * @see #joins
700     * @see TSelectSqlStatement
701     * @see TDeleteSqlStatement
702     * @see TUpdateSqlStatement
703     * @see TCreateTableSqlStatement
704     * @see gudusoft.gsqlparser.stmt.TMergeSqlStatement
705     */
706    public TTable getTargetTable() {
707        return targetTable;
708    }
709
710    public void setTargetTable(TTable targetTable) {
711        setNewSubNode(this.targetTable,targetTable,getAnchorNode());
712        this.targetTable = targetTable;
713    }
714
715    private TTable targetTable ;
716
717    /**
718     * joins represents table sources in the from clause. All structure information was reserved.
719     * <p>SQL 1:
720     * <p><blockquote><pre>select f from t1</pre></blockquote>
721     * <p>size of joins will be 1, t1 can be fetch via joins.getJoin(0).getTable()
722     * <p>
723     * <p>SQL 2:
724     * <p><blockquote><pre>select f from t1,t2</pre></blockquote>
725     * <p>size of joins will be 2,
726     * <p>t1 can be fetch via joins.getJoin(0).getTable()
727     * <p>t2 can be fetch via joins.getJoin(1).getTable()
728     * <p>
729     * <p>SQL 3:
730     * <p><blockquote><pre>select f from t1 join t2 on t1.f1 = t2.f1</pre></blockquote>
731     * <p>size of joins will be 1,
732     * <p>t1 information can be fetch via joins.getJoin(0).getTable()
733     * <p>In order to access t2, we need to introduce a new  class {@link TJoinItem} which includes all information about t2 and join condition.
734     * <p>There is a property named joinItems of {@link TJoin} which is type of {@link TJoinItemList} that includes a list of {@link TJoinItem}.
735     * <p>this property can be access via {@link gudusoft.gsqlparser.nodes.TJoin#getJoinItems()}.
736     * <p>Now, t2 can be fetch via  joins.getJoin(0).getJoinItems().getJoinItem(0).getTable()
737     * <p>
738     * <p>SQL 4:
739     * <p><blockquote><pre>select f from t1 join t2 on t1.f1 = t2.f1 join t3 on t1.f1 = t3.f1</pre></blockquote>
740     * <p>size of joins will be 1,
741     * <p>t1 can be fetch via joins.getJoin(0).getTable()
742     * <p>t2 can be fetch via joins.getJoin(0).getJoinItems().getJoinItem(0).getTable()
743     * <p>t3 can be fetch via joins.getJoin(0).getJoinItems().getJoinItem(1).getTable()
744     *
745     * @see #tables
746     */
747    public TJoinList joins;
748
749    /**
750     * Provides a quick way to access all tables involved in this SQL statement.
751     * <p>It stores all tables in a flat way while {@link #joins} stores all tables in a hierarchical structure.
752     * <p>joins only represents tables in from clause of select/delete statement, and tables in update/insert statement.
753     * <p>{@link #tables} includes all tables in all types of SQL statements  such as tables involved in a create table or create trigger statements.
754     */
755    public TTableList tables;
756
757    public TJoinList getJoins() {
758        return joins;
759    }
760
761    public TTableList getTables() {
762        return tables;
763    }
764
765    /**
766     * Saves all first level sub statements.
767     * <p>By iterating statements recursively, you can fetch all included statements in an easy way.
768     * <p><blockquote><pre>
769     * select f1+(select f2 from t2) from t1
770     * where f2 &gt; all (select f3 from t3 where f4 = (select f5 from t4))</pre>
771     * </blockquote>
772     * <p> Statements included in above SQL was save in a hierarchical way like this:
773     * <ul>
774     * <li>(select f2 from t2)</li>
775     * <li>(select f3 from t3 where f4 = (select f5 from t4))
776     *     <ul>
777     *      <li>(select f5 from t4)</li>
778     *     </ul>
779     * </li>
780     * </ul>
781     * <p>If this statement is a create procedure/function statement, then all declaration statements and statements in
782     * procedure body can also be fetched quickly by iterating this property recursively.
783     *
784     *
785     */
786    public TStatementList getStatements() {
787        if (statements == null){
788            statements = new TStatementList();
789        }
790        return statements;
791    }
792
793    private TStatementList statements;
794
795    public void setCteList(TCTEList cteList) {
796        setNewSubNode(this.cteList,cteList,getAnchorNode());
797        this.cteList = cteList;
798    }
799
800    /**
801     * Multiple common table expressions {@link TCTE} can be specified following the single WITH keyword.
802     *<p> Each common table expression specified can also be referenced by name in the FROM clause of subsequent common table expressions.
803     *
804     * <p>Used in select, delete, update statement.
805     * @return  List of common table expression.
806     */
807
808    public TCTEList getCteList() {
809
810        return cteList;
811    }
812
813    private TCTEList cteList = null;
814
815    public void setResultColumnList(TResultColumnList resultColumnList) {
816        setNewSubNode(this.resultColumnList,resultColumnList,getAnchorNode());
817        this.resultColumnList = resultColumnList;
818    }
819
820    /**
821     * In select statement, this method returns Items in select_list.
822     * Can be *, expr, and name.*
823     * <br><br>
824     * In update statement, this method returns assignments in set clause.
825     *
826     * @return select list of select statement or assignments of update statement.
827     */
828    public TResultColumnList getResultColumnList() {
829
830        return resultColumnList;
831    }
832
833    private TResultColumnList resultColumnList = null;
834
835    private TWhereClause whereClause = null;
836    private TTopClause topClause = null;
837    private TOutputClause outputClause = null;
838    private TReturningClause returningClause = null;
839
840    public void setReturningClause(TReturningClause returningClause) {
841        setNewSubNode(this.returningClause,returningClause,getAnchorNode());
842        this.returningClause = returningClause;
843    }
844
845    /**
846     * @return {@link TReturningClause returning clause.}
847     */
848
849    public TReturningClause getReturningClause() {
850
851        return returningClause;
852    }
853
854    public void setOutputClause(TOutputClause outputClause) {
855        setNewSubNode(this.outputClause,outputClause,getAnchorNode());
856        this.outputClause = outputClause;
857    }
858
859    /**
860     * @return output clause.
861     */
862
863    public TOutputClause getOutputClause() {
864
865        return outputClause;
866    }
867
868    public void setTopClause(TTopClause topClause) {
869        setNewSubNode(this.topClause,topClause,getAnchorNode());
870        this.topClause = topClause;
871    }
872
873    /**
874     * @return {@link TTopClause top clause.}
875     */
876    public TTopClause getTopClause() {
877        return topClause;
878    }
879
880    public void setWhereClause(TWhereClause newWhereClause){
881        setNewSubNode(this.whereClause ,newWhereClause,getAnchorNode());
882        this.whereClause = newWhereClause;
883    }
884
885
886    /**
887     * @deprecated As of 2.0.9.0, use {@link #setWhereClause(TWhereClause)} instead
888     * Or, use {@link TWhereClause#setText(String)}
889     *
890     * @param condition
891     * @return
892     */
893    public  TWhereClause addWhereClause(String condition){
894           return this.whereClause;
895    }
896
897    /**
898     * restrict the rows selected to those that satisfy one or more conditions.
899     * used in select, delete, update statement.
900     * @return {@link TWhereClause where clause.}
901     */
902    public TWhereClause getWhereClause() {
903        return whereClause;
904    }
905
906    public void setAlreadyAddToParent(boolean alreadyAddToParent) {
907        this.alreadyAddToParent = alreadyAddToParent;
908    }
909
910    private boolean alreadyAddToParent = false;
911
912    private boolean ableToIncludeCTE(ESqlStatementType sst){
913       return ((sst == ESqlStatementType.sstselect)
914               ||(sst == ESqlStatementType.sstupdate)
915               ||(sst == ESqlStatementType.sstinsert)
916               ||(sst == ESqlStatementType.sstdelete)
917               );
918    }
919
920    private TCTEList cteListInAllLevels = new TCTEList();
921
922    protected  TCTEList searchCTEList(Boolean stopAtFirstFinding){
923        cteListInAllLevels.clear();
924        if (cteList != null ) {
925            cteListInAllLevels.addAll(cteList);
926            if (stopAtFirstFinding) return cteListInAllLevels;
927        }
928
929        TCustomSqlStatement lcParent = this.parentStmt;
930        while (lcParent != null){
931            if (!ableToIncludeCTE(lcParent.sqlstatementtype)) break;
932
933            if (lcParent.cteList != null) {
934                cteListInAllLevels.addAll(lcParent.cteList);
935                if (stopAtFirstFinding)  break;
936            }
937            lcParent = lcParent.parentStmt;
938        }
939        return cteListInAllLevels;
940    }
941
942
943//    protected  TCTEList searchCTEList(){
944//       TCTEList ret = null;
945//       if (cteList != null ) {return cteList;}
946//       TCustomSqlStatement lcParent = this.parentStmt;
947//       while (lcParent != null){
948//           if (!ableToIncludeCTE(lcParent.sqlstatementtype)) break;
949//           ret = lcParent.cteList;
950//           if (ret != null) break;
951//           lcParent = lcParent.parentStmt;
952//       }
953//        return ret;
954//    }
955
956    public TCustomSqlStatement getParentStmt() {
957        return parentStmt;
958    }
959
960    public TParseTreeNode getParentObjectName(){
961        TParseTreeNode result = super.getParentObjectName();
962        if (result != null) return result;
963        return getParentStmt();
964    }
965
966    public void setParentStmt(TCustomSqlStatement parentStmt) {
967        if (!alreadyAddToParent){
968            this.parentStmt = parentStmt;
969            if (this.parentStmt != null){
970                parentStmt.getStatements().add(this);
971                alreadyAddToParent = true;
972            }
973        }
974    }
975
976    public void setParentStmtToNull() {
977        this.parentStmt = null;
978    }
979
980    private TCustomSqlStatement ancestorStmt = null;
981
982    public TCustomSqlStatement getAncestorStmt() {
983        TCustomSqlStatement lcRet = this;
984        while (lcRet.getParentStmt() != null){
985            lcRet = lcRet.getParentStmt();
986        }
987        return lcRet;
988    }
989
990    /**
991     * parent statement of this statement if any
992     */
993    private TCustomSqlStatement parentStmt = null;
994
995    /**
996     * Original Parse tree node from parser
997     */
998    public TParseTreeNode rootNode;
999
1000    private Stack symbolTable = null;
1001
1002    /**
1003     *
1004     * @deprecated since ver 2.5.3.5, please use {@link TStmtScope} instead
1005     */
1006    public Stack getSymbolTable() {
1007        if (symbolTable == null){
1008            symbolTable = new Stack();
1009        }
1010        return symbolTable;
1011    }
1012
1013    public TSourceToken semicolonended;
1014    public boolean isctequery;
1015    private ArrayList <TSyntaxError> syntaxErrors;
1016
1017    public ArrayList<TSyntaxError> getSyntaxErrors() {
1018        return syntaxErrors;
1019    }
1020
1021    public String getErrormessage(){
1022
1023        String s="",hint="Syntax error";
1024        TSyntaxError t;
1025        for (int i= 0; i< syntaxErrors.size(); i++)
1026        {
1027            t = (TSyntaxError) syntaxErrors.get(i);
1028            if (t.hint.length() > 0) hint = t.hint;
1029            s= s+hint+"("+t.errorno+") near: "+t.tokentext;
1030            s=s+"("+t.lineNo;
1031            s=s+","+t.columnNo +")";
1032            //s=s+" expected tokentext:"+t.hint;
1033
1034            // break;//get only one message, remove this one and uncomment next line to get all error messages
1035            if (i !=  syntaxErrors.size() - 1)
1036                s = s +TBaseType.linebreak;
1037        }
1038
1039        return s;
1040    }
1041
1042    public ArrayList<TSyntaxError> getSyntaxHints() {
1043        return syntaxHints;
1044    }
1045
1046    private ArrayList <TSyntaxError> syntaxHints;
1047
1048    protected boolean isparsed;
1049    TSourceToken _semicolon;
1050
1051    /**
1052     * Number of syntax errors for this statement.
1053     * @return 0 means no syntax error.
1054     */
1055    public int getErrorCount() {
1056        return syntaxErrors.size();
1057    }
1058
1059    
1060    public TCustomSqlStatement(EDbVendor dbvendor){
1061        super();
1062        this.dbvendor = dbvendor;
1063        sqlstatementtype = ESqlStatementType.sstunknown;
1064        dummytag = 0;
1065        sourcetokenlist = new TSourceTokenList();
1066        syntaxErrors = new ArrayList<TSyntaxError>(4);
1067        syntaxHints = new ArrayList<TSyntaxError>(4);
1068        tables = new TTableList();
1069        joins = new TJoinList();
1070        indexColumns = new TPTNodeList<TColumnWithSortOrder>();
1071     }
1072
1073    /**
1074     * Log error messages if syntax errors found while parsing this statement.
1075     * @param se syntax error structure.
1076     * @return  type of error
1077     */
1078    public EActionOnParseError parseerrormessagehandle(TSyntaxError se){
1079        if (se.errortype == EErrorType.sphint){
1080            this.getAncestorStmt().syntaxHints.add(se);
1081        }else
1082            this.getAncestorStmt().syntaxErrors.add(se);
1083        return EActionOnParseError.aopcontinue;
1084    }
1085
1086    public int parsestatement(TCustomSqlStatement pparentsql,boolean isparsetreeavailable){
1087        return parsestatement(pparentsql,isparsetreeavailable,false);
1088    }
1089
1090    /**
1091     * Parse this statement.
1092     * @param pparentsql
1093     * @param isparsetreeavailable
1094     * @return parse result, zero means no syntax error found.
1095     */
1096    public int parsestatement(TCustomSqlStatement pparentsql,boolean isparsetreeavailable, boolean onlyNeedRawParseTree){
1097        int ret = 0;
1098        isparsed = false;
1099        if (!isparsetreeavailable){
1100           ret = checksyntax(pparentsql);
1101        }
1102        if (ret == 0)
1103        {
1104            isparsed = true;
1105            if (!onlyNeedRawParseTree){
1106                ret = doParseStatement(pparentsql);
1107            }
1108        }else if ((dbvendor == EDbVendor.dbvsybase)||(dbvendor == EDbVendor.dbvsybasease || dbvendor == EDbVendor.dbvsqlanywhere || dbvendor == EDbVendor.dbvsybaseiq)){
1109            if ((this.rootNode != null)&&
1110                    ((sqlstatementtype == ESqlStatementType.sstmssqlcreateprocedure)
1111                     ||(sqlstatementtype == ESqlStatementType.sstmssqlcreatefunction)
1112                     ||(sqlstatementtype == ESqlStatementType.sstcreatetrigger)
1113                    )){
1114                if (!onlyNeedRawParseTree){
1115                    doParseStatement(pparentsql);
1116                }
1117
1118            }
1119        }
1120        return ret;
1121    }
1122
1123    public boolean OracleStatementCanBeSeparatedByBeginEndPair(){
1124        return  (
1125
1126                (this.sqlstatementtype == ESqlStatementType.sstplsql_createprocedure)
1127                //|| (this.sqlstatementtype == ESqlStatementType.sst_block_with_label)
1128                ||(this.sqlstatementtype == ESqlStatementType.sstplsql_createfunction)
1129                ||(this.sqlstatementtype == ESqlStatementType.sstplsql_createpackage)
1130                ||(this.sqlstatementtype == ESqlStatementType.sstplsql_createtype_placeholder)
1131                ||(this.sqlstatementtype == ESqlStatementType.sstoraclecreatepackagebody)
1132                ||(this.sqlstatementtype == ESqlStatementType.sstplsql_createtrigger)
1133//                        ||(this.sqlstatementtype == ESqlStatementType.sstplsql_createtypebody)
1134//                        ||(this.sqlstatementtype == ESqlStatementType.sstplsql_tabletypedef)
1135                ||(this.sqlstatementtype == ESqlStatementType.sstplsql_varraytypedef)
1136                ||(this.sqlstatementtype == ESqlStatementType.sstplsql_createprocedure)
1137                ||(this.sqlstatementtype == ESqlStatementType.sstplsql_execimmestmt)
1138                ||(this.sqlstatementtype == ESqlStatementType.sstoraclecreatelibrary)
1139                );
1140    }
1141
1142    public boolean VerticaStatementCanBeSeparatedByBeginEndPair(){
1143        return  (
1144
1145                        (this.sqlstatementtype == ESqlStatementType.sstcreatefunction)
1146        );
1147    }
1148
1149    public boolean isnzplsql(){
1150        return  (
1151                (this.sqlstatementtype == ESqlStatementType.sstcreateprocedure)
1152        );
1153    }
1154
1155    public boolean ispgplsql(){
1156        return (this instanceof TCommonBlock)
1157                ||(this.sqlstatementtype == ESqlStatementType.sstcreateprocedure)
1158                ||(this.sqlstatementtype == ESqlStatementType.sstcreatefunction)
1159                ||(this.sqlstatementtype == ESqlStatementType.sstDoExecuteBlock)
1160                ;
1161    }
1162
1163    public boolean isGaussDBStoredProcedure(){
1164        return (this instanceof TCommonBlock)
1165                ||(this.sqlstatementtype == ESqlStatementType.sstcreateprocedure)
1166                ||(this.sqlstatementtype == ESqlStatementType.sstcreatefunction)
1167                ||(this.sqlstatementtype == ESqlStatementType.sstplsql_createpackage)
1168                ||(this.sqlstatementtype == ESqlStatementType.sstoraclecreatepackagebody)
1169                ||(this.sqlstatementtype == ESqlStatementType.sstDoExecuteBlock)
1170                ||(this.sqlstatementtype == ESqlStatementType.sstcreatetrigger)
1171                ||(this.sqlstatementtype == ESqlStatementType.sstplsql_createtypebody)
1172                ;
1173    }
1174
1175    public boolean isdatabricksplsql(){
1176        return (this instanceof TCommonBlock)
1177                ;
1178    }
1179
1180    public boolean isgreeplumplsql(){
1181        return (this instanceof TCommonBlock)
1182                ||(this.sqlstatementtype == ESqlStatementType.sstcreateprocedure)
1183                ||(this.sqlstatementtype == ESqlStatementType.sstcreatefunction)
1184                ||(this.sqlstatementtype == ESqlStatementType.sstDoExecuteBlock)
1185                ;
1186    }
1187
1188    public boolean isathenaplsql(){
1189        return (this instanceof TCommonBlock)
1190                ;
1191    }
1192
1193    public boolean isprestoplsql(){
1194        return (this instanceof TCommonBlock)
1195                ;
1196    }
1197
1198    public boolean issnowflakeplsql(){
1199        return ((this instanceof TCommonBlock)
1200                ||(this.sqlstatementtype == ESqlStatementType.sstcreateprocedure)
1201        );
1202    }
1203
1204    public boolean isBigQueryplsql(){
1205        return ((this instanceof TCommonBlock)
1206                ||(this.sqlstatementtype == ESqlStatementType.sstcreateprocedure)
1207        );
1208    }
1209
1210    public boolean isverticaplsql(){
1211        return  (
1212                        (this.sqlstatementtype == ESqlStatementType.sstcreatefunction)
1213        );
1214    }
1215
1216    public boolean isoracleplsql(){
1217        return  (
1218                (this.sqlstatementtype == ESqlStatementType.sst_plsql_block)
1219                ||(this.sqlstatementtype == ESqlStatementType.sstplsql_createprocedure)
1220                        ||(this.sqlstatementtype == ESqlStatementType.sstplsql_createfunction)
1221                        ||(this.sqlstatementtype == ESqlStatementType.sstplsql_createpackage)
1222                        ||(this.sqlstatementtype == ESqlStatementType.sstplsql_createtype_placeholder)
1223                        ||(this.sqlstatementtype == ESqlStatementType.sstoraclecreatepackagebody)
1224                        ||(this.sqlstatementtype == ESqlStatementType.sstplsql_createtrigger)
1225                        ||(this.sqlstatementtype == ESqlStatementType.sstplsql_createtypebody)
1226                        ||(this.sqlstatementtype == ESqlStatementType.sstplsql_tabletypedef)
1227                        ||(this.sqlstatementtype == ESqlStatementType.sstplsql_varraytypedef)
1228                        ||(this.sqlstatementtype == ESqlStatementType.sstplsql_createprocedure)
1229                        ||(this.sqlstatementtype == ESqlStatementType.sstplsql_execimmestmt)
1230                        ||(this.sqlstatementtype == ESqlStatementType.sstoraclecreatelibrary)
1231                );
1232    }
1233
1234    int checksyntax(TCustomSqlStatement psql){
1235        return dochecksyntax(psql);
1236    }
1237
1238    protected int dochecksyntax(TCustomSqlStatement psql){
1239        int ret = -1;
1240        clear();
1241        if (sourcetokenlist.size() == 0) return ret;
1242
1243//        TCustomParser lcparser;
1244//        lcparser = new TLzParserOracleSql(sourcetokenlist);
1245//        lcparser.sql = this;
1246//        ret = lcparser.yyparse();
1247
1248
1249        if (((this.dbvendor == EDbVendor.dbvoracle)||(this.dbvendor == EDbVendor.dbvoceanbase))&&(this.isoracleplsql()&&(plsqlparser!=null))
1250//                || ((this.dbvendor == EDbVendor.dbvgaussdb) // gaussdb 中用 oracle plsql 写的存储过程,用 oracle plsql parser 来解析
1251//                        &&(
1252//                            ((this instanceof TCreateFunctionStmt)&&(((TCreateFunctionStmt)this).isGaussDBSpInOracle()))
1253//                            ||((this instanceof TCreateProcedureStmt)&&(((TCreateProcedureStmt)this).isGaussDBSpInOracle()))
1254//                            ||(this instanceof TPlsqlCreatePackage)
1255//                          )
1256//                )
1257        ){
1258            plsqlparser.sql = this;
1259//            if (this.dbvendor == EDbVendor.dbvgaussdb){
1260//                // 原来用 gaussDB lexer tokenize 的 token 需要用 Oracle lexer 重新 tokenize 一边
1261//                String sqlText="";
1262//                for(int k = 0;k<sourcetokenlist.size();k++){
1263//                    sqlText = sqlText + sourcetokenlist.get(k).toString();
1264//                }
1265//                // TODO, need to use singleton pattern to get a single instance of Oracle parser.
1266//                TGSqlParser sqlParser = new TGSqlParser(EDbVendor.dbvoracle);
1267//                // keep coordinate of the origin query
1268//                long originalLineNo = sourcetokenlist.get(0).lineNo;
1269//                long originalColumnNo = sourcetokenlist.get(0).columnNo;
1270//                sqlParser.sqltext = TBaseType.stringBlock((int) originalLineNo - 1,(int) originalColumnNo - 1)+ sqlText;;
1271//
1272//                int r = sqlParser.getrawsqlstatements();
1273//                sourcetokenlist.clear();
1274//                for(int k=0;k<sqlParser.sourcetokenlist.size();k++){
1275//                    sourcetokenlist.add(sqlParser.sourcetokenlist.get(k));
1276//                }
1277//            }
1278
1279            plsqlparser.sourcetokenlist = sourcetokenlist;
1280
1281            if ((this instanceof TCommonStoredProcedureSqlStatement)
1282                &&((TCommonStoredProcedureSqlStatement)this).isWrapped()){
1283                // don't parse wrapped oracle plsql
1284                ret = 0;
1285                this.rootNode = this;
1286            }else {
1287                ret = plsqlparser.yyparse();
1288                this.rootNode = plsqlparser.rootNode;
1289            }
1290        }
1291        else{
1292            if ((this.sqlstatementtype == ESqlStatementType.sstExplain)&&(dbvendor != EDbVendor.dbvhana)){
1293                    // EXPLAIN PLAN ... FOR statement; only parse token after FOR keyword
1294                    boolean isFoundStopToken = false;
1295
1296                    for(int k=0;k<sourcetokenlist.size();k++){
1297                        TSourceToken st = sourcetokenlist.get(k);
1298                        switch (dbvendor){
1299                            case dbvoracle:
1300                                if (st.tokencode == TBaseType.rrw_for) {
1301                                    st.tokencode = TBaseType.sqlpluscmd;
1302                                    isFoundStopToken = true;
1303                                }
1304                                break;
1305                            case dbvdameng:
1306                                if (st.tokencode == TBaseType.rrw_for) {
1307                                    st.tokencode = TBaseType.sqlpluscmd;
1308                                    isFoundStopToken = true;
1309                                }
1310                                break;
1311                            case dbvredshift:
1312                                if (st.tokencode == TBaseType.rrw_explain){
1313                                    st.tokencode = TBaseType.sqlpluscmd;
1314                                    TSourceToken nextst = st.nextSolidToken();
1315                                    if (nextst.tokencode == TBaseType.rrw_redshift_verbose){
1316                                        nextst.tokencode = TBaseType.sqlpluscmd;
1317                                        //System.out.println("Found verbose after explain");
1318                                    }
1319                                    isFoundStopToken = true;
1320                                }
1321                                break;
1322                            case dbvvertica:
1323                                if ((st.tokencode == TBaseType.rrw_select)
1324                                        ||(st.tokencode == TBaseType.rrw_insert)
1325                                        ||(st.tokencode == TBaseType.rrw_update)
1326                                        ||(st.tokencode == TBaseType.rrw_merge)
1327                                )
1328                                {
1329                                    isFoundStopToken = true;
1330                                }
1331                                break;
1332                            case dbvclickhouse:
1333                            case dbvmysql:
1334                            case dbvoceanbase:
1335                            case dbvsparksql:
1336                            case dbvdatabricks:
1337                                if ((st.tokencode == TBaseType.rrw_select)
1338                                        ||(st.tokencode == TBaseType.rrw_insert)
1339                                        ||(st.tokencode == TBaseType.rrw_update)
1340                                        ||(st.tokencode == TBaseType.rrw_delete)
1341                                        ||(st.tokencode == TBaseType.rrw_replace)
1342                                        ||(st.tokencode == TBaseType.rrw_with)
1343                                        ||(st.tokencode == TBaseType.rrw_create)
1344                                        ||(st.tokencode == '(')
1345                                )
1346                                {
1347                                    isFoundStopToken = true;
1348                                }
1349                                break;
1350                            case dbvpostgresql:
1351                                if ((st.tokencode == TBaseType.rrw_select)
1352                                        ||(st.tokencode == TBaseType.rrw_insert)
1353                                        ||(st.tokencode == TBaseType.rrw_update)
1354                                        ||(st.tokencode == TBaseType.rrw_delete)
1355                                        ||(st.tokencode == TBaseType.rrw_replace)
1356                                        ||(st.tokencode == TBaseType.rrw_with)
1357                                        ||(st.tokencode == TBaseType.rrw_create)
1358                                        ||(st.tokencode == TBaseType.rrw_execute)
1359                                )
1360                                {
1361                                    isFoundStopToken = true;
1362                                }else if (st.tokencode == '('){
1363                                    // Check if this '(' starts EXPLAIN options like (COSTS FALSE)
1364                                    // or a subquery like (SELECT ...)
1365                                    TSourceToken nextSolid = sourcetokenlist.nextsolidtoken(k, 1, false);
1366                                    if (nextSolid != null
1367                                            && nextSolid.tokencode != TBaseType.rrw_select
1368                                            && nextSolid.tokencode != TBaseType.rrw_insert
1369                                            && nextSolid.tokencode != TBaseType.rrw_update
1370                                            && nextSolid.tokencode != TBaseType.rrw_delete
1371                                            && nextSolid.tokencode != TBaseType.rrw_with){
1372                                        // Options list: skip past closing ')'
1373                                        int depth = 1;
1374                                        st.tokencode = TBaseType.sqlpluscmd;
1375                                        for (k = k + 1; k < sourcetokenlist.size() && depth > 0; k++){
1376                                            TSourceToken inner = sourcetokenlist.get(k);
1377                                            if (inner.tokencode == '(') depth++;
1378                                            else if (inner.tokencode == ')') depth--;
1379                                            inner.tokencode = TBaseType.sqlpluscmd;
1380                                        }
1381                                        k--; // adjust for loop increment
1382                                    }else{
1383                                        isFoundStopToken = true;
1384                                    }
1385                                }
1386                                break;
1387                            case dbvduckdb:
1388                                if ((st.tokencode == TBaseType.rrw_select)
1389                                        ||(st.tokencode == TBaseType.rrw_insert)
1390                                        ||(st.tokencode == TBaseType.rrw_update)
1391                                        ||(st.tokencode == TBaseType.rrw_delete)
1392                                        ||(st.tokencode == TBaseType.rrw_replace)
1393                                        ||(st.tokencode == TBaseType.rrw_with)
1394                                        ||(st.tokencode == TBaseType.rrw_create)
1395                                        ||(st.tokencode == TBaseType.rrw_alter)
1396                                        ||(st.tokencode == TBaseType.rrw_merge)
1397                                        ||(st.tokencode == '(')
1398                                )
1399                                {
1400                                    isFoundStopToken = true;
1401                                }
1402                                break;
1403                            case dbvflink:
1404                                // Flink EXPLAIN can have: EXPLAIN (options) stmt or EXPLAIN options stmt
1405                                // Don't stop at '(' because it might be part of EXPLAIN (ESTIMATED_COST, ...)
1406                                if ((st.tokencode == TBaseType.rrw_select)
1407                                        ||(st.tokencode == TBaseType.rrw_insert)
1408                                        ||(st.tokencode == TBaseType.rrw_update)
1409                                        ||(st.tokencode == TBaseType.rrw_delete)
1410                                        ||(st.tokencode == TBaseType.rrw_replace)
1411                                        ||(st.tokencode == TBaseType.rrw_with)
1412                                        ||(st.tokencode == TBaseType.rrw_create)
1413                                )
1414                                {
1415                                    isFoundStopToken = true;
1416                                }
1417                                break;
1418                            case dbvcouchbase:
1419                                if (st.tokencode == TBaseType.rrw_explain){
1420                                    st.tokencode = TBaseType.sqlpluscmd;
1421                                    isFoundStopToken = true;
1422                                }
1423                                break;
1424                            case dbvpresto:
1425                            case dbvathena:
1426                            case dbvnetezza:
1427                                if ((st.tokencode == TBaseType.rrw_select)
1428                                        ||(st.tokencode == TBaseType.rrw_insert)
1429                                        ||(st.tokencode == TBaseType.rrw_update)
1430                                        ||(st.tokencode == TBaseType.rrw_delete)
1431                                )
1432                                {
1433                                    isFoundStopToken = true;
1434                                }
1435                                break;
1436                            case dbvteradata:
1437                                if ((st.tokencode == TBaseType.rrw_select)
1438                                        ||(st.tokencode == TBaseType.rrw_insert)
1439                                        ||(st.tokencode == TBaseType.rrw_update)
1440                                        ||(st.tokencode == TBaseType.rrw_delete)
1441                                        ||(st.tokencode == TBaseType.rrw_teradata_collect)
1442                                )
1443                                {
1444                                    isFoundStopToken = true;
1445                                }
1446                                break;
1447                        }//switch
1448
1449                        if (isFoundStopToken) break;
1450                        st.tokencode = TBaseType.sqlpluscmd;
1451                    }
1452            }else if (this.sqlstatementtype == ESqlStatementType.sstProfile){
1453                for(int k=0;k<sourcetokenlist.size();k++) {
1454                    TSourceToken st = sourcetokenlist.get(k);
1455                    if (dbvendor == EDbVendor.dbvvertica){
1456                        if ((st.tokencode == TBaseType.rrw_select)
1457                                ||(st.tokencode == TBaseType.rrw_insert)
1458                                ||(st.tokencode == TBaseType.rrw_update)
1459                                ||(st.tokencode == TBaseType.rrw_merge)
1460                                )
1461                        {
1462                            break;
1463                        }
1464                    }
1465                    st.tokencode = TBaseType.sqlpluscmd;
1466                }
1467            }else if (this.sqlstatementtype == ESqlStatementType.sstprepare){
1468                if ((dbvendor == EDbVendor.dbvcouchbase)||(dbvendor == EDbVendor.dbvpresto)||(dbvendor == EDbVendor.dbvathena)){
1469                    int keywordCount = 0;
1470                    for(int k=0;k<sourcetokenlist.size();k++) {
1471                        TSourceToken st = sourcetokenlist.get(k);
1472                            if (st.tokencode == TBaseType.rrw_prepare)
1473                            {
1474                                keywordCount++;
1475                            }else if ((st.tokentype == ETokenType.ttkeyword)
1476                                    &&(st.tokencode != TBaseType.rrw_from)
1477                                    &&(st.tokencode != TBaseType.rrw_as)){
1478                                keywordCount++;
1479                                if (keywordCount > 1) break;
1480                            }
1481                        st.tokencode = TBaseType.sqlpluscmd;
1482                    }
1483                }
1484            }
1485
1486            if (parser == null){
1487                // statement such as select/insert and etc  inside plsql
1488                if (psql != null){
1489                    parser = psql.getTopStatement().parser;
1490                    this.setParentStmt(psql);
1491                }
1492                // parser =  new TParserOracleSql(null);
1493                //parser.lexer = new TLexerOracle();
1494                //parser.lexer.delimiterchar = '/';
1495            }
1496            parser.sql = this;
1497            parser.sourcetokenlist = sourcetokenlist;
1498            ret = parser.yyparse();
1499            this.rootNode = parser.rootNode;
1500        }
1501
1502        if (ret == 0){
1503            ret = syntaxErrors.size();
1504        }
1505       // if (rootNode == null) {
1506       if (rootNode == null) {
1507            // EXPLAIN FOR CONNECTION has no inner statement to parse - this is valid
1508            if (this.sqlstatementtype == ESqlStatementType.sstExplain && isExplainForConnection()) {
1509                ret = 0;
1510            } else {
1511                ret = TBaseType.MSG_ERROR_NO_ROOT_NODE;
1512                // todo , uncomment next line when all sql statements in .y file was processed
1513                 parseerrormessagehandle( new TSyntaxError("no root node",0,0,"no_root_node",EErrorType.sperror,TBaseType.MSG_ERROR_NO_ROOT_NODE,this,-1));
1514            }
1515        }
1516        return ret;
1517    }
1518
1519    private boolean isExplainForConnection() {
1520        if (sourcetokenlist == null) return false;
1521        for (int i = 0; i < sourcetokenlist.size() - 1; i++) {
1522            TSourceToken st = sourcetokenlist.get(i);
1523            if (st.toString().equalsIgnoreCase("for")) {
1524                TSourceToken next = sourcetokenlist.nextsolidtoken(i, 1, false);
1525                if (next != null && next.toString().equalsIgnoreCase("connection")) {
1526                    return true;
1527                }
1528            }
1529        }
1530        return false;
1531    }
1532
1533    public void clearError(){
1534        syntaxErrors.clear();
1535        syntaxHints.clear();
1536    }
1537
1538    void clear(){
1539        syntaxErrors.clear();
1540        syntaxHints.clear();
1541// todo all subclass should add super()       
1542    }
1543
1544    public void setStmtScope(TStmtScope stmtScope) {
1545        this.stmtScope = stmtScope;
1546    }
1547
1548    public TStmtScope getStmtScope() {
1549        return stmtScope;
1550    }
1551
1552    /**
1553     * Original SQL fragment of this statement.
1554     * @return   Original statement text.
1555     */
1556
1557    /*
1558    public String toString(){
1559       StringBuffer sb = new StringBuffer("");
1560       for(int i=0; i<sourcetokenlist.size();i++){
1561          sb.append(sourcetokenlist.get(i).toString());  
1562        }
1563       return sb.toString();
1564    }
1565    */
1566    protected TStmtScope stmtScope = null;
1567    void buildsql(){}
1568    public int doParseStatement(TCustomSqlStatement psql){
1569        if (psql != null){
1570            this.setParentStmt(psql);
1571            this.setFrameStack(psql.getFrameStack());
1572            psql.stmtScope.incrementCurrentStmtIndex();
1573            this.queryId = String.format("%s#stmt_%d_%s", psql.getQueryId(),psql.stmtScope.getCurrentStmtIndex(), this.sqlstatementtype);
1574            stmtScope = new TStmtScope(psql.stmtScope,this);
1575            // psql.statements.add(this);
1576        }else{
1577            stmtScope = new TStmtScope(this);
1578
1579            // global scope
1580            this.getFrameStack().peek().getScope().incrementCurrentStmtIndex();
1581            this.queryId = String.format("stmt_%d_%s",this.getFrameStack().peek().getScope().getCurrentStmtIndex(), this.sqlstatementtype);
1582        }
1583
1584        if ((this.getStartToken() == null)&&(rootNode != null)){
1585            this.setStartToken(rootNode.getStartToken());
1586        }
1587        if ((this.getEndToken() == null)&&(rootNode != null)){
1588            this.setEndToken(rootNode.getEndToken());
1589        }
1590
1591        if(this.getGsqlparser() == null){
1592            if (rootNode != null){
1593                this.setGsqlparser(rootNode.getGsqlparser());
1594            }
1595        }
1596        return 0;
1597    }
1598
1599    void addtokentolist(TSourceToken st){
1600       st.stmt = this;
1601       sourcetokenlist.add(st);
1602    }
1603
1604    public TTable analyzeTablename(TObjectName tableName){
1605        TTable lcTable = new TTable();
1606        lcTable.setTableType(ETableSource.objectname);
1607        lcTable.setStartToken(tableName.getStartToken());
1608        lcTable.setEndToken(tableName.getEndToken());
1609        lcTable.setGsqlparser(this.getGsqlparser());
1610        lcTable.setTableName(tableName);
1611
1612        tables.addTable(lcTable);
1613        return lcTable;
1614    }
1615
1616    protected boolean isTableACTE(TTable pTable){
1617        boolean lcResult = false;
1618        TCTEList cteList1 = getCteList();
1619        if (cteList1 == null){
1620            TCustomSqlStatement lcStmt = getParentStmt();
1621            while (lcStmt != null){
1622                if (lcStmt.getCteList() != null){
1623                    cteList1 = lcStmt.getCteList();
1624                    break;
1625                }else {
1626                    lcStmt = lcStmt.getParentStmt();
1627                }
1628            }
1629        }
1630        if (cteList1 == null) return  false;
1631       // TCTE lcCTE = cteList1.cteNames.get(TBaseType.getTextWithoutQuoted(pTable.toString()).toUpperCase());
1632        if (pTable.toString() == null) return false;
1633
1634        int searchPos = pTable.getStartToken().posinlist;
1635        if (this.getCteIncludeThisStmt() != null){
1636            searchPos = this.getCteIncludeThisStmt().getStartToken().posinlist;
1637        }
1638        TCTE lcCTE = cteList1.searchCTEByName(TBaseType.getTextWithoutQuoted(pTable.toString()).toUpperCase(),searchPos);
1639        if ( lcCTE != null ){
1640            if (pTable.setCTE(lcCTE)){
1641                pTable.setCTEName(true);
1642                lcResult = true;
1643            }
1644        }
1645//        for (int i=0;i<cteList1.size();i++){
1646//            lcCTE = cteList1.getCTE(i);
1647//            if (TBaseType.getTextWithoutQuoted(lcCTE.getTableName().toString()).equalsIgnoreCase(TBaseType.getTextWithoutQuoted(pTable.toString()))){
1648//                pTable.setCTEName(true);
1649//                pTable.setCTE(lcCTE);
1650//                lcResult = true;
1651//                break;
1652//            }
1653//        }
1654
1655        return lcResult;
1656
1657    }
1658
1659    public TTable findTable(ETableEffectType[] tableEffectTypes){
1660        TTable lcResult = null;
1661        for(int i=0;i<tables.size();i++){
1662            for(int j=0;j<tableEffectTypes.length;j++){
1663                if (tables.getTable(i).getEffectType() == tableEffectTypes[j]){
1664                    lcResult = tables.getTable(i);
1665                    return  lcResult;
1666                }
1667            }
1668        }
1669        return  lcResult;
1670    }
1671    public void addToTables(TTable pTable){
1672        tables.addTable(pTable);
1673        if (isTableACTE(pTable)) return;
1674
1675        if (pTable.getTableName() == null) return;
1676        if (pTable.getTableName().getTableToken() == null) return;
1677        if ((pTable.getTableName().getTableString().toString().equalsIgnoreCase("inserted"))||(pTable.getTableName().getTableString().toString().equalsIgnoreCase("deleted"))){
1678           if ((getAncestorStmt().sqlstatementtype == ESqlStatementType.sstcreatetrigger)
1679               ||(getAncestorStmt().sqlstatementtype == ESqlStatementType.sstmssqlaltertrigger)){
1680               //pTable.setLinkTable(true);
1681               ETableEffectType[] effectTypes = new ETableEffectType[]{
1682                       ETableEffectType.tetTriggerOn,ETableEffectType.tetTriggerInsert,ETableEffectType.tetTriggerDelete,ETableEffectType.tetTriggerUpdate,ETableEffectType.tetTriggerInsteadOf
1683               };
1684               pTable.setLinkTable(getAncestorStmt().findTable(effectTypes));
1685           }
1686        }
1687
1688    }
1689
1690    public TJoin analyzeTableOrJoin(TFromTable pfromTable){
1691        TFromTable lcFromTable = pfromTable;
1692        TJoin lcJoin;
1693        TTable lcTable;
1694
1695        if (lcFromTable.getFromtableType() != ETableSource.join){
1696            lcJoin = new TJoin();
1697            lcTable = analyzeFromTable(lcFromTable,true);
1698            lcTable.setEffectType(ETableEffectType.tetSelect);
1699            lcJoin.setTable(lcTable);
1700            lcJoin.setStartToken(lcJoin.getTable().getStartToken());
1701            lcJoin.setEndToken(lcJoin.getTable().getEndToken());
1702            lcJoin.setGsqlparser(getGsqlparser());
1703            this.fromSourceTable = lcTable;
1704            this.getRelations().add(lcTable);
1705        }else{
1706            this.fromSourceJoin = lcFromTable.getJoinExpr();
1707
1708            this.fromSourceTable = new TTable();
1709            this.fromSourceTable.setTableType(ETableSource.join);
1710            this.fromSourceTable.setAliasClause(lcFromTable.getJoinExpr().getAliasClause());
1711            this.fromSourceTable.setStartToken(lcFromTable.getStartToken());
1712            this.fromSourceTable.setEndToken(lcFromTable.getEndToken());
1713            this.fromSourceTable.setGsqlparser(lcFromTable.getGsqlparser());
1714            this.fromSourceTable.setJoinExpr(this.fromSourceJoin);
1715            this.getRelations().add(this.fromSourceTable);
1716
1717            lcJoin = analyzeJoin(lcFromTable.getJoinExpr(),null,true);
1718            lcJoin.doParse(this, ESqlClause.join);
1719
1720            if (lcFromTable.getLateralViewList() != null){
1721                for(TLateralView lateralView:lcFromTable.getLateralViewList()){
1722                    TTable t = lateralView.createATable(this);
1723                    addToTables(t);
1724                    this.relations.add(t);
1725                }
1726            }
1727        }
1728
1729        return lcJoin;
1730    }
1731
1732    public TTable analyzeFromTable(TFromTable pfromTable, Boolean addToTableList){
1733        return analyzeFromTable(pfromTable,addToTableList,ESqlClause.unknown);
1734    }
1735
1736    public TTable analyzeFromTable(TFromTable pfromTable, Boolean addToTableList, ESqlClause pLocation){
1737        TTable lcTable = new TTable();
1738        lcTable.setTableType(pfromTable.getFromtableType());
1739        lcTable.setAliasClause(pfromTable.getAliasClause());
1740        lcTable.setStartToken(pfromTable.getStartToken());
1741        lcTable.setEndToken(pfromTable.getEndToken());
1742        lcTable.setGsqlparser(pfromTable.getGsqlparser());
1743        lcTable.setTableHintList(pfromTable.getTableHintList());
1744        lcTable.setTableSample(pfromTable.getTableSample());
1745        lcTable.setLateralViewList(pfromTable.getLateralViewList());
1746        lcTable.setTableProperties(pfromTable.getTableProperties());
1747        lcTable.setPivotedTable(pfromTable.getPivotedTable());
1748        lcTable.setParenthesisCount(pfromTable.getParenthesisCount());
1749        lcTable.setParenthesisAfterAliasCount(pfromTable.getParenthesisAfterAliasCount());
1750        lcTable.setTableKeyword(pfromTable.isTableKeyword());
1751        lcTable.setOnlyKeyword(pfromTable.isOnlyKeyword());
1752        lcTable.setArrayIndexAlias(pfromTable.getArrayIndexAlias());
1753        lcTable.setSuperUnpivot(pfromTable.isSuperUnpivot());
1754        lcTable.setFlashback(pfromTable.getFlashback());
1755        lcTable.setPxGranule(pfromTable.getPxGranule());
1756        lcTable.setTimeTravelClause(pfromTable.getTimeTravelClause());
1757        //lcTable.setPartitionClause(pfromTable.getPartitionClause());
1758
1759        if(getFrameStack().firstElement() != null){
1760            TFrame stackFrame = getFrameStack().firstElement();
1761            TGlobalScope globalScope = (TGlobalScope)stackFrame.getScope();
1762            lcTable.setSqlEnv(globalScope.getSqlEnv());
1763        }
1764
1765       switch(lcTable.getTableType()){
1766           case objectname:{
1767              // tables.addTableByTableRefernce(pfromTable.getTableObjectName());
1768               boolean insertedInTrigger = false;
1769               if (getTopStatement().sqlstatementtype == ESqlStatementType.sstcreatetrigger){
1770                  insertedInTrigger = (pfromTable.getTableObjectName().toString().compareToIgnoreCase("inserted")==0);
1771               }
1772
1773               if (insertedInTrigger){
1774                   // change table name from inserted to onTable name in create trigger 
1775                 lcTable.setTableName(((TCreateTriggerStmt)getTopStatement()).getOnTable().getTableName());
1776                 //lcTable.setLinkTable(true);
1777                 lcTable.setLinkTable(((TCreateTriggerStmt)getTopStatement()).getOnTable());
1778
1779               }else{
1780                    lcTable.setTableName(pfromTable.getTableObjectName());
1781                   lcTable.getTableName().setSqlEnv(getSqlEnv());
1782
1783//                   if (getSqlEnv().getDefaultCatalogName() != null){
1784//                       if (lcTable.getTableName().getDatabaseToken() == null){
1785//                           lcTable.getTableName().setDatabaseToken(new TSourceToken(getSqlEnv().getDefaultCatalogName()),true);
1786//                       }
1787//                   }
1788
1789//                   if ((lcTable.getTableName().getSchemaToken() == null)&&(TSQLEnv.supportSchema(this.dbvendor))){
1790//                       // let find schema name for this table in env
1791//                       TSQLTable t = getSqlEnv().searchTable(".."+lcTable.getFullName());
1792//                       if (t != null){
1793//                           TSQLSchema s = t.getSchema();
1794//                           if (s != null){
1795//                               lcTable.getTableName().setSchemaToken(new TSourceToken(s.getName()),true);
1796//                           }
1797//                       }
1798//                   }
1799
1800               }
1801               // let's check is it cte name or ordinary table name
1802               TCTEList lcCteList = searchCTEList(false);
1803               TCTE lcCte = null;
1804               if (lcCteList != null){
1805                 for(int i=0;i<lcCteList.size();i++){
1806                    lcCte = lcCteList.getCTE(i);
1807                    if (lcCte.getTableName().toString().compareToIgnoreCase(TBaseType.getTextWithoutQuoted(lcTable.getTableName().toString()))==0){
1808                        // this is cte name
1809                        if (lcTable.setCTE(lcCte)){
1810                            lcTable.setCTEName(true);
1811                            lcTable.setCteColomnReferences(lcCte.getColumnList());
1812                            break;
1813                        }
1814                    }
1815                 }
1816               }
1817
1818               break;
1819           }
1820           case tableExpr:{
1821               ESqlClause location =  ESqlClause.tableExpr; //ESqlClause.resultColumn;
1822               if (sqlstatementtype == ESqlStatementType.sstinsert ){
1823                   // change location here
1824               }
1825               lcTable.setTableExpr(pfromTable.getTableExpr());
1826               lcTable.getTableExpr().doParse(this,location);
1827               // teradata: SELECT table1.* FROM table(strtok_split_to_table(1, 'dm-calcite-raven/td/bq', '-') RETURNS (outkey integer, tokennum integer, token varchar(20)) ) as table1;
1828               // RETURNS (outkey integer, tokennum integer, token varchar(20))
1829               lcTable.setColumnDefinitions(pfromTable.getColumnDefinitions());
1830               // Teradata table function HASH BY and LOCAL ORDER BY clauses
1831               lcTable.setHashByClause(pfromTable.getHashByClause());
1832               lcTable.setLocalOrderBy(pfromTable.getLocalOrderBy());
1833               break;
1834           }
1835           case subquery:{
1836//               if (pfromTable.getSubquerynode().isHiveFromQuery()){
1837//                   THiveFromQuery fromQuery  = new THiveFromQuery(dbvendor);
1838//                   lcTable.setHiveFromQuery(fromQuery);
1839//                   fromQuery.rootNode = pfromTable.getSubquerynode();
1840//                   fromQuery.setStartToken(pfromTable.getSubquerynode());
1841//                   fromQuery.setEndToken(pfromTable.getSubquerynode());
1842//                   fromQuery.setLabelName(this.labelName);
1843//                   fromQuery.doParseStatement(this);
1844//               }else{
1845//                   lcTable.subquery = new TSelectSqlStatement(dbvendor);
1846//                   lcTable.subquery.rootNode = pfromTable.getSubquerynode();
1847//                   lcTable.subquery.setLocation(ESqlClause.elTable);
1848//                   //lcTable.subquery.resultColumnList = ((TSelectSqlNode)lcTable.subquery.rootNode).getResultColumnList();
1849//                   lcTable.subquery.doParseStatement(this);
1850//               }
1851
1852               lcTable.subquery = new TSelectSqlStatement(dbvendor);
1853               lcTable.subquery.rootNode = pfromTable.getSubquerynode();
1854               if (pLocation == ESqlClause.unknown){
1855                   lcTable.subquery.setLocation(ESqlClause.elTable);
1856               }else{
1857                   lcTable.subquery.setLocation(pLocation);
1858               }
1859               //lcTable.subquery.resultColumnList = ((TSelectSqlNode)lcTable.subquery.rootNode).getResultColumnList();
1860               lcTable.subquery.doParseStatement(this);
1861
1862               break;
1863           }
1864           case function:{
1865               ESqlClause location = ESqlClause.tableFunction;// resultColumn;
1866               if (sqlstatementtype == ESqlStatementType.sstinsert ){
1867                   // change location here
1868               }
1869               lcTable.setFuncCall(pfromTable.getFuncCall());
1870               lcTable.getFuncCall().doParse(this,location);
1871               break;
1872           }
1873           case containsTable:{
1874               ESqlClause location = ESqlClause.containsTable;//resultColumn;
1875               if (sqlstatementtype == ESqlStatementType.sstinsert ){
1876                   // change location here
1877               }
1878               lcTable.setContainsTable(pfromTable.getContainsTable());
1879               lcTable.getContainsTable().doParse(this,location);
1880               break;
1881           }
1882
1883           case openrowset:{
1884               ESqlClause location = ESqlClause.openrowset;//resultColumn;
1885               if (sqlstatementtype == ESqlStatementType.sstinsert ){
1886                   // change location here
1887               }
1888               lcTable.setOpenRowSet(pfromTable.getOpenRowSet());
1889               lcTable.getOpenRowSet().doParse(this,location);
1890               break;
1891           }
1892
1893           case openxml:{
1894               ESqlClause location = ESqlClause.openxml;//resultColumn;
1895               if (sqlstatementtype == ESqlStatementType.sstinsert ){
1896                   // change location here
1897               }
1898               lcTable.setOpenXML(pfromTable.getOpenXML());
1899               lcTable.getOpenXML().doParse(this,location);
1900               break;
1901           }
1902
1903           case opendatasource:{
1904               ESqlClause location = ESqlClause.opendatasource;//resultColumn;
1905               if (sqlstatementtype == ESqlStatementType.sstinsert ){
1906                   // change location here
1907               }
1908               lcTable.setOpenDatasource(pfromTable.getOpenDatasource());
1909               lcTable.getOpenDatasource().doParse(this,location);
1910               break;
1911           }
1912
1913           case openquery:{
1914               ESqlClause location = ESqlClause.openquery;//resultColumn;
1915               if (sqlstatementtype == ESqlStatementType.sstinsert ){
1916                   // change location here
1917               }
1918               lcTable.setOpenquery(pfromTable.getOpenQuery());
1919               lcTable.getOpenquery().doParse(this,location);
1920               lcTable.setSubquery(lcTable.getOpenquery().getSubquery());
1921               break;
1922           }
1923
1924           case datachangeTable:{
1925               ESqlClause location = ESqlClause.datachangeTable;//resultColumn;
1926               if (sqlstatementtype == ESqlStatementType.sstinsert ){
1927                   // change location here
1928               }
1929               lcTable.setDatachangeTable(pfromTable.getDatachangeTable());
1930               lcTable.getDatachangeTable().doParse(this,location);
1931               break;
1932           }
1933           case rowList:{
1934               ESqlClause location = ESqlClause.rowList;//resultColumn;
1935               lcTable.setValueClause(pfromTable.getValueClause());
1936               lcTable.getValueClause().doParse(this,location);
1937               break;
1938           }
1939           case pivoted_table:{
1940               ESqlClause location = ESqlClause.pivoted_table;//resultColumn;
1941               lcTable.getPivotedTable().doParse(this,location);
1942               addToTableList = false;
1943               targetTable = lcTable;
1944               break;
1945           }
1946           case xmltable:{
1947               ESqlClause location = ESqlClause.xmltable;//resultColumn;
1948               lcTable.setXmlTable(pfromTable.getXmlTable());
1949               lcTable.getXmlTable().doParse(this,location);
1950               break;
1951           }
1952
1953           case informixOuter:{
1954               ESqlClause location = ESqlClause.outerTable;//resultColumn;
1955               lcTable.setOuterClause(pfromTable.getOuterClause());
1956               lcTable.getOuterClause().doParse(this,location);
1957               break;
1958           }
1959
1960           case table_ref_list:{
1961               lcTable.setFromTableList(pfromTable.getFromTableList());
1962               break;
1963           }
1964//           case hiveFromQuery:{
1965//               THiveFromQuery fromQuery = new THiveFromQuery(EDbVendor.dbvhive);
1966//               fromQuery.rootNode = pfromTable.getFromQuerySqlNode();
1967//               fromQuery.doParseStatement(this);
1968//               lcTable.setHiveFromQuery(fromQuery);
1969//               break;
1970//           }
1971           case output_merge:{
1972               TMergeSqlStatement outputMerge = new TMergeSqlStatement(EDbVendor.dbvmssql);
1973               outputMerge.rootNode = pfromTable.getMergeSqlNode();
1974               outputMerge.doParseStatement(this);
1975               lcTable.setOutputMerge(outputMerge);
1976               break;
1977           }
1978           case td_unpivot:{
1979               // Set the TD_UNPIVOT output table before doParse so that VALUE_COLUMNS and
1980               // UNPIVOT_COLUMN can be linked to it (they are output columns of TD_UNPIVOT)
1981               pfromTable.getTdUnpivot().setTdUnpivotOutputTable(lcTable);
1982               pfromTable.getTdUnpivot().doParse(this,ESqlClause.tdUnPivot);
1983               lcTable.setTdUnpivot(pfromTable.getTdUnpivot());
1984               break;
1985           }
1986           case unnest:{
1987               pfromTable.getUnnestClause().doParse(this,ESqlClause.elTable);
1988               lcTable.setUnnestClause(pfromTable.getUnnestClause());
1989               if (lcTable.getAliasClause() != null){
1990                   if (lcTable.getAliasClause().getColumns() != null){
1991                       for(TObjectName pColumn:lcTable.getAliasClause().getColumns()){
1992                           lcTable.getLinkedColumns().addObjectName(pColumn);
1993                           pColumn.setSourceTable(lcTable);
1994                       }
1995                   }else if (lcTable.getAliasClause().getAliasName() != null){
1996//                       SELECT *
1997//                               FROM UNNEST(['foo', 'bar', 'baz', 'qux', 'corge', 'garply', 'waldo', 'fred']) AS element
1998//                       WITH OFFSET AS offset
1999
2000                       // add element as column of unnest table.
2001                       TObjectName newColumn = TObjectName.createObjectName(this.dbvendor,EDbObjectType.column,lcTable.getAliasClause().getAliasName().getStartToken());
2002                       lcTable.getLinkedColumns().addObjectName(newColumn);
2003                       newColumn.setSourceTable(lcTable);
2004                   }
2005               }
2006
2007               if (lcTable.getUnnestClause().getWithOffset() != null){
2008                   if (lcTable.getUnnestClause().getWithOffsetAlais() != null){
2009                       // with offset as offsetAlias
2010                       TAliasClause aliasClause = lcTable.getUnnestClause().getWithOffsetAlais();
2011                       if (aliasClause.getAliasName() != null){
2012                           TObjectName newColumn = TObjectName.createObjectName(this.dbvendor,EDbObjectType.column,aliasClause.getAliasName().getStartToken());
2013                           lcTable.getLinkedColumns().addObjectName(newColumn);
2014                           newColumn.setSourceTable(lcTable);
2015                       }
2016                   }else{
2017                       // with offset
2018                       TObjectName newColumn = TObjectName.createObjectName(this.dbvendor,EDbObjectType.column,new TSourceToken("offset"));
2019                       lcTable.getLinkedColumns().addObjectName(newColumn);
2020                       newColumn.setSourceTable(lcTable);
2021                   }
2022               }
2023
2024               // link columns in the select list to unnest()
2025               // select emp_id,name,state,city,zipcode from `absolute-runner-302907.gudu_sqlflow.ADDRESS_NESTED`, UNNEST(address)
2026               if (lcTable.getUnnestClause().getDerivedColumnList() != null){
2027                   TObjectNameList derivedColumns = lcTable.getUnnestClause().getDerivedColumnList();
2028                   for(int i=0;i<derivedColumns.size();i++){
2029                       //System.out.println(derivedColumns.getObjectName(i).toString());
2030                       lcTable.getLinkedColumns().addObjectName(derivedColumns.getObjectName(i));
2031                   }
2032               }
2033
2034               break;
2035           }
2036           case jsonTable:{
2037               ESqlClause location = ESqlClause.jsonTable;//resultColumn;
2038               lcTable.setJsonTable(pfromTable.getJsonTable());
2039               lcTable.getJsonTable().doParse(this,location);
2040               break;
2041           }
2042           case externalTable:
2043               lcTable.setTableName(pfromTable.getTableObjectName());
2044               lcTable.setColumnDefinitions(pfromTable.getColumnDefinitions());
2045               lcTable.getColumnDefinitions().doParse(this,pLocation);
2046               lcTable.setTableType(ETableSource.externalTable); // tableType is reset in setTableName() method, so we reset it here
2047               break;
2048           case caseJoin:
2049               lcTable.setCaseJoin(pfromTable.getCaseJoin());
2050               lcTable.getCaseJoin().doParse(this,pLocation);
2051               break;
2052           case stageReference:
2053               lcTable.setStageReference(pfromTable.getStageReference());
2054               lcTable.getStageReference().doParse(this,pLocation);
2055
2056               lcTable.setTableName(lcTable.getStageReference().getStageName());
2057               lcTable.setTableType(ETableSource.stageReference);
2058               lcTable.getTableName().setSqlEnv(getSqlEnv());
2059
2060               break;
2061
2062      }//switch
2063
2064//        if (pfromTable.getPivotClause() != null){
2065//            lcTable.setPivotClause(pfromTable.getPivotClause());
2066//            lcTable.getPivotClause().doParse(this,ESqlClause.resultColumn);
2067//        }
2068
2069        lcTable.setPartitionExtensionClause(pfromTable.getPartitionExtensionClause());
2070
2071        //tables.addTable(lcTable);
2072        if (addToTableList) {
2073            addToTables(lcTable);
2074        }
2075
2076        if (lcTable.getTableHintList() != null){
2077            for(int i=0;i<lcTable.getTableHintList().size();i++){
2078                TTableHint hint = lcTable.getTableHintList().getElement(i);
2079                hint.setOwnerTable(lcTable);
2080                hint.doParse(this,ESqlClause.tableHint);
2081            }
2082        }
2083
2084        if (lcTable.getLateralViewList() != null){
2085            for(TLateralView lateralView:lcTable.getLateralViewList()){
2086                TTable t = lateralView.createATable(this);
2087                addToTables(t);
2088                this.relations.add(t);
2089            }
2090        }
2091
2092        if (lcTable.getAliasClause() != null){
2093            if (lcTable.getAliasClause().toString().equalsIgnoreCase("and")){
2094                // end keyword can't be alias name
2095                TSourceToken st1 = lcTable.getAliasClause().getStartToken();
2096                TSyntaxError err = new TSyntaxError(st1.toString()
2097                        ,st1.lineNo,st1.columnNo
2098                        ,String.format("AND keyword can't be table alias")
2099                        ,EErrorType.sperror
2100                        ,TBaseType.MSG_ERROR_AND_KEYWORD_CANT_USED_AS_TABLE_ALIAS
2101                        ,this,st1.posinlist);
2102                this.parseerrormessagehandle( err);
2103
2104            }
2105        }
2106
2107        return lcTable;
2108    }
2109
2110   public TJoin analyzeJoin(TJoinExpr pJoinExpr,TJoin pJoin,Boolean isSub){
2111        TJoin retval = pJoin;
2112        TJoinItem lcJoinItem = null ;
2113
2114        if (pJoinExpr == null) {return retval;}
2115
2116        if (pJoinExpr.getJointype() == EJoinType.nested)
2117        {
2118            if (isSub)
2119            {
2120                if (retval == null) {  // top level, left side is a join
2121                  retval = new TJoin();
2122                  retval.setStartToken(pJoinExpr.getStartToken());
2123                  retval.setEndToken(pJoinExpr.getEndToken());
2124                }
2125
2126                pJoinExpr.setJointype(pJoinExpr.original_jontype);
2127                retval.setJoin(analyzeJoin(pJoinExpr,null,true));
2128                //retval =analyzeJoin(pJoinExpr,null,true);
2129                retval.setKind(TBaseType.join_source_join);
2130                retval.getJoin().setAliasClause(pJoinExpr.getAliasClause());
2131                retval.getJoin().setWithParen(true);
2132                retval.getJoin().setNestedParen(pJoinExpr.getNestedParen());
2133            }
2134           else
2135            {
2136                if (retval == null)
2137                {
2138                    retval = new TJoin();
2139                    retval.setStartToken(pJoinExpr.getStartToken());
2140                    retval.setEndToken(pJoinExpr.getEndToken());
2141                    retval.setGsqlparser(this.getGsqlparser());
2142                }
2143                else
2144                {
2145                }
2146                pJoinExpr.setJointype(pJoinExpr.original_jontype);
2147                retval = analyzeJoin(pJoinExpr,retval,false);
2148                //retval.setJoin(analyzeJoin(pJoinExpr,retval,false));
2149                //retval = analyzeJoin(pJoinExpr,retval,false);
2150                //retval.setKind(TBaseType.join_source_join);
2151                //retval.setKind(TBaseType.join_source_table);
2152                //retval.setAliasClause(pJoinExpr.getAliasClause());
2153                retval.setAliasClause(pJoinExpr.getAliasClause());
2154                retval.setWithParen(true);
2155                retval.setNestedParen(pJoinExpr.getNestedParen());
2156            }
2157            return retval;
2158        }
2159
2160        if (pJoinExpr.getLeftOperand().getFromtableType() != ETableSource.join){
2161            if (retval == null) {
2162              retval = new TJoin();
2163              retval.setStartToken(pJoinExpr.getStartToken());
2164              retval.setEndToken(pJoinExpr.getEndToken());
2165              retval.setGsqlparser(this.getGsqlparser());
2166
2167              //  retval.setStartToken(pJoinExpr.getLeftOperand().getStartToken());
2168              //  retval.setEndToken(pJoinExpr.getLeftOperand().getEndToken());
2169            }
2170            TTable lcTable = analyzeFromTable(pJoinExpr.getLeftOperand(),true,ESqlClause.join);
2171            lcTable.setEffectType(ETableEffectType.tetSelect);
2172            retval.setTable(lcTable);
2173            //retval.joinTable.OwnerJoin = result;
2174            retval.setKind(TBaseType.join_source_table);
2175            pJoinExpr.setLeftTable(lcTable);
2176        }else{
2177            TJoinExpr lcJoinItemJoinExpr = pJoinExpr.getLeftOperand().getJoinExpr();
2178            //if (lcJoinItemJoinExpr.getJointype() == TBaseType.join_nested){
2179            //    lcJoinItemJoinExpr.setJointype(lcJoinItemJoinExpr.original_jontype);
2180            //}
2181
2182            if (retval != null) {
2183              retval = analyzeJoin(lcJoinItemJoinExpr,retval,true);
2184            } else {
2185              retval = analyzeJoin(lcJoinItemJoinExpr,retval,isSub);
2186            }
2187            retval.setStartToken(lcJoinItemJoinExpr.getStartToken());
2188            retval.setEndToken(lcJoinItemJoinExpr.getEndToken());
2189
2190
2191            TTable lcTable = new TTable();
2192            lcTable.setTableType(pJoinExpr.getLeftOperand().getFromtableType());
2193            lcTable.setAliasClause(lcJoinItemJoinExpr.getAliasClause());
2194            lcTable.setStartToken(lcJoinItemJoinExpr.getStartToken());
2195            lcTable.setEndToken(lcJoinItemJoinExpr.getEndToken());
2196            pJoinExpr.setLeftTable(lcTable);
2197            lcTable.setJoinExpr(lcJoinItemJoinExpr);
2198        }
2199
2200        if (pJoinExpr.getRightOperand().getFromtableType() != ETableSource.join){
2201            if (retval != null)
2202            {
2203                lcJoinItem = new TJoinItem();
2204                TTable lcTable = analyzeFromTable(pJoinExpr.getRightOperand(),true,ESqlClause.join);
2205                lcTable.setEffectType(ETableEffectType.tetSelect);
2206                lcJoinItem.setTable(lcTable);
2207                lcJoinItem.setStartToken(lcJoinItem.getTable().getStartToken());
2208                lcJoinItem.setEndToken(lcJoinItem.getTable().getEndToken());
2209               // lcJoinItem.JoinItemTable.OwnerJoinItem := lcJoinItem;
2210                lcJoinItem.setKind(TBaseType.join_source_table);
2211                retval.getJoinItems().addJoinItem(lcJoinItem);
2212                pJoinExpr.setRightTable(lcTable);
2213            }
2214        }else{
2215            if (retval != null)
2216            {
2217                lcJoinItem = new TJoinItem();
2218                lcJoinItem.setKind(TBaseType.join_source_join);
2219                TJoinExpr lcJoinItemJoinExpr = pJoinExpr.getRightOperand().getJoinExpr();
2220                //if (lcJoinItemJoinExpr.getJointype() == TBaseType.join_nested){
2221                //    lcJoinItemJoinExpr.setJointype(lcJoinItemJoinExpr.original_jontype);
2222                //}
2223                lcJoinItem.setJoin(analyzeJoin(pJoinExpr.getRightOperand().getJoinExpr(),null,false));
2224                lcJoinItem.getJoin().setAliasClause(lcJoinItemJoinExpr.getAliasClause());
2225                lcJoinItem.setStartToken(lcJoinItem.getJoin().getStartToken());
2226                lcJoinItem.setEndToken(lcJoinItem.getJoin().getEndToken());
2227                retval.getJoinItems().addJoinItem(lcJoinItem);
2228
2229                TTable lcTable = new TTable();
2230                lcTable.setTableType(pJoinExpr.getRightOperand().getFromtableType());
2231                lcTable.setAliasClause(lcJoinItemJoinExpr.getAliasClause());
2232                lcTable.setStartToken(lcJoinItemJoinExpr.getStartToken());
2233                lcTable.setEndToken(lcJoinItemJoinExpr.getEndToken());
2234                pJoinExpr.setRightTable(lcTable);
2235                lcTable.setJoinExpr(lcJoinItemJoinExpr);
2236            }
2237        }
2238
2239        if (lcJoinItem == null) return retval;
2240
2241        lcJoinItem.setJoinType(pJoinExpr.getJointype());
2242        lcJoinItem.setUsingColumns(pJoinExpr.usingColumns);
2243        if ((lcJoinItem.getUsingColumns() != null) && (tables.size()>1)){
2244            TObjectName crf ;
2245            for (int i=0;i<lcJoinItem.getUsingColumns().size();i++){
2246                crf = lcJoinItem.getUsingColumns().getObjectName(i);
2247                // link this column to last 2 tables
2248                tables.getTable(tables.size()-1).getObjectNameReferences().addObjectName(crf);
2249                tables.getTable(tables.size()-2).getObjectNameReferences().addObjectName(crf);
2250
2251                tables.getTable(tables.size()-1).getLinkedColumns().addObjectName(crf);
2252                crf.setSourceTable(tables.getTable(tables.size()-1));
2253                tables.getTable(tables.size()-2).getLinkedColumns().addObjectName(crf);
2254                crf.setSourceTable(tables.getTable(tables.size()-2));
2255
2256            }
2257            lcJoinItem.setEndToken(lcJoinItem.getUsingColumns().getEndToken());
2258        }
2259        lcJoinItem.setOnCondition(pJoinExpr.onCondition);
2260        if (lcJoinItem.getOnCondition() != null)
2261        {
2262            lcJoinItem.getOnCondition().doParse(this,ESqlClause.joinCondition);
2263            lcJoinItem.setEndToken(lcJoinItem.getOnCondition().getEndToken());
2264        }
2265
2266
2267        return retval;
2268    }
2269
2270    public boolean locateVariableOrParameter(TObjectName cr){
2271        return locateVariableOrParameter(cr,false);
2272    }
2273
2274    public boolean locateVariableOrParameter(TObjectName cr, boolean checkVariableDeclaredInProcedure){
2275      boolean ret =  false;
2276      if (cr.getDbObjectType() == EDbObjectType.variable) return true;
2277      if (cr.toString().equalsIgnoreCase("*")) return  false;
2278      //search variable in framestack
2279
2280      TVariable symbolVariable = null;
2281
2282      if (cr.getTableToken() != null){
2283          // record_variable.column
2284          symbolVariable =  TSymbolTableManager.searchSymbolVariable(this.getFrameStack(),cr.getTableToken().toString());
2285          if (symbolVariable != null){
2286              cr.getTableToken().setDbObjectType(EDbObjectType.variable);
2287              //TTable sourceTable = new TTable(new TObjectName(EDbObjectType.table,symbolVariable.getVariableName().getStartToken()));
2288              TTable sourceTable = new TTable(TObjectName.createObjectName (this.dbvendor, EDbObjectType.variable,symbolVariable.getVariableName().getStartToken()));
2289              sourceTable.getLinkedColumns().addObjectName(cr);
2290              cr.setSourceTable(sourceTable);
2291             // symbolVariable.getVariableName().getReferencedObjects().addObjectName(cr);
2292             // System.out.println("find variable:"+cr.toString());
2293              cr.setResolveStatus(TBaseType.RESOLVED_AND_FOUND); // set resolve status to resolved,避免在 TAttributeResolver 中关联到其他 table
2294              return true;
2295          }
2296
2297      }else{
2298          // variable
2299          symbolVariable =  TSymbolTableManager.searchSymbolVariable(this.getFrameStack(),cr.toString());
2300          if (symbolVariable != null){
2301              cr.setDbObjectType(EDbObjectType.variable);
2302              symbolVariable.getVariableName().getReferencedObjects().addObjectName(cr);
2303              return true;
2304          }
2305      }
2306
2307        // check parameters in plsql only, may add support for sql server later.
2308      if(! ((dbvendor == EDbVendor.dbvoracle)||(dbvendor == EDbVendor.dbvmysql))) return false;
2309      if (cr.getObjectType() == TObjectName.ttobjVariable) return true;
2310
2311        Stack symbolTable = this.getTopStatement().getSymbolTable();
2312        TSymbolTableItem item = null;
2313        TObjectName objName = null;
2314        TObjectName qualifiedName = null; // function/procedure name or label name of plsql block
2315        for (int i = symbolTable.size()-1;i>=0;i--){
2316            item = (TSymbolTableItem)symbolTable.get(i);
2317            if (item.getData() instanceof TParameterDeclaration){
2318                objName = ((TParameterDeclaration)item.getData()).getParameterName();
2319            }else if (item.getData() instanceof TVarDeclStmt){
2320                objName = ((TVarDeclStmt)item.getData()).getElementName();
2321            }else  if (item.getData() instanceof TObjectName){
2322                objName = (TObjectName)item.getData();
2323            }
2324
2325            if (objName == null) continue;
2326
2327            // strip quote characters so a quoted declaration ("P_DATE" IN NUMBER)
2328            // still matches an unquoted reference P_DATE (MantisBT 4504)
2329            if (TBaseType.getTextWithoutQuoted(cr.toString())
2330                    .compareToIgnoreCase(TBaseType.getTextWithoutQuoted(objName.toString())) == 0){
2331                ret = true;
2332                if (checkVariableDeclaredInProcedure) break; // return true if variable declared in procedure
2333                for(int j=0;i<tables.size();i++){
2334                    TTable lcTable = tables.getTable(j);
2335                    if (lcTable.isBaseTable()){
2336                        if (fireOnMetaDatabaseTableColumn(
2337                                             lcTable.getPrefixServer()
2338                                            ,lcTable.getPrefixDatabase()
2339                                            ,lcTable.getPrefixSchema()
2340                                            ,lcTable.getName()
2341                                            ,cr.getColumnNameOnly())){
2342                            ret = false;
2343                            break;
2344                        }
2345                    }
2346                }
2347
2348                if (ret)  break;
2349            }else if (cr.toString().indexOf(".")>0){
2350                // qualified object reference, compare it with procedure/function/block label prefixed
2351                if (item.getStmt() instanceof TPlsqlCreateFunction){
2352                   qualifiedName = ((TPlsqlCreateFunction)item.getStmt()).getFunctionName();
2353                }else if (item.getStmt() instanceof TPlsqlCreateProcedure){
2354                   qualifiedName = ((TPlsqlCreateProcedure)item.getStmt()).getProcedureName();
2355                }else if (item.getStmt() instanceof TCommonBlock){
2356                   qualifiedName = ((TCommonBlock)item.getStmt()).getLabelName();
2357                }
2358
2359                if (qualifiedName != null){
2360                    if (TBaseType.getTextWithoutQuoted(cr.toString()).compareToIgnoreCase(
2361                            TBaseType.getTextWithoutQuoted(qualifiedName.toString()) + '.'
2362                                    + TBaseType.getTextWithoutQuoted(objName.toString())) == 0){
2363                        ret = true;
2364                    }
2365                }
2366
2367                if (ret ) break;
2368            }
2369
2370        }
2371          if (ret){
2372              //add this parameter or variable reference to original parameter/variable
2373                  objName.getReferencedObjects().addObjectName(cr);
2374                  cr.setObjectType(TObjectName.ttobjVariable);
2375          }
2376          return ret;
2377    }
2378
2379    TCTE findCTEByName(String cteName){
2380        TCTEList lcCteList = searchCTEList(false);
2381        TCTE lcCte = null;
2382        if (lcCteList != null){
2383          for(int i=0;i<lcCteList.size();i++){
2384             if (lcCteList.getCTE(i).getTableName().toString().compareToIgnoreCase(cteName)==0){
2385                 lcCte = lcCteList.getCTE(i);
2386                 break;
2387             }
2388          }
2389        }
2390     return lcCte;
2391    }
2392
2393    /**
2394     * @deprecated since 2.3.8.2, use {@link TTable#getExpandedStarColumns()} instead.
2395     *
2396     * @param lcTable
2397     * @return
2398     */
2399    public ArrayList<String> getColumnsInTable(TTable lcTable){
2400        if (lcTable.isCTEName()){
2401            ArrayList<String> columns = new ArrayList<>();
2402            if (lcTable.getCteColomnReferences()!=null){
2403                for(TObjectName n:lcTable.getCteColomnReferences()){
2404                    columns.add(n.toString());
2405                }
2406            }else if (lcTable.getCTE().getSubquery() != null && lcTable.getCTE().getSubquery().getResultColumnList() != null){
2407                for(TResultColumn resultColumn:lcTable.getCTE().getSubquery().getResultColumnList()){
2408                    columns.add(resultColumn.getDisplayName());
2409                }
2410            }
2411            return columns;
2412        }else{
2413            return getColumnsInTable(
2414                    lcTable.getPrefixServer()
2415                    ,lcTable.getPrefixDatabase()
2416                    ,lcTable.getPrefixSchema()
2417                    ,lcTable.getName()
2418            );
2419        }
2420    }
2421
2422
2423    /**
2424     * @deprecated since 2.3.8.2, use {@link TTable#getExpandedStarColumns()} instead.
2425     *
2426     * @param pServer
2427     * @param pDatabase
2428     * @param pSchema
2429     * @param pTable
2430     * @return
2431     */
2432    public ArrayList<String> getColumnsInTable(String pServer,String pDatabase,String pSchema,String pTable){
2433        TFrame stackFrame = getFrameStack().firstElement();
2434        TGlobalScope globalScope = (TGlobalScope)stackFrame.getScope();
2435
2436        if (globalScope.getSqlEnv() != null){
2437            return globalScope.getSqlEnv().getColumnsInTable(pDatabase+"."+pSchema+"."+pTable,false);
2438        }else{
2439            return  null;
2440        }
2441    }
2442
2443
2444    public boolean fireOnMetaDatabaseTableColumn(String pServer,String pDatabase,String pSchema,String pTable,String pColumn){
2445//        boolean lcResult = false;
2446//        if (this.getGsqlparser().getMetaDatabase() != null){
2447//            lcResult = this.getGsqlparser().getMetaDatabase().checkColumn(pServer,pDatabase,pSchema,pTable,pColumn);
2448//        }
2449
2450        TFrame stackFrame = getFrameStack().firstElement();
2451        TGlobalScope globalScope = (TGlobalScope)stackFrame.getScope();
2452
2453        if (globalScope.getSqlEnv() != null){
2454           // System.out.println(globalScope.getSqlEnv().toString());
2455
2456            return globalScope.getSqlEnv().columnInTable(pDatabase+"."+pSchema+"."+pTable,pColumn);
2457        }else{
2458            return  false;
2459        }
2460
2461//        return lcResult;
2462    }
2463
2464    public TTable getFirstPhysicalTable(){
2465        TTable ret = null;
2466        if (tables.size() == 0) return null;
2467        for(int i=0;i<tables.size();i++){
2468            if (tables.getTable(i).isBaseTable()) {
2469                ret = tables.getTable(i);
2470                break;
2471            }
2472        }
2473        return ret;
2474    }
2475    private TObjectNameList orphanColumns = null;
2476
2477    public TObjectNameList getOrphanColumns() {
2478        if (orphanColumns == null) orphanColumns = new TObjectNameList();
2479        return orphanColumns;
2480    }
2481
2482    protected boolean linkToFirstTable(TObjectName pColumn,int pCandidateTableCnt){
2483        boolean lcResult = false;
2484        // ClickHouse expression-CTE alias resolved by an outer scope during
2485        // the parent-walk: the reference is a scalar symbol — guessing it onto
2486        // this scope's first table would fabricate lineage (and did, when
2487        // metadata ruled the inner table out and the outer WITH alias won).
2488        if ((pColumn.getExpressionCteRef() != null) && (pColumn.getSourceTable() == null)){
2489            return true;
2490        }
2491        if ((dbvendor == EDbVendor.dbvteradata)&&(pColumn.isQualified())&&(pColumn.getTableToken().getDbObjectType() != EDbObjectType.subquery_alias)){
2492            // update table1 set col = 'value' where table1.id = table2.id2
2493            boolean isFoundLinkedTable = false;
2494            TCustomSqlStatement lcSql = this;
2495            while (lcSql != null){
2496                int i = 0;
2497                i = lcSql.tables.searchTableByNameOrAlias(pColumn.getTableToken().toString());
2498                isFoundLinkedTable = ( i != -1);
2499                if (isFoundLinkedTable) {
2500                    if (lcSql.tables.getTable(i).getEffectType() == ETableEffectType.tetImplicitLateralDerivedTable ){
2501                        // 如果不查重table,会导致 employee.first_name 中的 employee 被第二次加到 tables 中
2502//                        DELETE FROM foodmart.trimmed_employee ACT
2503//                        WHERE ACT.employee_id = employee.employee_id
2504//                        AND  employee.first_name = 'Walter'
2505//                        AND  trimmed_salary.employee_id = -1
2506
2507                        TTable newTable = lcSql.tables.getTable(i);
2508                        newTable.getLinkedColumns().addObjectName(pColumn);
2509                        pColumn.setSourceTable(newTable);
2510                        pColumn.setValidate_column_status(TBaseType.COLUMN_LINKED_TO_TABLE_IN_OLD_ALGORITHM);
2511                    }
2512                    break;
2513                }
2514                lcSql = lcSql.getParentStmt();
2515            }
2516            if (!isFoundLinkedTable){
2517                TTable newTable = null;
2518                if (pColumn.getDatabaseToken() == null){
2519
2520                    //newTable = new TTable(new TObjectName(EDbObjectType.table,pColumn.getTableToken()));
2521                    newTable = new TTable(TObjectName.createObjectName (this.dbvendor, EDbObjectType.table,pColumn.getTableToken()));
2522                    newTable.setStartToken(pColumn.getTableToken());
2523                    newTable.setEndToken(pColumn.getTableToken());
2524                }else{
2525
2526                    //newTable = new TTable(new TObjectName(EDbObjectType.table,pColumn.getSchemaToken(), pColumn.getTableToken()));
2527                    newTable = new TTable(TObjectName.createObjectName (this.dbvendor,EDbObjectType.table,pColumn.getDatabaseToken(), pColumn.getTableToken()));
2528                    newTable.setStartToken(pColumn.getSchemaToken());
2529                    newTable.setEndToken(pColumn.getTableToken());
2530                }
2531
2532                newTable.setTableType(ETableSource.objectname);
2533                newTable.setEffectType(ETableEffectType.tetImplicitLateralDerivedTable);
2534                newTable.getLinkedColumns().addObjectName(pColumn);
2535                pColumn.setSourceTable(newTable);
2536                pColumn.setValidate_column_status(TBaseType.COLUMN_LINKED_TO_TABLE_IN_OLD_ALGORITHM);
2537                this.addToTables(newTable);
2538
2539                // 2024 年
2540                // 不能加入到 relations 中,否则会导致 下面 SQL 中 star column 同时链接到 SPCOMM.L_FIXED_RATE_PLAN_REF, ipshare_ofccplv.cprof_d_period_dates_ref
2541                // 从而导致 本来不该有的歧义产生
2542
2543                // UPDATE b_rate_plan
2544                //FROM
2545                //(
2546                //SELECT * FROM SPCOMM.L_FIXED_RATE_PLAN_REF
2547                //WHERE rate_plan_ref_eff_dt<= ipshare_ofccplv.cprof_d_period_dates_ref.PERIOD
2548                //) AS ref
2549                //SET accs_fee = REF.accs_fee,
2550                //SVC_TYPE = REF.prod_grp_lvl_1,
2551                //rate_plan_lvl3 = REF.rate_plan_lvl_3,
2552                //prod_grp_lvl3 = REF.prod_grp_lvl_2
2553                //WHERE b_rate_plan.svc_type IS NULL
2554
2555                // 2025/2/25, v3.0.4.8
2556                // 需要加入到 relations 中,新的 gudusoft.gsqlparser.resolver package 中的算法会处理这种情况
2557                // teradata 的隐式横向派生表不能加入到关系解析器中
2558                // this.getRelations().add(newTable);
2559            }
2560            return true;
2561        }
2562        if (pColumn.getCandidateTables().size() == 1){
2563            TTable table = pColumn.getCandidateTables().getTable(0);
2564            table.getLinkedColumns().addObjectName(pColumn);
2565            pColumn.setSourceTable(table);
2566            lcResult = true;
2567        }
2568        else if ((tables.size() == 1) || (pCandidateTableCnt == 1)){
2569            TTable table = tables.getTable(0);
2570
2571            if(table.getTableType() == ETableSource.function){
2572                //lcResult = linkToFunctionTable(table, pColumn);
2573                int iRet = table.getFuncCall().isColumnInThisTableFunction(this.getSqlEnv(),this.dbvendor,pColumn);
2574                if ( iRet == TBaseType.COLUMN_IN_TABEL_FUNCTION_YES){
2575                    lcResult = true;
2576                    table.getLinkedColumns().addObjectName(pColumn);
2577                    pColumn.setSourceTable(table);
2578                    lcResult = true;
2579                }else if ( iRet == TBaseType.COLUMN_IN_TABEL_FUNCTION_NO){
2580                    lcResult = false;
2581                }else{
2582                    table.getLinkedColumns().addObjectName(pColumn);
2583                    pColumn.setSourceTable(table);
2584                    lcResult = true;
2585                }
2586            }else if(table.getTableType() == ETableSource.subquery){
2587                if (! table.getSubquery().searchColumnInResultSet(pColumn,(tables.size()==1))){
2588                    getOrphanColumns().addObjectName(pColumn);
2589                    pColumn.setOrphanColumn(true);
2590                    pColumn.setOwnStmt(this);
2591                    TSourceToken st = pColumn.getStartToken();
2592                    if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){
2593                        TBaseType.log(String.format("Add orphan column <%s> to statement in old algorithm in subquery %s",pColumn.toString(),table.getAliasName()),TLog.WARNING,table);
2594                    }
2595                    this.parseerrormessagehandle(new TSyntaxError(st.getAstext(), st.lineNo, st.columnNo
2596                            ,"find orphan column", EErrorType.sphint
2597                            , TBaseType.MSG_HINT_FIND_ORPHAN_COLUMN,this,st.posinlist,pColumn));
2598                }
2599            }
2600            else{
2601                table.getLinkedColumns().addObjectName(pColumn);
2602                pColumn.setSourceTable(table);
2603                lcResult = true;
2604                if ((dbvendor == EDbVendor.dbvbigquery)&&(pCandidateTableCnt == 0) && (pColumn.isQualified())){
2605                    // bigquery struct column used in query
2606//                    create view test as (SELECT rollNo,
2607//                            info.name as n1,
2608//                    info2.name as n2,
2609//                            info.age from my_first_dataset.student_records);
2610
2611                    pColumn.columnToProperty();
2612                }
2613            }
2614        }else if (tables.size() > 1){
2615            // if there is only a table without table alias, then, link to this table
2616            boolean foundOnlyOneTable = false;
2617            TTable tableWithoutAlias = null;
2618            for(TTable table:tables){
2619                if (table.isCTEName()) continue; // CTE 即便没有 指定alias,也不作为考虑对象
2620                if (table.getAliasClause() == null){
2621                    tableWithoutAlias = table;
2622                    if (foundOnlyOneTable){
2623                        foundOnlyOneTable = false;
2624                        break;
2625                    }else{
2626                        foundOnlyOneTable = true;
2627                    }
2628                }
2629            }
2630
2631            if (foundOnlyOneTable){
2632                tableWithoutAlias.getLinkedColumns().addObjectName(pColumn);
2633                pColumn.setSourceTable(tableWithoutAlias);
2634                lcResult = true;
2635            }else{
2636                getOrphanColumns().addObjectName(pColumn);
2637                pColumn.setOrphanColumn(true);
2638                pColumn.setOwnStmt(this);
2639                TSourceToken st = pColumn.getStartToken();
2640                if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){
2641                    TBaseType.log(String.format("Add orphan column <%s> to statement in old algorithm ",pColumn.toString()),TLog.WARNING,this);
2642                }
2643
2644                this.parseerrormessagehandle(new TSyntaxError(st.getAstext(), st.lineNo, st.columnNo
2645                        ,"find orphan column", EErrorType.sphint
2646                        , TBaseType.MSG_HINT_FIND_ORPHAN_COLUMN,this,st.posinlist,pColumn));
2647            }
2648
2649        }
2650        return  lcResult;
2651    }
2652
2653    private boolean linkToFunctionTable(TTable table, TObjectName pColumn) {
2654        if(table.getTableName().toString().toUpperCase().equals("STRING_SPLIT")){
2655            if(pColumn.getColumnNameOnly().toUpperCase().equals("VALUE")){
2656                table.getLinkedColumns().addObjectName(pColumn);
2657                pColumn.setSourceTable(table);
2658                return true;
2659            }
2660            else return false;
2661        }
2662        else {
2663            table.getLinkedColumns().addObjectName(pColumn);
2664            pColumn.setSourceTable(table);
2665            return true;
2666        }
2667    }
2668
2669
2670    private TTable findInsertedOrDeleteTable(TTable table) {
2671        if (table == null) return null;
2672        
2673        // Check if this table is 'inserted' by examining both name and toString()
2674        // The inserted table might have different representations
2675        String tableName = table.getName();
2676        String tableString = table.toString();
2677        
2678        if ("inserted".equalsIgnoreCase(tableName) || 
2679            "inserted".equalsIgnoreCase(tableString) ||
2680            "deleted".equalsIgnoreCase(tableName) ||
2681            "deleted".equalsIgnoreCase(tableString)) {
2682            return table;
2683        }
2684        
2685        // If this is a join table, recursively check its components
2686        if (table.getTableType() == ETableSource.join && table.getJoinExpr() != null) {
2687            TJoinExpr joinExpr = table.getJoinExpr();
2688            
2689            // Check left side recursively
2690            TTable leftResult = findInsertedOrDeleteTable(joinExpr.getLeftTable());
2691            if (leftResult != null) {
2692                return leftResult;
2693            }
2694            
2695            // Check right side recursively  
2696            TTable rightResult = findInsertedOrDeleteTable(joinExpr.getRightTable());
2697            if (rightResult != null) {
2698                return rightResult;
2699            }
2700        }
2701        
2702        return null;
2703    }
2704   
2705
2706    boolean isSQLServerInsertedDelete(TObjectName pColumn){
2707        if (dbvendor != EDbVendor.dbvmssql) return false;
2708
2709        // only process sql server inserted delete column, if not then return false
2710       // if (!((pColumn.toString().toUpperCase().startsWith("INSERTED"))||(pColumn.toString().toUpperCase().startsWith("DELETED")))) return false;
2711        if (!((pColumn.getStartToken().tokencode == TBaseType.rrw_sqlserver_INSERTED )
2712                    ||(pColumn.getStartToken().tokencode == TBaseType.rrw_sqlserver_DELETED ))) return false;
2713
2714        // we need to get the target table in this statement's from clause which can be complex join, so we need to iterate all tables in this statement's from clause
2715        // to find the target table in the literal as 'inserted' or 'deleted'.
2716
2717        TTable lcTargetTable = null;
2718        for(TTable table : this.getRelations()){
2719            lcTargetTable = findInsertedOrDeleteTable(table);
2720            if (lcTargetTable != null){
2721                break;
2722            }
2723        }
2724
2725        if (lcTargetTable == null) return false;
2726
2727
2728        if (lcTargetTable.getLinkTable() != null){
2729            lcTargetTable.getLinkTable().getLinkedColumns().addObjectName(pColumn);
2730            pColumn.setSourceTable(lcTargetTable.getLinkTable());
2731
2732            pColumn.setResolveStatus(TBaseType.RESOLVED_AND_FOUND); // 避免在 // TAttributeResolver 中再次进行处理,关联到其他 table
2733
2734        }else{
2735            lcTargetTable.getLinkedColumns().addObjectName(pColumn);
2736            pColumn.setSourceTable(lcTargetTable);
2737            pColumn.setResolveStatus(TBaseType.RESOLVED_AND_FOUND); // 避免在 // TAttributeResolver 中再次进行处理,关联到其他 table
2738        }
2739        return true;
2740
2741    }
2742
2743    boolean isOracleNewOldTable(TObjectName pColumn){
2744        boolean ret = false;
2745        if (dbvendor != EDbVendor.dbvoracle) return false;
2746        if (!(pColumn.isQualified())) return false;
2747        if ((pColumn.getTableString().equalsIgnoreCase(":new"))
2748            ||(pColumn.getTableString().equalsIgnoreCase(":old"))){
2749            if (getAncestorStmt().tables != null){
2750                if (getAncestorStmt().tables.size() > 0){
2751                    getAncestorStmt().tables.getTable(0).getLinkedColumns().addObjectName(pColumn);
2752                    pColumn.setSourceTable(getAncestorStmt().tables.getTable(0));
2753                    ret = true;
2754                }
2755            }
2756        }
2757
2758        return ret;
2759    }
2760
2761    public boolean searchDaxVariableInStack(TObjectName pName){
2762        boolean ret = false;
2763        if (getVariableStack().size() == 0) return false;
2764        if (pName.getDbObjectType() == EDbObjectType.column) return false;
2765        for(int i=0;i<variableStack.size();i++){
2766            if (pName.toString().equalsIgnoreCase(((TObjectName) variableStack.get(i)).toString())){
2767                ret = true;
2768                break;
2769            }
2770        }
2771        return ret;
2772    }
2773
2774    boolean linkColumnToTableDax(TObjectName pColumn, ESqlClause pLocation){
2775        boolean lcResult = true ;
2776        TDaxFunction daxFunction = null;
2777        if (searchDaxVariableInStack(pColumn)) return false;
2778        if (getDaxFunctionStack().size() > 0){
2779            daxFunction = daxFunctionStack.peek();
2780        }
2781
2782        if (pColumn.getTableToken() != null){
2783            //TTable sourceTable = new TTable(new TObjectName(EDbObjectType.table,pColumn.getTableToken()));
2784            TTable sourceTable = new TTable(TObjectName.createObjectName (this.dbvendor,EDbObjectType.table,pColumn.getTableToken()));
2785            sourceTable.getLinkedColumns().addObjectName(pColumn);
2786            addToTables(sourceTable);
2787            if ((daxFunction != null) && (daxFunction.getDefaultTable() == null)){
2788                daxFunction.setDefaultTable(sourceTable);
2789            }
2790        }else{
2791            if ((daxFunction != null) &&(daxFunction.getDefaultTable() != null)){
2792                daxFunction.getDefaultTable().getLinkedColumns().addObjectName(pColumn);
2793            }else{
2794                ((TDaxStmt)this).getDefaultTable().getLinkedColumns().addObjectName(pColumn);
2795            }
2796        }
2797        return  lcResult;
2798    }
2799
2800    /**
2801     * 将列引用解析并绑定到其来源(表、子查询、CTE、表函数、OPENQUERY/UNNEST 等)。
2802     *
2803     * 功能概述:
2804     * 1) 针对 DAX 语法直接走 DAX 分支。
2805     * 2) 已绑定或标记“延迟到列解析器”的列直接返回。
2806     * 3) 设定列所在语法位置,并校验列名/保留字(含 MySQL true/false/default、内置函数等)。
2807     * 4) 处理厂商伪表/特殊前缀(Oracle :new/:old;SQL Server INSERTED/DELETED)。
2808     * 5) Insert All/VALUES 场景:优先在子查询结果集中/变量或过程参数中匹配。
2809     * 6) 在当前语句的 FROM 表集合中查找并建立绑定:
2810     *    - 限定列 table.col:按别名/表名匹配;对子查询/CTE/OPENQUERY 进一步在结果集中定位源列;
2811     *      命中后写入 linkedColumns,必要时将 TableToken 标记为 subquery_alias。
2812     *    - 非限定列 col:
2813     *      a. 先尝试同层 SELECT 列别名(支持 LATERAL 语义且位置在别名之后);
2814     *      b. 处理通配符“*”:收集所有来源表;
2815     *      c. 基础表通过元数据回调 fireOnMetaDatabaseTableColumn 校验;未命中则记录候选;
2816     *      d. 子查询/CTE/函数/UNNEST/PIVOT 分别按各自规则匹配。
2817     * 7) 命中后将列加入表的 linkedColumns 并设置 sourceTable/sourceColumn,必要时维持 isContinue 以继续匹配“*”。
2818     * 8) 若未命中:尝试变量/参数;再按条件(语句类型/位置/是否限定等)向上一层语句递归查找(维护 searchLevel)。
2819     * 9) 仍未命中:在顶层(searchLevel==0)按“候选唯一/或首表”兜底策略 {@link #linkToFirstTable(TObjectName, int)}。
2820     *
2821     * 参数:
2822     * @param pColumn   需要绑定的列名对象(方法会更新其 location、sourceTable、sourceColumn 等)
2823     * @param pLocation 列出现的语法位置(如 selectList、where、insertValues 等)
2824     *
2825     * 返回值:
2826     * @return 成功绑定到某个来源返回 true;未能绑定或被识别为变量/保留字等返回 false
2827     *
2828     * 厂商兼容:
2829     * - Oracle: 处理 :new/:old,Insert All 的 values 子句源自子查询的匹配
2830     * - SQL Server: 处理 INSERTED/DELETED 伪表
2831     * - MySQL: 对保留字/布尔字面量/内置函数名的特殊判断
2832     * - DAX: 委托 {@link #linkColumnToTableDax(TObjectName, ESqlClause)}
2833     *
2834     * 副作用:
2835     * - 更新 pColumn 的 location/searchLevel/sourceTable/sourceColumn/validate 状态
2836     * - 向命中的表写入 linkedColumns 或向别名列写入 targetColumns
2837     * - 对“*”列填充 sourceTableList;对子查询命中时可能将 TableToken 标为 subquery_alias
2838     * - 记录候选表数量并填充 pColumn.candidateTables,用于后续兜底
2839     *
2840     * 复杂度与顺序:
2841     * - 优先使用同层信息(别名/元数据/子查询结果),再逐层向外查找;避免无谓的上层搜索
2842     *
2843     * 注意:
2844     * - 本方法完成“旧算法”的快速联接,新的解析/消歧逻辑在解析器(如 TStmtScope/TAttributeResolver)中继续处理
2845     */
2846    /**
2847     * ClickHouse only: the expression-CTE ({@code WITH <expr> AS ident}) whose
2848     * alias matches the unqualified column reference, or null. The search
2849     * covers this statement's own WITH list and ascends ONLY through
2850     * set-operation membership (a compound query's WITH list is shared with
2851     * its members). It must NOT walk arbitrary lexical parents: ClickHouse
2852     * resolves closest-scope-first, so an outer alias may only win after this
2853     * scope's own tables failed — which the existing parent-walk recursion in
2854     * linkColumnToTable already provides (the recursion re-enters this check
2855     * at the parent's own level). A reference located inside a CTE's own
2856     * defining expression never matches that CTE (it is a column named like
2857     * the alias, not a self-reference).
2858     */
2859    private TCTE findExpressionCteInList(TCTEList lcCteList, TObjectName pColumn){
2860        if (lcCteList == null) return null;
2861        for(int i=0;i<lcCteList.size();i++){
2862            TCTE lcCte = lcCteList.getCTE(i);
2863            if ((lcCte.getExpression() == null) || (lcCte.getTableName() == null)) continue;
2864            if ((pColumn.getStartToken() != null)
2865                    && (lcCte.getExpression().getStartToken() != null)
2866                    && (lcCte.getExpression().getEndToken() != null)
2867                    && (pColumn.getStartToken().posinlist >= lcCte.getExpression().getStartToken().posinlist)
2868                    && (pColumn.getStartToken().posinlist <= lcCte.getExpression().getEndToken().posinlist)){
2869                continue;
2870            }
2871            if (SQLUtil.sameName(dbvendor, ESQLDataObjectType.dotColumn,
2872                    pColumn.toString(), lcCte.getTableName().toString())){
2873                return lcCte;
2874            }
2875        }
2876        return null;
2877    }
2878
2879    private TCTE findExpressionCteForAlias(TObjectName pColumn){
2880        TCustomSqlStatement stmt = this;
2881        while (stmt != null){
2882            TCTE found = findExpressionCteInList(stmt.getCteList(), pColumn);
2883            if (found != null) return found;
2884            if (!((stmt instanceof TSelectSqlStatement)
2885                    && ((TSelectSqlStatement)stmt).isChildOfCombinedQuery())) break;
2886            stmt = stmt.parentStmt;
2887        }
2888        return null;
2889    }
2890
2891    /**
2892     * ClickHouse only: like {@link #findExpressionCteForAlias} but across every
2893     * enclosing statement. Used ONLY where the local scope has already failed
2894     * to PROVE the column (e.g. an unverifiable table-function column) — a
2895     * proven outer alias then outranks the local guess.
2896     */
2897    private TCTE findExpressionCteForAliasInAnyScope(TObjectName pColumn){
2898        for(TCustomSqlStatement stmt = this; stmt != null; stmt = stmt.parentStmt){
2899            TCTE found = findExpressionCteInList(stmt.getCteList(), pColumn);
2900            if (found != null) return found;
2901        }
2902        return null;
2903    }
2904
2905    public boolean linkColumnToTable(TObjectName pColumn, ESqlClause pLocation){
2906        boolean lcResult = false,isContinue = false;
2907        int candidateTableCnt = 0;
2908        if (pColumn == null) return false;
2909        if (dbvendor == EDbVendor.dbvdax){
2910            return linkColumnToTableDax(pColumn,pLocation);
2911        }
2912
2913        // Skip alias definition columns - they define column names in alias clauses, not column references
2914        // Example: In "AS x (numbers, animals)", numbers and animals are column_alias type
2915        if (pColumn.getDbObjectType() == EDbObjectType.column_alias) {
2916            return true;
2917        }
2918
2919        if (pColumn.getSourceTable() != null) {
2920            lcResult = true;
2921            return lcResult;
2922        }
2923
2924        if (pColumn.getResolveStatus() == TBaseType.RESOLVE_DELAY_TO_COLUMN_RESOLVER) return true;
2925
2926        pColumn.setLocation(pLocation);
2927
2928        if (! pColumn.isValidColumnName(dbvendor)) {
2929            if (pColumn.isReservedKeyword()){
2930                if (
2931                        ((pColumn.getStartToken().tokencode != TBaseType.rrw_mysql_true)&&(!(pColumn.getStartToken().toString().equalsIgnoreCase("true"))))
2932                        &&((pColumn.getStartToken().tokencode != TBaseType.rrw_mysql_false)&&(!(pColumn.getStartToken().toString().equalsIgnoreCase("false"))))
2933                        &&(pColumn.getStartToken().tokencode != TBaseType.rrw_mysql_default)
2934                        &&(pColumn.getStartToken().tokencode != TBaseType.rrw_on)&&(!(pColumn.getStartToken().toString().equalsIgnoreCase("on")))
2935                   ) {
2936                        boolean mysqlBuiltFunction = false;
2937                        if (dbvendor == EDbVendor.dbvmysql){
2938                            mysqlBuiltFunction = functionChecker.isBuiltInFunction(pColumn.toString(),EDbVendor.dbvmysql,"6.0");
2939                        }
2940                        if (!mysqlBuiltFunction){
2941                            TSourceToken st1 = pColumn.getStartToken();
2942                            TSyntaxError err = new TSyntaxError(st1.toString()
2943                                    , st1.lineNo, st1.columnNo
2944                                    , String.format("Reserved keyword can't be column name")
2945                                    , EErrorType.sperror
2946                                    , TBaseType.MSG_ERROR_RESERVED_KEYWORD_CANT_USED_AS_COLUMN_NAME
2947                            ,this,st1.posinlist);
2948                            this.parseerrormessagehandle(err);
2949                        }
2950                }
2951            }
2952            return false;
2953        }
2954
2955        if (isOracleNewOldTable(pColumn)) return true;
2956        if (isSQLServerInsertedDelete(pColumn)) return true;
2957
2958        // oracle insert all statement,
2959        // WHEN id <= 3 THEN INTO dest_tab1 VALUES(id, description1)
2960        // column in values clause must be in the subquery of insert all statement
2961        if (pColumn.getLocation() == ESqlClause.insertValues){
2962            if (this instanceof TInsertSqlStatement){
2963                TInsertSqlStatement insertSqlStatement = (TInsertSqlStatement)this;
2964                if (insertSqlStatement.isInsertAll()){
2965                   // if (pColumn.getStartToken().tokencode == TBaseType.rrw_snowflake_default) return true;
2966                    lcResult = insertSqlStatement.getSubQuery().searchColumnInResultSet(pColumn, true);
2967                }
2968            }
2969
2970            if (lcResult) return true;
2971
2972            // value in values clause maybe parameter of the procedure/function parameter
2973            lcResult = locateVariableOrParameter(pColumn,true);
2974            if (lcResult) return true;
2975        }
2976
2977        // ClickHouse expression-CTE alias: WITH <expr> AS ident makes ident a
2978        // scalar symbol of the consuming query, not a column of its FROM
2979        // tables (default ClickHouse semantics: the alias shadows a
2980        // same-named table column). Binding it to a table would fabricate
2981        // lineage; resolve it to the CTE instead, before any table search.
2982        if ((dbvendor == EDbVendor.dbvclickhouse) && (!pColumn.isQualified())){
2983            TCTE lcExprCte = findExpressionCteForAlias(pColumn);
2984            if (lcExprCte != null){
2985                pColumn.setExpressionCteRef(lcExprCte);
2986                pColumn.setResolveStatus(TBaseType.RESOLVED_AND_FOUND);
2987                return false;
2988            }
2989        }
2990
2991        boolean foundInMetaData = false;
2992
2993        for(int i=0;i<tables.size();i++){
2994            TTable lcTable = tables.getTable(i);
2995            if (lcTable.getEffectType() == ETableEffectType.tetSelectInto) continue;
2996            if (lcTable.getEffectType() == ETableEffectType.tetImplicitLateralDerivedTable) continue;
2997
2998            if (pColumn.isQualified()){
2999                lcResult = pColumn.resolveWithThisTable(lcTable);
3000                if ((lcResult) && (lcTable.getTableType() == ETableSource.subquery)){
3001                    pColumn.getTableToken().setDbObjectType(EDbObjectType.subquery_alias);
3002
3003                    int lcPos = lcTable.searchColumnInAlias(pColumn);
3004                    lcResult = lcPos>=0;
3005                    if (lcResult){
3006                        // 在 alias 中找到 source column, 还需要对应到 subquery select 中的 select list
3007                        // sql 见 https://e.gitee.com/gudusoft/projects/151613/tasks/list?issue=I8JR0W#note_23051633
3008                        if ((lcTable.getSubquery() != null)&&(lcTable.getSubquery().getResultColumnList() != null)){
3009                            pColumn.setSourceColumn(lcTable.getSubquery().getResultColumnList().getResultColumn(lcPos));
3010                        }
3011                    }else{
3012                        lcResult =  lcTable.getSubquery().searchColumnInResultSet(pColumn,true);
3013                    }
3014               //     lcTable.getSubquery().searchColumnInResultSet(pColumn,true);
3015
3016                }else if ((lcResult) && (lcTable.isCTEName())){
3017                    lcTable.getCTE().searchColumnInResultSet(this,lcTable,pColumn,true);
3018                }else if ((lcResult) && (lcTable.getTableType() == ETableSource.openquery)&&(lcTable.getSubquery() != null)){
3019                    pColumn.getTableToken().setDbObjectType(EDbObjectType.subquery_alias);
3020                    lcTable.getSubquery().searchColumnInResultSet(pColumn,true);
3021//                }else if ((lcResult) && (lcTable.getTableType() == ETableSource.unnest)&&(lcTable.getUnnestClause() != null)){
3022//                    pColumn.getTableToken().setDbObjectType(EDbObjectType.subquery_alias);
3023//                    lcTable.getSubquery().searchColumnInResultSet(pColumn,true);
3024                }
3025                if (lcResult&&pColumn.toString().endsWith("*")){
3026                    pColumn.getSourceTableList().add(lcTable);
3027//                    ArrayList<String> lcColumns = getColumnsInTable(lcTable);
3028//                    if (lcColumns != null){
3029//                        pColumn.getColumnsLinkedToStarColumn().addAll(lcColumns);
3030//                    }
3031                }
3032            }else {
3033              // column not qualified
3034
3035                // check if this is the column alias in current select list.
3036                // The binding rule itself lives in TResultColumnList.findLateralAliasDefinition()
3037                // so that this path and resolver2's handleSelectListAliasResolution() can never
3038                // disagree about what counts as an alias reference (MantisBT 4659).
3039                if((!lcResult)&& ((!pColumn.isQualified()) && (this instanceof TSelectSqlStatement)&&(getResultColumnList() !=null))){
3040                    TResultColumn lcField = getResultColumnList().findLateralAliasDefinition(dbvendor, pColumn);
3041                    if (lcField != null){
3042                        pColumn.setSourceColumn(lcField);
3043                        lcField.getTargetColumns().addObjectName(pColumn);
3044                        pColumn.setValidate_column_status(TBaseType.COLUMN_LINKED_TO_COLUMN_ALIAS_IN_OLD_ALGORITHM);
3045                        return true;
3046                    }
3047                }
3048
3049                if (pColumn.getColumnNameOnly().equalsIgnoreCase("*")){
3050                    lcResult = true;
3051                    isContinue = true; // in order to match next table in the from clause
3052                    pColumn.getSourceTableList().add(lcTable);
3053//                    ArrayList<String> lcColumns = getColumnsInTable(lcTable);
3054//                    if (lcColumns != null){
3055//                        pColumn.getColumnsLinkedToStarColumn().addAll(lcColumns);
3056//                    }
3057                }else if (lcTable.isBaseTable()){
3058                    lcResult = fireOnMetaDatabaseTableColumn(
3059                                         lcTable.getPrefixServer()
3060                                        ,lcTable.getPrefixDatabase()
3061                                        ,lcTable.getPrefixSchema()
3062                                        ,lcTable.getName()
3063                                        ,pColumn.getColumnNameOnly());
3064                    if (! lcResult) {
3065                        candidateTableCnt++;
3066                        pColumn.getCandidateTables().addTable(lcTable);
3067                    }else{
3068                        foundInMetaData = true;
3069                        isContinue = false;
3070                    }
3071
3072                }else if ((lcTable.getTableType() == ETableSource.subquery)
3073                            ||((lcTable.getTableType() == ETableSource.openquery)&&(lcTable.getSubquery() != null))){
3074
3075                    lcResult = lcTable.searchColumnInAlias(pColumn)>=0;
3076                    if (!lcResult){
3077                        lcResult = lcTable.getSubquery().searchColumnInResultSet(pColumn,(tables.size() == 1)
3078                                &&(pColumn.getCandidateTables().size() == 0));
3079                        if (! lcResult) {
3080                            candidateTableCnt++;
3081                            pColumn.getCandidateTables().addTable(lcTable);
3082                        }
3083                    }
3084
3085
3086//                    if (lcTable.isIncludeColumnAlias()){
3087//                       // System.out.println("subquery with alias:"+lcTable.getAliasClause().toString()+", skip search:"+pColumn.toString());
3088//
3089//                    }else{
3090//                        lcResult = lcTable.getSubquery().searchColumnInResultSet(pColumn,(tables.size() == 1)&&(pColumn.getCandidateTables().size() == 0));
3091//                        if (! lcResult) candidateTableCnt++;
3092//                    }
3093                }else  if (lcTable.isCTEName()){
3094                    lcResult = lcTable.getCTE().searchColumnInResultSet(this,lcTable,pColumn,tables.size() == 1);
3095                    if (! lcResult) {
3096                        candidateTableCnt++;
3097                        pColumn.getCandidateTables().addTable(lcTable);
3098                    }
3099                }else if (lcTable.getTableType() == ETableSource.function){
3100                    //  search in this table function
3101                        if(tables.size() == 1){
3102                                int lcInTableFunction = lcTable.getFuncCall().isColumnInThisTableFunction(this.getSqlEnv(),this.dbvendor,pColumn);
3103                                lcResult = ( lcInTableFunction != TBaseType.COLUMN_IN_TABEL_FUNCTION_NO);
3104                                // ClickHouse: an unverifiable table-function column
3105                                // (NOTSURE) must not outrank an expression-CTE alias
3106                                // visible in an enclosing scope — the alias is proven,
3107                                // the guessed column is not. Leaving it unmatched lets
3108                                // the parent-walk resolve the alias.
3109                                if (lcResult && (lcInTableFunction == TBaseType.COLUMN_IN_TABEL_FUNCTION_NOTSURE)
3110                                                && (dbvendor == EDbVendor.dbvclickhouse) && (!pColumn.isQualified())
3111                                                && (findExpressionCteForAliasInAnyScope(pColumn) != null)){
3112                                        lcResult = false;
3113                                }
3114                        }
3115                        else{
3116                                lcResult = ( lcTable.getFuncCall().isColumnInThisTableFunction(this.getSqlEnv(),this.dbvendor,pColumn)
3117                                                                        == TBaseType.COLUMN_IN_TABEL_FUNCTION_YES);
3118                        }
3119                }else if (lcTable.getTableType() == ETableSource.tableExpr 
3120                                && lcTable.getTableExpr().getExpressionType() == EExpressionType.function_t
3121                                && lcTable.getTableExpr().getFunctionCall() != null){
3122                    //  search in this table function
3123                    lcResult = ( lcTable.getTableExpr().getFunctionCall().isColumnInThisTableFunction(this.getSqlEnv(),this.dbvendor,pColumn)
3124                                                                        == TBaseType.COLUMN_IN_TABEL_FUNCTION_YES);
3125                }else if (lcTable.getTableType() == ETableSource.pivoted_table){
3126                    lcResult = fireOnMetaDatabaseTableColumn(
3127                            lcTable.getPrefixServer()
3128                            ,lcTable.getPrefixDatabase()
3129                            ,lcTable.getPrefixSchema()
3130                            ,lcTable.getName()
3131                            ,pColumn.getColumnNameOnly());
3132                    if (lcResult){
3133                            foundInMetaData = true;
3134                            isContinue = false;
3135                    }
3136                }else if (lcTable.getTableType() == ETableSource.unnest){
3137                    for(TObjectName objectName:lcTable.getLinkedColumns()){
3138                        if (SQLUtil.compareIdentifier(this.dbvendor, ESQLDataObjectType.dotColumn, objectName.toString(), pColumn.toString())){
3139                            lcResult = true;
3140                            break;
3141                        }
3142                    }
3143
3144                    if (!lcResult){
3145                        if (lcTable.getAliasClause() == null){
3146                            // this unnest() clause generate column with default name: "value"
3147                            if (pColumn.toString().equalsIgnoreCase("value")){
3148                                lcResult = true;
3149                            }
3150                        }else{
3151                        }
3152                    }
3153                }//unnest
3154            }
3155
3156            if (lcResult) {
3157                lcTable.getLinkedColumns().addObjectName(pColumn);
3158                pColumn.setSourceTable(lcTable);
3159                // A real table binding in this scope outranks an alias mark a
3160                // wider-scope pre-pass may have left (ClickHouse closest-scope).
3161                pColumn.setExpressionCteRef(null);
3162               // pColumn.setValidate_column_status(TBaseType.COLUMN_LINKED_TO_TABLE_IN_OLD_ALGORITHM);
3163                if (!isContinue) break;
3164            }
3165        }
3166
3167        if ((lcResult) && (foundInMetaData)) return true;
3168
3169        // check variable after metadata checking
3170        if (locateVariableOrParameter(pColumn)) return false;
3171
3172        // check if this is the column alias in current select list.
3173//        if((!lcResult)&& ((!pColumn.isPrefixed()) && (this instanceof TSelectSqlStatement)&&(getResultColumnList() !=null))){
3174//            for(int j=0;j<getResultColumnList().size();j++){
3175//                TResultColumn lcField = getResultColumnList().getResultColumn(j);
3176//                lcResult = lcField.isMatchedUsingAlias(pColumn);
3177//                if ((lcResult)&&(pColumn.getStartToken().posinlist > lcField.getAliasClause().getStartToken().posinlist)){
3178//                    pColumn.setSourceColumn(lcField);
3179//                    lcField.getTargetColumns().addObjectName(pColumn);
3180//                    break;
3181//                }else{
3182//                    lcResult = false;
3183//                }
3184//            }
3185//        }
3186
3187        if (lcResult) return true;
3188
3189        boolean isSearchUpLevel = (this.parentStmt != null);
3190
3191        if ((isSearchUpLevel) && (sqlstatementtype == ESqlStatementType.sstselect)){
3192            isSearchUpLevel = (pColumn.isQualified()
3193                                || (
3194//                                        (((TSelectSqlStatement)(this)).getLocation() != ESqlClause.elTable) &&
3195                                         (! ((TSelectSqlStatement)(this)).isQueryOfCTE())
3196                                    )
3197                               )
3198                            && (parentStmt.sqlstatementtype != ESqlStatementType.sstinsert)
3199                            && (!((pColumn.getLocation() == ESqlClause.selectList)&&(((TSelectSqlStatement)(this)).getLocation() == ESqlClause.join)))
3200                            && ((((TSelectSqlStatement)(this)).getLocation() != ESqlClause.pivot_in))
3201                            && (!((parentStmt.sqlstatementtype == ESqlStatementType.sstcreatetable)))
3202                            && (!((parentStmt.sqlstatementtype == ESqlStatementType.sstcreateview)))
3203//                            && (!((pColumn.getLocation() == ESqlClause.selectList)&&(parentStmt.sqlstatementtype == ESqlStatementType.sstcreatetable)))
3204//                            && (!((pColumn.getLocation() == ESqlClause.selectList)&&(parentStmt.sqlstatementtype == ESqlStatementType.sstcreateview)))
3205                            && (! ((pColumn.getLocation() == ESqlClause.selectList)
3206                                    &&(candidateTableCnt == 1) && (this instanceof TSelectSqlStatement)
3207                                    && (((TSelectSqlStatement)(this)).getLocation() == ESqlClause.elTable)
3208                                    ) ) // ref:mantis: #2628
3209                           // && ( ((TSelectSqlStatement)(this.parentStmt)).getSetOperatorType() == ESetOperatorType.none)
3210            ;
3211
3212            if (isSearchUpLevel){
3213                isSearchUpLevel = !((!pColumn.isQualified())&&(((TSelectSqlStatement) this).getLocation() == ESqlClause.where));
3214            }
3215        }
3216
3217        if (isSearchUpLevel&&(pColumn.isContinueToSearch())){ // only search one level up, c:\prg\gsp_sqlfiles\TestCases\java\oracle\dbobject\berger_sqltest_04.sql
3218            boolean increaseLevel = true;
3219            if (parentStmt instanceof TSelectSqlStatement){
3220                if( ((TSelectSqlStatement)parentStmt).getSetOperatorType() != ESetOperatorType.none){
3221                   increaseLevel = false;
3222                }
3223            }
3224            if (increaseLevel){
3225                pColumn.searchLevel++;
3226            }
3227
3228            lcResult = parentStmt.linkColumnToTable(pColumn,pLocation);
3229
3230            if (increaseLevel){
3231                pColumn.searchLevel--;
3232            }
3233        }
3234
3235        if ((! lcResult) && (pColumn.searchLevel == 0)) {
3236            if (this.sqlstatementtype == ESqlStatementType.sstselect){
3237                if( ((TSelectSqlStatement)this).getSetOperatorType() == ESetOperatorType.none){
3238                    //                    USING _spVV0 (INTEGER)
3239                    //                            INSERT INTO table3
3240                    //                    SELECT :_spVV0,x. *,m.col3
3241                    //                    from ((           select table1.col1, (table1.col1 + table5.col2) c from table1
3242                    //                            union all select col3,col4 from table2) x
3243                    //                    cross join (select id from table2) m )
3244
3245                    // table5 in the above sql only link to the nearest level sql, but not to up-level which is union all
3246
3247                    linkToFirstTable(pColumn,candidateTableCnt);
3248                }
3249            }else{
3250                linkToFirstTable(pColumn,candidateTableCnt);
3251            }
3252        }
3253
3254        return lcResult;
3255    }
3256
3257
3258    /**
3259     *
3260     * @deprecated As of v1.6.0.1, use  {@link #linkColumnToTable} instead
3261     */
3262    /**
3263     * True when {@code cr} is an Oracle pseudocolumn rather than a reference to
3264     * a column of a table, judged only on what the statement itself proves.
3265     *
3266     * <p>The name list lives in {@link OraclePseudoColumnUtil}; this method
3267     * supplies the statement context that decides the non-reserved names.
3268     * Nothing here consults a catalog, so names that are indistinguishable from
3269     * a real column without one - {@code ORA_ROWSCN}, {@code OBJECT_ID},
3270     * {@code OBJECT_VALUE}, {@code XMLDATA}, {@code COLUMN_VALUE} - are
3271     * deliberately not claimed. {@code OBJECT_ID} in particular is a real
3272     * column of {@code ALL_OBJECTS}, and claiming it would silently drop that
3273     * lineage.</p>
3274     *
3275     * @param cr the column reference being linked
3276     * @return true if {@code cr} is provably a pseudocolumn
3277     */
3278    protected boolean isOraclePseudoColumnReference(TObjectName cr){
3279        if (dbvendor != EDbVendor.dbvoracle) return false;
3280        if (cr == null) return false;
3281        // getColumnNameOnly() is the raw text of the LAST segment, quotes
3282        // included, so a quoted "ROWID" simply fails to match the name list -
3283        // which is the correct outcome, since "ROWID" and ROWID are different
3284        // names in Oracle. Do NOT add a getQuoteType() guard here: that method
3285        // reports the quote state of the FIRST segment, so it would reject
3286        // "seq".NEXTVAL, whose terminal segment is unquoted and really is the
3287        // sequence pseudocolumn.
3288        String nameOnly = cr.getColumnNameOnly();
3289        if (nameOnly == null || nameOnly.length() == 0) return false;
3290
3291        // Reserved words with no owning table (ROWNUM, LEVEL). Oracle rejects
3292        // an unquoted column of these names, so no qualifier or clause can make
3293        // this a real column reference. ROWID is deliberately NOT here: it is
3294        // scoped to one table, keeps its attribution, and is retyped in
3295        // TObjectName.setSourceTable() instead.
3296        if (OraclePseudoColumnUtil.isDetachedReservedPseudoColumn(nameOnly)) return true;
3297
3298        // CONNECT_BY_ISLEAF / CONNECT_BY_ISCYCLE are not reserved. They are
3299        // pseudocolumns only inside a hierarchical query, and only unqualified:
3300        // Oracle has no table to qualify them with, so t.CONNECT_BY_ISLEAF is a
3301        // reference to a column somebody created called CONNECT_BY_ISLEAF.
3302        if (OraclePseudoColumnUtil.isConnectByPseudoColumn(nameOnly)
3303                && cr.getTableToken() == null
3304                && hasHierarchicalQueryClause()) return true;
3305
3306        // VERSIONS_* are only pseudocolumns when the table they come from
3307        // carries a flashback VERSIONS clause.
3308        if (OraclePseudoColumnUtil.isVersionsPseudoColumn(nameOnly)
3309                && hasFlashbackVersionsClause(cr.getTableString())) return true;
3310
3311        return false;
3312    }
3313
3314    /**
3315     * True when {@code cr} is {@code [schema.]sequence.NEXTVAL} or
3316     * {@code [schema.]sequence.CURRVAL}.
3317     *
3318     * <p>Neither name is reserved, so this needs the qualifier to be something
3319     * other than a table or alias in scope: {@code x.CURRVAL} where {@code x}
3320     * is a table alias is an ordinary reference to a column named
3321     * {@code CURRVAL}, and an unqualified {@code CURRVAL} likewise.</p>
3322     *
3323     * <p>Kept separate from {@link #isOraclePseudoColumnReference} because the
3324     * two get different treatment - see the call site.</p>
3325     *
3326     * @param cr the column reference being linked
3327     * @return true if {@code cr} is a sequence pseudocolumn
3328     */
3329    protected boolean isOracleSequencePseudoColumnReference(TObjectName cr){
3330        if (dbvendor != EDbVendor.dbvoracle) return false;
3331        if (cr == null) return false;
3332        // See isOraclePseudoColumnReference(): quote state is segment-specific
3333        // and already carried by getColumnNameOnly(); getQuoteType() reads the
3334        // wrong segment for a qualified name.
3335        String nameOnly = cr.getColumnNameOnly();
3336        if (nameOnly == null || nameOnly.length() == 0) return false;
3337        return OraclePseudoColumnUtil.isSequencePseudoColumn(nameOnly)
3338                && cr.getTableToken() != null
3339                && !isTableOrAliasInScope(cr.getTableString());
3340    }
3341
3342    /**
3343     * True when this statement (or the statement it belongs to) carries an
3344     * Oracle {@code CONNECT BY}.
3345     */
3346    private boolean hasHierarchicalQueryClause(){
3347        // Deliberately NOT walking up to the parent statement. CONNECT BY
3348        // pseudocolumns belong to the query that carries the clause; they are
3349        // not visible to a subquery nested inside it. Walking up would claim
3350        // CONNECT_BY_ISLEAF in
3351        //   SELECT (SELECT CONNECT_BY_ISLEAF FROM other) FROM t CONNECT BY ...
3352        // where it is an ordinary column of "other", and drop that edge.
3353        if (this instanceof TSelectSqlStatement){
3354            return ((TSelectSqlStatement)this).getHierarchicalClause() != null;
3355        }
3356        return false;
3357    }
3358
3359    /**
3360     * True when any table in scope carries a flashback {@code VERSIONS BETWEEN}
3361     * clause, which is what makes the {@code VERSIONS_*} pseudocolumns
3362     * available.
3363     */
3364    private boolean hasFlashbackVersionsClause(String qualifier){
3365        // Same scoping rule as hasHierarchicalQueryClause(): the VERSIONS_*
3366        // pseudocolumns come from a table in THIS statement's FROM clause, so
3367        // no parent-chain walk. An inner subquery of a version query does not
3368        // inherit them.
3369        if (tables == null) return false;
3370        // When the reference names a table, only THAT table's clause counts.
3371        // In "FROM x VERSIONS BETWEEN ... , y", y.VERSIONS_XID is an ordinary
3372        // column of y - y is not being version-queried.
3373        if (qualifier != null && qualifier.length() > 0){
3374            int idx = tables.searchTableByNameOrAlias(qualifier);
3375            if (idx < 0) return false;
3376            TFlashback flashback = tables.getTable(idx).getFlashback();
3377            return flashback != null && flashback.isVersionsQuery();
3378        }
3379        for (int i = 0; i < tables.size(); i++){
3380            TFlashback flashback = tables.getTable(i).getFlashback();
3381            if (flashback != null && flashback.isVersionsQuery()) return true;
3382        }
3383        return false;
3384    }
3385
3386    /**
3387     * True when {@code name} matches a table name or alias visible to this
3388     * statement. Used to tell {@code sequence.CURRVAL} from
3389     * {@code tablealias.CURRVAL}.
3390     */
3391    private boolean isTableOrAliasInScope(String name){
3392        if (name == null || name.length() == 0) return false;
3393        TCustomSqlStatement stmt = this;
3394        while (stmt != null){
3395            if (stmt.tables != null && stmt.tables.searchTableByNameOrAlias(name) >= 0) return true;
3396            stmt = stmt.getParentStmt();
3397        }
3398        return false;
3399    }
3400
3401    public void linkColumnReferenceToTable(TObjectName cr, ESqlClause plocation){
3402        // this is the column name, link it to table
3403        if (cr == null) return;
3404        cr.setLocation(plocation);
3405        if (cr.getObjectType() == TObjectName.ttobjVariable) return;
3406        if (cr.getObjectType() == TObjectName.ttobjColumnAlias) return;
3407        if ((this.dbvendor == EDbVendor.dbvsybase)||(this.dbvendor == EDbVendor.dbvsybasease || this.dbvendor == EDbVendor.dbvsqlanywhere || this.dbvendor == EDbVendor.dbvsybaseiq)){
3408            TSourceToken pt = cr.getPartToken();
3409            if ( pt != null){
3410                if (pt.tokentype == ETokenType.ttdqstring){
3411                //"0123", quoted string start with a number can't a column
3412                    if ((pt.toString().charAt(1) >= '0')
3413                        &&(pt.toString().charAt(1) <= '9')){
3414                        return;
3415                    }else if (pt.toString().length() == 2){
3416                        //"", empty
3417                        return;
3418                    }else if (pt.toString().substring(1,pt.toString().length()-1).trim().length() == 0){
3419                        //"  "
3420                        return;
3421                    }
3422                }
3423            }
3424        }
3425
3426
3427        if (cr.getPartToken() != null){
3428            if (cr.getPartToken().tokentype == ETokenType.ttkeyword){
3429                boolean reservedKeyword = false;
3430                switch (dbvendor){
3431                    case dbvmssql:
3432                        //reservedKeyword = ! this.getGsqlparser().getFlexer().canBeColumnName(cr.getPartToken().tokencode);
3433                        reservedKeyword = ! TLexerMssql.canBeColumnName(cr.getPartToken().tokencode);
3434                        break;
3435                    case dbvsybase:
3436                        reservedKeyword = keywordChecker.isKeyword(cr.getPartToken().toString(), EDbVendor.dbvsybase, "15.7", true);
3437                        break;
3438                    case dbvsybasease:
3439                        // Shares dbvsybase's keyword DATA file (it is the ASE
3440                        // reserved-word list); independent case block per HC-5.
3441                        reservedKeyword = keywordChecker.isKeyword(cr.getPartToken().toString(), EDbVendor.dbvsybase, "15.7", true);
3442                        break;
3443                    case dbvsqlanywhere:
3444                        // Same shared keyword DATA file; independent case per HC-5.
3445                        reservedKeyword = keywordChecker.isKeyword(cr.getPartToken().toString(), EDbVendor.dbvsybase, "15.7", true);
3446                        break;
3447                    case dbvsybaseiq:
3448                        // Same again; independent case per HC-5.
3449                        reservedKeyword = keywordChecker.isKeyword(cr.getPartToken().toString(), EDbVendor.dbvsybase, "15.7", true);
3450                        break;
3451                    default:
3452                        break;
3453                }
3454                if (reservedKeyword) return;
3455            }
3456        }
3457
3458        // let's check is this columnreference is variable or parameter of plsql function/procedure
3459      //  if (locateVariableOrParameter(cr)) return;
3460
3461//        if ((cr.getPartToken() != null)&&((dbvendor == EDbVendor.dbvmssql)||(dbvendor == EDbVendor.dbvsybase))){
3462//            if ((cr.getPartToken().tokentype == ETokenType.ttkeyword)&&(!(this.getGsqlparser().getFlexer().canBeColumnName(cr.getPartToken().tokencode)))){
3463//                // keyword can't be column name:
3464//                //select * From dbo.table Where DATEDIFF(day, create_date, expiry_date) < 14
3465//               return;
3466//            }
3467//        }
3468
3469        if ((cr.toString().startsWith("@")))
3470//            if ((cr.toString().endsWith("*"))||(cr.toString().startsWith("@")))
3471        {
3472            cr.setObjectType(TObjectName.ttobjNotAObject);
3473            return;
3474        }
3475
3476        if (dbvendor == EDbVendor.dbvoracle){
3477            if ( //(cr.toString().compareToIgnoreCase ("rowid") == 0)||
3478                    (cr.toString().compareToIgnoreCase ("sysdate") == 0)
3479            || (cr.toString().compareToIgnoreCase ("nextval") == 0)
3480            || (cr.toString().compareToIgnoreCase ("rownum") == 0)
3481            || (cr.toString().compareToIgnoreCase ("level") == 0)
3482                    ){
3483                cr.setObjectType(TObjectName.ttobjNotAObject);
3484                if (cr.getDbObjectType() == EDbObjectType.unknown){
3485                    cr.setDbObjectType(EDbObjectType.notAColumn);
3486                }
3487                return;
3488            }
3489            // The comparisons above are against the whole rendered text, so they
3490            // only ever fire for a bare, unqualified name. Oracle documents 18
3491            // pseudocolumns and most of them reach here qualified (X.ROWID,
3492            // SEQ.CURRVAL) or under a spelling the list never had. Left alone
3493            // they fall through to checkColumnReferenceInTables() below and get
3494            // bound to a table in the FROM clause, which asserts that e.g.
3495            // ORA_ROWSCN is a column of that table (Mantis #4675).
3496            if (isOracleSequencePseudoColumnReference(cr)){
3497                // [schema.]sequence.NEXTVAL|CURRVAL. Detach it from the FROM
3498                // clause but leave dbObjectType alone, which keeps CURRVAL on
3499                // the exact footing NEXTVAL has always had (unknown, no source
3500                // table). Promoting these to notAColumn would reclassify them
3501                // from column references to constants in DataFlowAnalyzer and
3502                // move existing lineage output; the defect reported was CURRVAL
3503                // being bound to an unrelated table, not its type.
3504                // Deliberately no setSourceTable(null) here: nothing has linked
3505                // this reference yet, and that setter has a side effect of
3506                // forcing dbObjectType to column, which is exactly the type we
3507                // are trying to leave alone.
3508                cr.setObjectType(TObjectName.ttobjNotAObject);
3509                cr.setValidate_column_status(TBaseType.VALIDATED_CAN_NOT_BE_A_COLUMN_NAME);
3510                return;
3511            }
3512            if (isOraclePseudoColumnReference(cr)){
3513                cr.setObjectType(TObjectName.ttobjNotAObject);
3514                cr.setSourceTable(null);
3515                cr.setValidate_column_status(TBaseType.MARKED_NOT_A_COLUMN_IN_COLUMN_RESOLVER);
3516                cr.setDbObjectTypeDirectly(EDbObjectType.notAColumn);
3517                return;
3518            }
3519        }
3520
3521        if (((cr.toString().toUpperCase().startsWith("INSERTED"))||(cr.toString().toUpperCase().startsWith("DELETED")))&&(plocation == ESqlClause.output)&&(targetTable != null)){
3522            targetTable.getObjectNameReferences().addObjectName(cr);
3523            return;
3524        }
3525
3526        if ( ((cr.toString().toUpperCase().startsWith(":NEW"))
3527              ||(cr.toString().toUpperCase().startsWith(":OLD")))
3528             &&(this.getTopStatement() instanceof TPlsqlCreateTrigger)
3529             &&(dbvendor == EDbVendor.dbvoracle)){
3530             this.getTopStatement().tables.getTable(0).getObjectNameReferences().addObjectName(cr);
3531            return;
3532        }
3533
3534
3535
3536        int ret = this.tables.checkColumnReferenceInTables(cr);
3537         if (ret >= 0) {
3538             TTable lcTable = this.tables.getTable(ret);
3539             if (lcTable.isBaseTable()){
3540                lcTable.getObjectNameReferences().addObjectName(cr);
3541             }else if (lcTable.isCTEName()){
3542                //WITH temp
3543                //     AS (SELECT *
3544                //         FROM   sysibm.systables),
3545                //     temp1
3546                //     AS (SELECT *
3547                //         FROM   sysibm.syscolumns)
3548                //SELECT *
3549                //FROM   temp A
3550                //       INNER JOIN temp1 B
3551                //               ON A.creator = B.tbcreator
3552                //                  AND A.name = B.tbname
3553                 TCTE lccte = findCTEByName(lcTable.toString());
3554                 if (lccte != null){
3555                     TObjectName objectName = new TObjectName();
3556                     objectName.init(cr.getPartToken());
3557                     if (lccte.getSubquery() != null){
3558                         lccte.getSubquery().linkColumnReferenceToTable(objectName,plocation);
3559                     }
3560                 }
3561             }else if (lcTable.getTableType() == ETableSource.subquery){
3562                // link s2t1a1 to  subselect2table1 via s2
3563                //select
3564                //       s2.s2t1a1
3565                //from
3566                //    (
3567                //       select s2t1.*
3568                //          from subselect2table1 s2t1
3569                //    ) s2
3570               TSelectSqlStatement subquery = lcTable.getSubquery();
3571
3572                 if(((subquery.getValueClause() == null))&&(!subquery.isCombinedQuery())&&(subquery.getResultColumnList() != null)&&(subquery.getResultColumnList().size() == 1)){
3573                     TResultColumn lcColumn = subquery.getResultColumnList().getResultColumn(0);
3574                     if (lcColumn.toString().endsWith("*")){
3575                        boolean isfound = false;
3576
3577                        for(int i=0;i<subquery.tables.size();i++){
3578                            if (subquery.tables.getTable(i).getTableType() == ETableSource.subquery) continue;
3579                            String columnStr = null;
3580                            if (cr.getPartToken() != null){
3581                                //cr.getObjectType() is not ttObjColumn, so we can't use
3582                                // getColumnToken, this is a bug, need to check it later.
3583                                columnStr = cr.getPartToken().toString();
3584                            }
3585                            if (this.fireOnMetaDatabaseTableColumn(
3586                                    subquery.tables.getTable(i).getTableName().getServerString(),
3587                                    subquery.tables.getTable(i).getTableName().getDatabaseString(),
3588                                    subquery.tables.getTable(i).getTableName().getSchemaString(),
3589                                    subquery.tables.getTable(i).getName(),columnStr)){
3590                                subquery.tables.getTable(i).getObjectNameReferences().addObjectName(cr);
3591                                isfound = true;
3592                                break;
3593                            }
3594                        }
3595
3596
3597
3598                         if (!isfound)
3599                         {
3600                             if(subquery.tables.size() > 1){
3601                                 cr.setTableDetermined(false);
3602                             }
3603                           for(int i=0;i<subquery.tables.size();i++){
3604                             subquery.tables.getTable(i).getObjectNameReferences().addObjectName(cr);
3605                           }
3606                         }
3607
3608                     } // "*"
3609                 }
3610             }
3611         }else if (ret == -2){
3612           // no qualifier before column, check is this column of a cte, if not,set it to non-cte table
3613           boolean isfound = false;
3614             for (int i=0;i<this.tables.size();i++){
3615                 if ((this.tables.getTable(i).isCTEName()) &&(this.tables.getTable(i).getCteColomnReferences() != null)){
3616                     if (this.tables.getTable(i).getCteColomnReferences().searchColumnReference(cr) >= 0){
3617                        this.tables.getTable(i).getObjectNameReferences().addObjectName(cr);
3618                         isfound = true;
3619                         break;
3620                     }
3621                 }
3622             }
3623
3624           // no qualifier before column, but we still need to check uplevel table like this:
3625            //SELECT
3626            //       col1 ,
3627            //
3628            //           (    SELECT col2
3629            //                FROM tab1
3630            //                WHERE col2 = col1      )
3631            //   FROM tab2
3632           // we need to link col1 to tab2 in up level, but not to tab1
3633            if ((!isfound) &&(
3634                            (cr.getLocation() != ESqlClause.resultColumn)
3635                          &&(cr.getLocation() != ESqlClause.insertColumn)
3636                                    &&(cr.getLocation() != ESqlClause.mergeInsert)
3637                                    &&(cr.getLocation() != ESqlClause.selectList)
3638            ) ){  // code #111
3639                TCustomSqlStatement lcParent = null;
3640                lcParent = this.getParentStmt();
3641               while ( lcParent != null) {
3642                   TTable lcTable;
3643                 //ret = lcParent.tables.checkColumnReferenceInTables(cr);
3644                   if (lcParent.sqlstatementtype != ESqlStatementType.sstselect) {
3645                       break;
3646                   }
3647                   for (int i=0;i<lcParent.tables.size();i++){
3648                       lcTable = lcParent.tables.getTable(i);
3649                       if (lcTable.getTableType() == ETableSource.objectname) {
3650                           for(int k = 0; k< lcTable.getObjectNameReferences().size();k++){
3651                               if (lcTable.getObjectNameReferences().getObjectName(k).isTableDetermined()){
3652                                   if (SQLUtil.compareIdentifier(this.dbvendor, ESQLDataObjectType.dotColumn, cr.toString(), lcTable.getObjectNameReferences().getObjectName(k).toString())){
3653                                      isfound = true;
3654                                       break;
3655                                   }
3656                               }
3657                           }
3658                       if (isfound) break;
3659                       }
3660                   }
3661
3662                 if (isfound){
3663                     break;
3664                 }else{
3665                     lcParent = lcParent.getParentStmt();
3666                 }
3667               } // while
3668
3669             } // end of code #111
3670
3671            if (!isfound){
3672                isfound = checkNonQualifiedColumnReferenceInSubQueryOfUplevelStmt(cr
3673                        , ((plocation == ESqlClause.resultColumn)
3674                            ||(plocation == ESqlClause.insertColumn)
3675                                ||(plocation == ESqlClause.mergeInsert)
3676                                ||(plocation == ESqlClause.selectList)
3677                        )
3678                );
3679            }
3680
3681             if ((!isfound)&&(this.tables.size() > 0)){
3682                 int candidate = 0, firstCandidate = -1;
3683                 // add this column reference to first non-cte( or cte with column list is null) and non-subquery table
3684                 for (int i=0;i<this.tables.size();i++){
3685                     // no qualified column can't belong to a table with alias, that column must be qualified if it's belong to a table with alias
3686                     //if (this.tables.getTable(i).aliasClause != null) continue;
3687                     if (
3688                             (
3689                                     (!this.tables.getTable(i).isCTEName())
3690                                ||((this.tables.getTable(i).isCTEName())&&(this.tables.getTable(i).getCteColomnReferences() == null))
3691                             )&&((this.tables.getTable(i).getTableType() != ETableSource.subquery))
3692                     )
3693                     {
3694                         candidate++;
3695                         if (firstCandidate == -1) firstCandidate = i;
3696                         if (this.fireOnMetaDatabaseTableColumn(
3697                                    this.tables.getTable(i).getTableName().getServerString(),
3698                                    this.tables.getTable(i).getTableName().getDatabaseString(),
3699                                    this.tables.getTable(i).getTableName().getSchemaString(),
3700                                    this.tables.getTable(i).getName(),cr.toString())){
3701                                this.tables.getTable(i).getObjectNameReferences().addObjectName(cr);
3702                                isfound = true;
3703                                break;
3704                            }
3705                         else{
3706                             this.tables.getTable(i).getObjectNameReferences().addObjectName(cr);
3707                             if (this.tables.size() > 1){
3708                                 cr.setTableDetermined(false);
3709                             }
3710                             isfound = true;
3711                             break;
3712                         }
3713                     }
3714                  }
3715                 if ((!isfound) && (candidate == 1)){
3716                     this.tables.getTable(firstCandidate).getObjectNameReferences().addObjectName(cr);
3717                 }
3718             }
3719         }else if (ret == -1){
3720             TCustomSqlStatement lcParent = null;
3721             lcParent = this.getParentStmt();
3722            while ( lcParent != null) {
3723              ret = lcParent.tables.checkColumnReferenceInTables(cr);
3724              if (ret >= 0){
3725               lcParent.tables.getTable(ret).getObjectNameReferences().addObjectName(cr);
3726               break;
3727              }else{
3728                  lcParent = lcParent.getParentStmt();
3729              }
3730            } // while
3731         } //-1
3732
3733    }
3734
3735    /**
3736     * Found out is a non qualified column is a column in uplevel subquery table like this:
3737     * take ma_parkey for example: ma_parkey is not a physical column
3738     * 
3739        SELECT c_mandant
3740             , CASE WHEN EXISTS (SELECT 1
3741                                   FROM CDS_H_GRUPPE  GRP1
3742                                  WHERE GRP1.c_mandant = c_mandant
3743                                    AND GRP1.parkey1       = ma_parkey)
3744                      THEN 1
3745                  ELSE NULL
3746               END MA_ME
3747          FROM (SELECT c_mandant
3748                     , CASE WHEN funktionscode = 'U'
3749                              THEN parkey1
3750                          ELSE parkey2
3751                       END MA_PARKEY
3752                  FROM
3753                       CDS_H_GRUPPE
3754               )
3755     */
3756    public boolean checkNonQualifiedColumnReferenceInSubQueryOfUplevelStmt(TObjectName crf,boolean sameLevelOnly){
3757        boolean ret = false;
3758
3759        TCustomSqlStatement lcParent = null;
3760        lcParent = this;//getParentStmt();
3761       while ( lcParent != null) {
3762           TTable lcTable;
3763             for (int i=0;i<lcParent.tables.size();i++){
3764                 lcTable = lcParent.tables.getTable(i);
3765
3766                 if ((lcTable.getTableType() != ETableSource.subquery)) {continue;}
3767
3768                 ret = isColumnNameInSelectList(crf.toString(),lcTable.subquery);
3769                if (ret) {break;}
3770
3771             }
3772          if (ret) {break;}
3773           else{
3774              if (sameLevelOnly){
3775                  lcParent = null;
3776              }else{
3777                lcParent = lcParent.getParentStmt();
3778              }
3779          }
3780       } // while
3781
3782        return ret;
3783    }
3784
3785    private boolean isColumnNameInSelectList(String pColumn, TSelectSqlStatement pSelect){
3786        // Iterative rewrite of the original recursive descent. Provably identical
3787        // output: it visits exactly the same set of leaf (non-combined) SELECTs as
3788        // the recursion, in the same left-before-right order, and applies the
3789        // identical per-column test (alias match, else expr match, case-insensitive).
3790        // The only reason for the change is that UNION trees are left-leaning and
3791        // can be thousands of branches deep, so recursive descent risks
3792        // StackOverflowError (see repo CLAUDE.md: iterative UNION traversal).
3793        Deque<TSelectSqlStatement> pending = new ArrayDeque<>();
3794        pending.push(pSelect);
3795        while (!pending.isEmpty()) {
3796            TSelectSqlStatement select = pending.pop();
3797            if (select == null) {
3798                continue;
3799            }
3800            if (select.isCombinedQuery()) {
3801                // Push right then left so left is popped/processed first, matching
3802                // the recursion's "check left before right" order.
3803                pending.push(select.getRightStmt());
3804                pending.push(select.getLeftStmt());
3805                continue;
3806            }
3807            if (select.getResultColumnList() != null){ //if it's a db2 value row, then getResultColumnList() will be null
3808                TResultColumnList resultColumns = select.getResultColumnList();
3809                for(int j=0;j<resultColumns.size();j++){
3810                   TResultColumn lcColumn = resultColumns.getResultColumn(j);
3811                   boolean ret = false;
3812                   if (lcColumn.getAliasClause() != null){
3813                       ret = SQLUtil.compareIdentifier(this.dbvendor, ESQLDataObjectType.dotColumn, pColumn, lcColumn.getAliasClause().toString());
3814                   }
3815                   if (ret) return true;
3816                   ret = pColumn.equalsIgnoreCase(lcColumn.getExpr().toString());
3817                   if (ret) return true;
3818                }
3819            }
3820        }
3821        return false;
3822    }
3823
3824    public TCustomSqlStatement getTopStatement(){
3825        TCustomSqlStatement ret = this;
3826        while (ret.getParentStmt() != null){
3827            ret = ret.getParentStmt();
3828        }
3829        return ret;
3830    }
3831
3832
3833//    public String toScript(){
3834//        if (!isChanged()){
3835//            return this.toString();
3836//        }
3837//        return super.toScript();
3838//    }
3839
3840}
3841
3842class constantVisitor extends TParseTreeVisitor {
3843    private boolean inWhere = false,inExprList = false;
3844    public void preVisit(TWhereClause node){
3845        inWhere = true;
3846    }
3847
3848    public void postVisit(TWhereClause node){
3849        inWhere = false;
3850    }
3851
3852    public void preVisit(TExpression node){
3853        if (inWhere){
3854            switch (node.getExpressionType()){
3855                case list_t:
3856                    inExprList = true;
3857                    boolean isNumber = true;
3858                    if (node.getExprList().size() > 0){
3859                        // check the type of the constant in the expr list
3860                        TExpression expr = node.getExprList().getExpression(0);
3861                        if (expr.getExpressionType() == EExpressionType.simple_constant_t){
3862                            if (expr.getConstantOperand().getLiteralType() == ELiteralType.etString){
3863                                isNumber = false;
3864                            }
3865                        }
3866                    }
3867
3868                    TSourceToken lcStartToken = node.getStartToken();
3869                    TSourceToken lcEndToken = node.getEndToken();
3870                    int tokenPos = 0;
3871                    if ((lcEndToken != null) && (lcStartToken != null)){
3872                        TSourceToken lcCurrentToken = lcStartToken;
3873                        while (lcCurrentToken != null){
3874
3875                            if (lcCurrentToken.equals(lcEndToken)){
3876                                break;
3877                            }else{
3878
3879                                if (tokenPos == 1){
3880                                    if (isNumber){
3881                                        lcCurrentToken.setTextWithBackup("999");
3882                                    }else{
3883                                        lcCurrentToken.setTextWithBackup("'placeholder_str'");
3884                                    }
3885                                }else if (tokenPos > 1){
3886                                    lcCurrentToken.tokenstatus = ETokenStatus.tsdeleted;
3887                                }
3888
3889                                lcCurrentToken = lcCurrentToken.getNextTokenInChain();
3890                                tokenPos++;
3891                            }
3892                        }
3893                    }
3894
3895                    break;
3896            }
3897        } // where
3898    }
3899
3900    public void postVisit(TExpression node){
3901        if (inWhere){
3902            switch (node.getExpressionType()){
3903                case list_t:
3904                    inExprList = false;
3905                    break;
3906            }
3907        }
3908    }
3909
3910    public void preVisit(TConstant node){
3911        if (inWhere&&(!inExprList)){
3912            switch (node.getLiteralType()){
3913                case etNumber:
3914                case etFloat:
3915                    node.getStartToken().setTextWithBackup("999");
3916                    break;
3917                case etString:
3918                    node.getStartToken().setTextWithBackup("'placeholder_str'");
3919                    break;
3920            }
3921        }
3922    }
3923
3924    public void preVisit(TFunctionCall node){
3925        if (TBaseType.as_canonical_f_decrypt_replace_password){
3926            int i = TBaseType.searchCryptFunction(node.getFunctionName().toString());
3927
3928            if (i>0){ // find this function
3929                if (node.getArgs().size() >= i){
3930                    TExpression secondArg = node.getArgs().getExpression(i-1);
3931                    if (secondArg.getExpressionType() == EExpressionType.simple_constant_t){
3932                        TConstant constant = secondArg.getConstantOperand();
3933                        constant.getValueToken().setTextWithBackup("'***'");
3934                        //System.out.println(node.toString()+":"+constant.toString());
3935                    }else if (secondArg.getExpressionType() == EExpressionType.simple_object_name_t){
3936                        TObjectName objectName = secondArg.getObjectOperand();
3937                        objectName.getStartToken().setTextWithBackup("'***'");
3938                        //System.out.println(node.toString()+":"+constant.toString());
3939                    }
3940                }
3941            }
3942
3943        }
3944    }
3945
3946    void processConstant(TConstant node){
3947        switch (node.getLiteralType()){
3948            case etNumber:
3949            case etFloat:
3950                node.getStartToken().setTextWithBackup("999");
3951                break;
3952            case etString:
3953                node.getStartToken().setTextWithBackup("'placeholder_str'");
3954                break;
3955        }
3956    }
3957
3958}