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