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.setArrayIndexAlias(pfromTable.getArrayIndexAlias());
1748        lcTable.setSuperUnpivot(pfromTable.isSuperUnpivot());
1749        lcTable.setFlashback(pfromTable.getFlashback());
1750        lcTable.setPxGranule(pfromTable.getPxGranule());
1751        lcTable.setTimeTravelClause(pfromTable.getTimeTravelClause());
1752        //lcTable.setPartitionClause(pfromTable.getPartitionClause());
1753
1754        if(getFrameStack().firstElement() != null){
1755            TFrame stackFrame = getFrameStack().firstElement();
1756            TGlobalScope globalScope = (TGlobalScope)stackFrame.getScope();
1757            lcTable.setSqlEnv(globalScope.getSqlEnv());
1758        }
1759
1760       switch(lcTable.getTableType()){
1761           case objectname:{
1762              // tables.addTableByTableRefernce(pfromTable.getTableObjectName());
1763               boolean insertedInTrigger = false;
1764               if (getTopStatement().sqlstatementtype == ESqlStatementType.sstcreatetrigger){
1765                  insertedInTrigger = (pfromTable.getTableObjectName().toString().compareToIgnoreCase("inserted")==0);
1766               }
1767
1768               if (insertedInTrigger){
1769                   // change table name from inserted to onTable name in create trigger 
1770                 lcTable.setTableName(((TCreateTriggerStmt)getTopStatement()).getOnTable().getTableName());
1771                 //lcTable.setLinkTable(true);
1772                 lcTable.setLinkTable(((TCreateTriggerStmt)getTopStatement()).getOnTable());
1773
1774               }else{
1775                    lcTable.setTableName(pfromTable.getTableObjectName());
1776                   lcTable.getTableName().setSqlEnv(getSqlEnv());
1777
1778//                   if (getSqlEnv().getDefaultCatalogName() != null){
1779//                       if (lcTable.getTableName().getDatabaseToken() == null){
1780//                           lcTable.getTableName().setDatabaseToken(new TSourceToken(getSqlEnv().getDefaultCatalogName()),true);
1781//                       }
1782//                   }
1783
1784//                   if ((lcTable.getTableName().getSchemaToken() == null)&&(TSQLEnv.supportSchema(this.dbvendor))){
1785//                       // let find schema name for this table in env
1786//                       TSQLTable t = getSqlEnv().searchTable(".."+lcTable.getFullName());
1787//                       if (t != null){
1788//                           TSQLSchema s = t.getSchema();
1789//                           if (s != null){
1790//                               lcTable.getTableName().setSchemaToken(new TSourceToken(s.getName()),true);
1791//                           }
1792//                       }
1793//                   }
1794
1795               }
1796               // let's check is it cte name or ordinary table name
1797               TCTEList lcCteList = searchCTEList(false);
1798               TCTE lcCte = null;
1799               if (lcCteList != null){
1800                 for(int i=0;i<lcCteList.size();i++){
1801                    lcCte = lcCteList.getCTE(i);
1802                    if (lcCte.getTableName().toString().compareToIgnoreCase(TBaseType.getTextWithoutQuoted(lcTable.getTableName().toString()))==0){
1803                        // this is cte name
1804                        if (lcTable.setCTE(lcCte)){
1805                            lcTable.setCTEName(true);
1806                            lcTable.setCteColomnReferences(lcCte.getColumnList());
1807                            break;
1808                        }
1809                    }
1810                 }
1811               }
1812
1813               break;
1814           }
1815           case tableExpr:{
1816               ESqlClause location =  ESqlClause.tableExpr; //ESqlClause.resultColumn;
1817               if (sqlstatementtype == ESqlStatementType.sstinsert ){
1818                   // change location here
1819               }
1820               lcTable.setTableExpr(pfromTable.getTableExpr());
1821               lcTable.getTableExpr().doParse(this,location);
1822               // 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;
1823               // RETURNS (outkey integer, tokennum integer, token varchar(20))
1824               lcTable.setColumnDefinitions(pfromTable.getColumnDefinitions());
1825               // Teradata table function HASH BY and LOCAL ORDER BY clauses
1826               lcTable.setHashByClause(pfromTable.getHashByClause());
1827               lcTable.setLocalOrderBy(pfromTable.getLocalOrderBy());
1828               break;
1829           }
1830           case subquery:{
1831//               if (pfromTable.getSubquerynode().isHiveFromQuery()){
1832//                   THiveFromQuery fromQuery  = new THiveFromQuery(dbvendor);
1833//                   lcTable.setHiveFromQuery(fromQuery);
1834//                   fromQuery.rootNode = pfromTable.getSubquerynode();
1835//                   fromQuery.setStartToken(pfromTable.getSubquerynode());
1836//                   fromQuery.setEndToken(pfromTable.getSubquerynode());
1837//                   fromQuery.setLabelName(this.labelName);
1838//                   fromQuery.doParseStatement(this);
1839//               }else{
1840//                   lcTable.subquery = new TSelectSqlStatement(dbvendor);
1841//                   lcTable.subquery.rootNode = pfromTable.getSubquerynode();
1842//                   lcTable.subquery.setLocation(ESqlClause.elTable);
1843//                   //lcTable.subquery.resultColumnList = ((TSelectSqlNode)lcTable.subquery.rootNode).getResultColumnList();
1844//                   lcTable.subquery.doParseStatement(this);
1845//               }
1846
1847               lcTable.subquery = new TSelectSqlStatement(dbvendor);
1848               lcTable.subquery.rootNode = pfromTable.getSubquerynode();
1849               if (pLocation == ESqlClause.unknown){
1850                   lcTable.subquery.setLocation(ESqlClause.elTable);
1851               }else{
1852                   lcTable.subquery.setLocation(pLocation);
1853               }
1854               //lcTable.subquery.resultColumnList = ((TSelectSqlNode)lcTable.subquery.rootNode).getResultColumnList();
1855               lcTable.subquery.doParseStatement(this);
1856
1857               break;
1858           }
1859           case function:{
1860               ESqlClause location = ESqlClause.tableFunction;// resultColumn;
1861               if (sqlstatementtype == ESqlStatementType.sstinsert ){
1862                   // change location here
1863               }
1864               lcTable.setFuncCall(pfromTable.getFuncCall());
1865               lcTable.getFuncCall().doParse(this,location);
1866               break;
1867           }
1868           case containsTable:{
1869               ESqlClause location = ESqlClause.containsTable;//resultColumn;
1870               if (sqlstatementtype == ESqlStatementType.sstinsert ){
1871                   // change location here
1872               }
1873               lcTable.setContainsTable(pfromTable.getContainsTable());
1874               lcTable.getContainsTable().doParse(this,location);
1875               break;
1876           }
1877
1878           case openrowset:{
1879               ESqlClause location = ESqlClause.openrowset;//resultColumn;
1880               if (sqlstatementtype == ESqlStatementType.sstinsert ){
1881                   // change location here
1882               }
1883               lcTable.setOpenRowSet(pfromTable.getOpenRowSet());
1884               lcTable.getOpenRowSet().doParse(this,location);
1885               break;
1886           }
1887
1888           case openxml:{
1889               ESqlClause location = ESqlClause.openxml;//resultColumn;
1890               if (sqlstatementtype == ESqlStatementType.sstinsert ){
1891                   // change location here
1892               }
1893               lcTable.setOpenXML(pfromTable.getOpenXML());
1894               lcTable.getOpenXML().doParse(this,location);
1895               break;
1896           }
1897
1898           case opendatasource:{
1899               ESqlClause location = ESqlClause.opendatasource;//resultColumn;
1900               if (sqlstatementtype == ESqlStatementType.sstinsert ){
1901                   // change location here
1902               }
1903               lcTable.setOpenDatasource(pfromTable.getOpenDatasource());
1904               lcTable.getOpenDatasource().doParse(this,location);
1905               break;
1906           }
1907
1908           case openquery:{
1909               ESqlClause location = ESqlClause.openquery;//resultColumn;
1910               if (sqlstatementtype == ESqlStatementType.sstinsert ){
1911                   // change location here
1912               }
1913               lcTable.setOpenquery(pfromTable.getOpenQuery());
1914               lcTable.getOpenquery().doParse(this,location);
1915               lcTable.setSubquery(lcTable.getOpenquery().getSubquery());
1916               break;
1917           }
1918
1919           case datachangeTable:{
1920               ESqlClause location = ESqlClause.datachangeTable;//resultColumn;
1921               if (sqlstatementtype == ESqlStatementType.sstinsert ){
1922                   // change location here
1923               }
1924               lcTable.setDatachangeTable(pfromTable.getDatachangeTable());
1925               lcTable.getDatachangeTable().doParse(this,location);
1926               break;
1927           }
1928           case rowList:{
1929               ESqlClause location = ESqlClause.rowList;//resultColumn;
1930               lcTable.setValueClause(pfromTable.getValueClause());
1931               lcTable.getValueClause().doParse(this,location);
1932               break;
1933           }
1934           case pivoted_table:{
1935               ESqlClause location = ESqlClause.pivoted_table;//resultColumn;
1936               lcTable.getPivotedTable().doParse(this,location);
1937               addToTableList = false;
1938               targetTable = lcTable;
1939               break;
1940           }
1941           case xmltable:{
1942               ESqlClause location = ESqlClause.xmltable;//resultColumn;
1943               lcTable.setXmlTable(pfromTable.getXmlTable());
1944               lcTable.getXmlTable().doParse(this,location);
1945               break;
1946           }
1947
1948           case informixOuter:{
1949               ESqlClause location = ESqlClause.outerTable;//resultColumn;
1950               lcTable.setOuterClause(pfromTable.getOuterClause());
1951               lcTable.getOuterClause().doParse(this,location);
1952               break;
1953           }
1954
1955           case table_ref_list:{
1956               lcTable.setFromTableList(pfromTable.getFromTableList());
1957               break;
1958           }
1959//           case hiveFromQuery:{
1960//               THiveFromQuery fromQuery = new THiveFromQuery(EDbVendor.dbvhive);
1961//               fromQuery.rootNode = pfromTable.getFromQuerySqlNode();
1962//               fromQuery.doParseStatement(this);
1963//               lcTable.setHiveFromQuery(fromQuery);
1964//               break;
1965//           }
1966           case output_merge:{
1967               TMergeSqlStatement outputMerge = new TMergeSqlStatement(EDbVendor.dbvmssql);
1968               outputMerge.rootNode = pfromTable.getMergeSqlNode();
1969               outputMerge.doParseStatement(this);
1970               lcTable.setOutputMerge(outputMerge);
1971               break;
1972           }
1973           case td_unpivot:{
1974               // Set the TD_UNPIVOT output table before doParse so that VALUE_COLUMNS and
1975               // UNPIVOT_COLUMN can be linked to it (they are output columns of TD_UNPIVOT)
1976               pfromTable.getTdUnpivot().setTdUnpivotOutputTable(lcTable);
1977               pfromTable.getTdUnpivot().doParse(this,ESqlClause.tdUnPivot);
1978               lcTable.setTdUnpivot(pfromTable.getTdUnpivot());
1979               break;
1980           }
1981           case unnest:{
1982               pfromTable.getUnnestClause().doParse(this,ESqlClause.elTable);
1983               lcTable.setUnnestClause(pfromTable.getUnnestClause());
1984               if (lcTable.getAliasClause() != null){
1985                   if (lcTable.getAliasClause().getColumns() != null){
1986                       for(TObjectName pColumn:lcTable.getAliasClause().getColumns()){
1987                           lcTable.getLinkedColumns().addObjectName(pColumn);
1988                           pColumn.setSourceTable(lcTable);
1989                       }
1990                   }else if (lcTable.getAliasClause().getAliasName() != null){
1991//                       SELECT *
1992//                               FROM UNNEST(['foo', 'bar', 'baz', 'qux', 'corge', 'garply', 'waldo', 'fred']) AS element
1993//                       WITH OFFSET AS offset
1994
1995                       // add element as column of unnest table.
1996                       TObjectName newColumn = TObjectName.createObjectName(this.dbvendor,EDbObjectType.column,lcTable.getAliasClause().getAliasName().getStartToken());
1997                       lcTable.getLinkedColumns().addObjectName(newColumn);
1998                       newColumn.setSourceTable(lcTable);
1999                   }
2000               }
2001
2002               if (lcTable.getUnnestClause().getWithOffset() != null){
2003                   if (lcTable.getUnnestClause().getWithOffsetAlais() != null){
2004                       // with offset as offsetAlias
2005                       TAliasClause aliasClause = lcTable.getUnnestClause().getWithOffsetAlais();
2006                       if (aliasClause.getAliasName() != null){
2007                           TObjectName newColumn = TObjectName.createObjectName(this.dbvendor,EDbObjectType.column,aliasClause.getAliasName().getStartToken());
2008                           lcTable.getLinkedColumns().addObjectName(newColumn);
2009                           newColumn.setSourceTable(lcTable);
2010                       }
2011                   }else{
2012                       // with offset
2013                       TObjectName newColumn = TObjectName.createObjectName(this.dbvendor,EDbObjectType.column,new TSourceToken("offset"));
2014                       lcTable.getLinkedColumns().addObjectName(newColumn);
2015                       newColumn.setSourceTable(lcTable);
2016                   }
2017               }
2018
2019               // link columns in the select list to unnest()
2020               // select emp_id,name,state,city,zipcode from `absolute-runner-302907.gudu_sqlflow.ADDRESS_NESTED`, UNNEST(address)
2021               if (lcTable.getUnnestClause().getDerivedColumnList() != null){
2022                   TObjectNameList derivedColumns = lcTable.getUnnestClause().getDerivedColumnList();
2023                   for(int i=0;i<derivedColumns.size();i++){
2024                       //System.out.println(derivedColumns.getObjectName(i).toString());
2025                       lcTable.getLinkedColumns().addObjectName(derivedColumns.getObjectName(i));
2026                   }
2027               }
2028
2029               break;
2030           }
2031           case jsonTable:{
2032               ESqlClause location = ESqlClause.jsonTable;//resultColumn;
2033               lcTable.setJsonTable(pfromTable.getJsonTable());
2034               lcTable.getJsonTable().doParse(this,location);
2035               break;
2036           }
2037           case externalTable:
2038               lcTable.setTableName(pfromTable.getTableObjectName());
2039               lcTable.setColumnDefinitions(pfromTable.getColumnDefinitions());
2040               lcTable.getColumnDefinitions().doParse(this,pLocation);
2041               lcTable.setTableType(ETableSource.externalTable); // tableType is reset in setTableName() method, so we reset it here
2042               break;
2043           case caseJoin:
2044               lcTable.setCaseJoin(pfromTable.getCaseJoin());
2045               lcTable.getCaseJoin().doParse(this,pLocation);
2046               break;
2047           case stageReference:
2048               lcTable.setStageReference(pfromTable.getStageReference());
2049               lcTable.getStageReference().doParse(this,pLocation);
2050
2051               lcTable.setTableName(lcTable.getStageReference().getStageName());
2052               lcTable.setTableType(ETableSource.stageReference);
2053               lcTable.getTableName().setSqlEnv(getSqlEnv());
2054
2055               break;
2056
2057      }//switch
2058
2059//        if (pfromTable.getPivotClause() != null){
2060//            lcTable.setPivotClause(pfromTable.getPivotClause());
2061//            lcTable.getPivotClause().doParse(this,ESqlClause.resultColumn);
2062//        }
2063
2064        lcTable.setPartitionExtensionClause(pfromTable.getPartitionExtensionClause());
2065
2066        //tables.addTable(lcTable);
2067        if (addToTableList) {
2068            addToTables(lcTable);
2069        }
2070
2071        if (lcTable.getTableHintList() != null){
2072            for(int i=0;i<lcTable.getTableHintList().size();i++){
2073                TTableHint hint = lcTable.getTableHintList().getElement(i);
2074                hint.setOwnerTable(lcTable);
2075                hint.doParse(this,ESqlClause.tableHint);
2076            }
2077        }
2078
2079        if (lcTable.getLateralViewList() != null){
2080            for(TLateralView lateralView:lcTable.getLateralViewList()){
2081                TTable t = lateralView.createATable(this);
2082                addToTables(t);
2083                this.relations.add(t);
2084            }
2085        }
2086
2087        if (lcTable.getAliasClause() != null){
2088            if (lcTable.getAliasClause().toString().equalsIgnoreCase("and")){
2089                // end keyword can't be alias name
2090                TSourceToken st1 = lcTable.getAliasClause().getStartToken();
2091                TSyntaxError err = new TSyntaxError(st1.toString()
2092                        ,st1.lineNo,st1.columnNo
2093                        ,String.format("AND keyword can't be table alias")
2094                        ,EErrorType.sperror
2095                        ,TBaseType.MSG_ERROR_AND_KEYWORD_CANT_USED_AS_TABLE_ALIAS
2096                        ,this,st1.posinlist);
2097                this.parseerrormessagehandle( err);
2098
2099            }
2100        }
2101
2102        return lcTable;
2103    }
2104
2105   public TJoin analyzeJoin(TJoinExpr pJoinExpr,TJoin pJoin,Boolean isSub){
2106        TJoin retval = pJoin;
2107        TJoinItem lcJoinItem = null ;
2108
2109        if (pJoinExpr == null) {return retval;}
2110
2111        if (pJoinExpr.getJointype() == EJoinType.nested)
2112        {
2113            if (isSub)
2114            {
2115                if (retval == null) {  // top level, left side is a join
2116                  retval = new TJoin();
2117                  retval.setStartToken(pJoinExpr.getStartToken());
2118                  retval.setEndToken(pJoinExpr.getEndToken());
2119                }
2120
2121                pJoinExpr.setJointype(pJoinExpr.original_jontype);
2122                retval.setJoin(analyzeJoin(pJoinExpr,null,true));
2123                //retval =analyzeJoin(pJoinExpr,null,true);
2124                retval.setKind(TBaseType.join_source_join);
2125                retval.getJoin().setAliasClause(pJoinExpr.getAliasClause());
2126                retval.getJoin().setWithParen(true);
2127                retval.getJoin().setNestedParen(pJoinExpr.getNestedParen());
2128            }
2129           else
2130            {
2131                if (retval == null)
2132                {
2133                    retval = new TJoin();
2134                    retval.setStartToken(pJoinExpr.getStartToken());
2135                    retval.setEndToken(pJoinExpr.getEndToken());
2136                    retval.setGsqlparser(this.getGsqlparser());
2137                }
2138                else
2139                {
2140                }
2141                pJoinExpr.setJointype(pJoinExpr.original_jontype);
2142                retval = analyzeJoin(pJoinExpr,retval,false);
2143                //retval.setJoin(analyzeJoin(pJoinExpr,retval,false));
2144                //retval = analyzeJoin(pJoinExpr,retval,false);
2145                //retval.setKind(TBaseType.join_source_join);
2146                //retval.setKind(TBaseType.join_source_table);
2147                //retval.setAliasClause(pJoinExpr.getAliasClause());
2148                retval.setAliasClause(pJoinExpr.getAliasClause());
2149                retval.setWithParen(true);
2150                retval.setNestedParen(pJoinExpr.getNestedParen());
2151            }
2152            return retval;
2153        }
2154
2155        if (pJoinExpr.getLeftOperand().getFromtableType() != ETableSource.join){
2156            if (retval == null) {
2157              retval = new TJoin();
2158              retval.setStartToken(pJoinExpr.getStartToken());
2159              retval.setEndToken(pJoinExpr.getEndToken());
2160              retval.setGsqlparser(this.getGsqlparser());
2161
2162              //  retval.setStartToken(pJoinExpr.getLeftOperand().getStartToken());
2163              //  retval.setEndToken(pJoinExpr.getLeftOperand().getEndToken());
2164            }
2165            TTable lcTable = analyzeFromTable(pJoinExpr.getLeftOperand(),true,ESqlClause.join);
2166            lcTable.setEffectType(ETableEffectType.tetSelect);
2167            retval.setTable(lcTable);
2168            //retval.joinTable.OwnerJoin = result;
2169            retval.setKind(TBaseType.join_source_table);
2170            pJoinExpr.setLeftTable(lcTable);
2171        }else{
2172            TJoinExpr lcJoinItemJoinExpr = pJoinExpr.getLeftOperand().getJoinExpr();
2173            //if (lcJoinItemJoinExpr.getJointype() == TBaseType.join_nested){
2174            //    lcJoinItemJoinExpr.setJointype(lcJoinItemJoinExpr.original_jontype);
2175            //}
2176
2177            if (retval != null) {
2178              retval = analyzeJoin(lcJoinItemJoinExpr,retval,true);
2179            } else {
2180              retval = analyzeJoin(lcJoinItemJoinExpr,retval,isSub);
2181            }
2182            retval.setStartToken(lcJoinItemJoinExpr.getStartToken());
2183            retval.setEndToken(lcJoinItemJoinExpr.getEndToken());
2184
2185
2186            TTable lcTable = new TTable();
2187            lcTable.setTableType(pJoinExpr.getLeftOperand().getFromtableType());
2188            lcTable.setAliasClause(lcJoinItemJoinExpr.getAliasClause());
2189            lcTable.setStartToken(lcJoinItemJoinExpr.getStartToken());
2190            lcTable.setEndToken(lcJoinItemJoinExpr.getEndToken());
2191            pJoinExpr.setLeftTable(lcTable);
2192            lcTable.setJoinExpr(lcJoinItemJoinExpr);
2193        }
2194
2195        if (pJoinExpr.getRightOperand().getFromtableType() != ETableSource.join){
2196            if (retval != null)
2197            {
2198                lcJoinItem = new TJoinItem();
2199                TTable lcTable = analyzeFromTable(pJoinExpr.getRightOperand(),true,ESqlClause.join);
2200                lcTable.setEffectType(ETableEffectType.tetSelect);
2201                lcJoinItem.setTable(lcTable);
2202                lcJoinItem.setStartToken(lcJoinItem.getTable().getStartToken());
2203                lcJoinItem.setEndToken(lcJoinItem.getTable().getEndToken());
2204               // lcJoinItem.JoinItemTable.OwnerJoinItem := lcJoinItem;
2205                lcJoinItem.setKind(TBaseType.join_source_table);
2206                retval.getJoinItems().addJoinItem(lcJoinItem);
2207                pJoinExpr.setRightTable(lcTable);
2208            }
2209        }else{
2210            if (retval != null)
2211            {
2212                lcJoinItem = new TJoinItem();
2213                lcJoinItem.setKind(TBaseType.join_source_join);
2214                TJoinExpr lcJoinItemJoinExpr = pJoinExpr.getRightOperand().getJoinExpr();
2215                //if (lcJoinItemJoinExpr.getJointype() == TBaseType.join_nested){
2216                //    lcJoinItemJoinExpr.setJointype(lcJoinItemJoinExpr.original_jontype);
2217                //}
2218                lcJoinItem.setJoin(analyzeJoin(pJoinExpr.getRightOperand().getJoinExpr(),null,false));
2219                lcJoinItem.getJoin().setAliasClause(lcJoinItemJoinExpr.getAliasClause());
2220                lcJoinItem.setStartToken(lcJoinItem.getJoin().getStartToken());
2221                lcJoinItem.setEndToken(lcJoinItem.getJoin().getEndToken());
2222                retval.getJoinItems().addJoinItem(lcJoinItem);
2223
2224                TTable lcTable = new TTable();
2225                lcTable.setTableType(pJoinExpr.getRightOperand().getFromtableType());
2226                lcTable.setAliasClause(lcJoinItemJoinExpr.getAliasClause());
2227                lcTable.setStartToken(lcJoinItemJoinExpr.getStartToken());
2228                lcTable.setEndToken(lcJoinItemJoinExpr.getEndToken());
2229                pJoinExpr.setRightTable(lcTable);
2230                lcTable.setJoinExpr(lcJoinItemJoinExpr);
2231            }
2232        }
2233
2234        if (lcJoinItem == null) return retval;
2235
2236        lcJoinItem.setJoinType(pJoinExpr.getJointype());
2237        lcJoinItem.setUsingColumns(pJoinExpr.usingColumns);
2238        if ((lcJoinItem.getUsingColumns() != null) && (tables.size()>1)){
2239            TObjectName crf ;
2240            for (int i=0;i<lcJoinItem.getUsingColumns().size();i++){
2241                crf = lcJoinItem.getUsingColumns().getObjectName(i);
2242                // link this column to last 2 tables
2243                tables.getTable(tables.size()-1).getObjectNameReferences().addObjectName(crf);
2244                tables.getTable(tables.size()-2).getObjectNameReferences().addObjectName(crf);
2245
2246                tables.getTable(tables.size()-1).getLinkedColumns().addObjectName(crf);
2247                crf.setSourceTable(tables.getTable(tables.size()-1));
2248                tables.getTable(tables.size()-2).getLinkedColumns().addObjectName(crf);
2249                crf.setSourceTable(tables.getTable(tables.size()-2));
2250
2251            }
2252            lcJoinItem.setEndToken(lcJoinItem.getUsingColumns().getEndToken());
2253        }
2254        lcJoinItem.setOnCondition(pJoinExpr.onCondition);
2255        if (lcJoinItem.getOnCondition() != null)
2256        {
2257            lcJoinItem.getOnCondition().doParse(this,ESqlClause.joinCondition);
2258            lcJoinItem.setEndToken(lcJoinItem.getOnCondition().getEndToken());
2259        }
2260
2261
2262        return retval;
2263    }
2264
2265    public boolean locateVariableOrParameter(TObjectName cr){
2266        return locateVariableOrParameter(cr,false);
2267    }
2268
2269    public boolean locateVariableOrParameter(TObjectName cr, boolean checkVariableDeclaredInProcedure){
2270      boolean ret =  false;
2271      if (cr.getDbObjectType() == EDbObjectType.variable) return true;
2272      if (cr.toString().equalsIgnoreCase("*")) return  false;
2273      //search variable in framestack
2274
2275      TVariable symbolVariable = null;
2276
2277      if (cr.getTableToken() != null){
2278          // record_variable.column
2279          symbolVariable =  TSymbolTableManager.searchSymbolVariable(this.getFrameStack(),cr.getTableToken().toString());
2280          if (symbolVariable != null){
2281              cr.getTableToken().setDbObjectType(EDbObjectType.variable);
2282              //TTable sourceTable = new TTable(new TObjectName(EDbObjectType.table,symbolVariable.getVariableName().getStartToken()));
2283              TTable sourceTable = new TTable(TObjectName.createObjectName (this.dbvendor, EDbObjectType.variable,symbolVariable.getVariableName().getStartToken()));
2284              sourceTable.getLinkedColumns().addObjectName(cr);
2285              cr.setSourceTable(sourceTable);
2286             // symbolVariable.getVariableName().getReferencedObjects().addObjectName(cr);
2287             // System.out.println("find variable:"+cr.toString());
2288              cr.setResolveStatus(TBaseType.RESOLVED_AND_FOUND); // set resolve status to resolved,避免在 TAttributeResolver 中关联到其他 table
2289              return true;
2290          }
2291
2292      }else{
2293          // variable
2294          symbolVariable =  TSymbolTableManager.searchSymbolVariable(this.getFrameStack(),cr.toString());
2295          if (symbolVariable != null){
2296              cr.setDbObjectType(EDbObjectType.variable);
2297              symbolVariable.getVariableName().getReferencedObjects().addObjectName(cr);
2298              return true;
2299          }
2300      }
2301
2302        // check parameters in plsql only, may add support for sql server later.
2303      if(! ((dbvendor == EDbVendor.dbvoracle)||(dbvendor == EDbVendor.dbvmysql))) return false;
2304      if (cr.getObjectType() == TObjectName.ttobjVariable) return true;
2305
2306        Stack symbolTable = this.getTopStatement().getSymbolTable();
2307        TSymbolTableItem item = null;
2308        TObjectName objName = null;
2309        TObjectName qualifiedName = null; // function/procedure name or label name of plsql block
2310        for (int i = symbolTable.size()-1;i>=0;i--){
2311            item = (TSymbolTableItem)symbolTable.get(i);
2312            if (item.getData() instanceof TParameterDeclaration){
2313                objName = ((TParameterDeclaration)item.getData()).getParameterName();
2314            }else if (item.getData() instanceof TVarDeclStmt){
2315                objName = ((TVarDeclStmt)item.getData()).getElementName();
2316            }else  if (item.getData() instanceof TObjectName){
2317                objName = (TObjectName)item.getData();
2318            }
2319
2320            if (objName == null) continue;
2321
2322            // strip quote characters so a quoted declaration ("P_DATE" IN NUMBER)
2323            // still matches an unquoted reference P_DATE (MantisBT 4504)
2324            if (TBaseType.getTextWithoutQuoted(cr.toString())
2325                    .compareToIgnoreCase(TBaseType.getTextWithoutQuoted(objName.toString())) == 0){
2326                ret = true;
2327                if (checkVariableDeclaredInProcedure) break; // return true if variable declared in procedure
2328                for(int j=0;i<tables.size();i++){
2329                    TTable lcTable = tables.getTable(j);
2330                    if (lcTable.isBaseTable()){
2331                        if (fireOnMetaDatabaseTableColumn(
2332                                             lcTable.getPrefixServer()
2333                                            ,lcTable.getPrefixDatabase()
2334                                            ,lcTable.getPrefixSchema()
2335                                            ,lcTable.getName()
2336                                            ,cr.getColumnNameOnly())){
2337                            ret = false;
2338                            break;
2339                        }
2340                    }
2341                }
2342
2343                if (ret)  break;
2344            }else if (cr.toString().indexOf(".")>0){
2345                // qualified object reference, compare it with procedure/function/block label prefixed
2346                if (item.getStmt() instanceof TPlsqlCreateFunction){
2347                   qualifiedName = ((TPlsqlCreateFunction)item.getStmt()).getFunctionName();
2348                }else if (item.getStmt() instanceof TPlsqlCreateProcedure){
2349                   qualifiedName = ((TPlsqlCreateProcedure)item.getStmt()).getProcedureName();
2350                }else if (item.getStmt() instanceof TCommonBlock){
2351                   qualifiedName = ((TCommonBlock)item.getStmt()).getLabelName();
2352                }
2353
2354                if (qualifiedName != null){
2355                    if (TBaseType.getTextWithoutQuoted(cr.toString()).compareToIgnoreCase(
2356                            TBaseType.getTextWithoutQuoted(qualifiedName.toString()) + '.'
2357                                    + TBaseType.getTextWithoutQuoted(objName.toString())) == 0){
2358                        ret = true;
2359                    }
2360                }
2361
2362                if (ret ) break;
2363            }
2364
2365        }
2366          if (ret){
2367              //add this parameter or variable reference to original parameter/variable
2368                  objName.getReferencedObjects().addObjectName(cr);
2369                  cr.setObjectType(TObjectName.ttobjVariable);
2370          }
2371          return ret;
2372    }
2373
2374    TCTE findCTEByName(String cteName){
2375        TCTEList lcCteList = searchCTEList(false);
2376        TCTE lcCte = null;
2377        if (lcCteList != null){
2378          for(int i=0;i<lcCteList.size();i++){
2379             if (lcCteList.getCTE(i).getTableName().toString().compareToIgnoreCase(cteName)==0){
2380                 lcCte = lcCteList.getCTE(i);
2381                 break;
2382             }
2383          }
2384        }
2385     return lcCte;
2386    }
2387
2388    /**
2389     * @deprecated since 2.3.8.2, use {@link TTable#getExpandedStarColumns()} instead.
2390     *
2391     * @param lcTable
2392     * @return
2393     */
2394    public ArrayList<String> getColumnsInTable(TTable lcTable){
2395        if (lcTable.isCTEName()){
2396            ArrayList<String> columns = new ArrayList<>();
2397            if (lcTable.getCteColomnReferences()!=null){
2398                for(TObjectName n:lcTable.getCteColomnReferences()){
2399                    columns.add(n.toString());
2400                }
2401            }else if (lcTable.getCTE().getSubquery() != null && lcTable.getCTE().getSubquery().getResultColumnList() != null){
2402                for(TResultColumn resultColumn:lcTable.getCTE().getSubquery().getResultColumnList()){
2403                    columns.add(resultColumn.getDisplayName());
2404                }
2405            }
2406            return columns;
2407        }else{
2408            return getColumnsInTable(
2409                    lcTable.getPrefixServer()
2410                    ,lcTable.getPrefixDatabase()
2411                    ,lcTable.getPrefixSchema()
2412                    ,lcTable.getName()
2413            );
2414        }
2415    }
2416
2417
2418    /**
2419     * @deprecated since 2.3.8.2, use {@link TTable#getExpandedStarColumns()} instead.
2420     *
2421     * @param pServer
2422     * @param pDatabase
2423     * @param pSchema
2424     * @param pTable
2425     * @return
2426     */
2427    public ArrayList<String> getColumnsInTable(String pServer,String pDatabase,String pSchema,String pTable){
2428        TFrame stackFrame = getFrameStack().firstElement();
2429        TGlobalScope globalScope = (TGlobalScope)stackFrame.getScope();
2430
2431        if (globalScope.getSqlEnv() != null){
2432            return globalScope.getSqlEnv().getColumnsInTable(pDatabase+"."+pSchema+"."+pTable,false);
2433        }else{
2434            return  null;
2435        }
2436    }
2437
2438
2439    public boolean fireOnMetaDatabaseTableColumn(String pServer,String pDatabase,String pSchema,String pTable,String pColumn){
2440//        boolean lcResult = false;
2441//        if (this.getGsqlparser().getMetaDatabase() != null){
2442//            lcResult = this.getGsqlparser().getMetaDatabase().checkColumn(pServer,pDatabase,pSchema,pTable,pColumn);
2443//        }
2444
2445        TFrame stackFrame = getFrameStack().firstElement();
2446        TGlobalScope globalScope = (TGlobalScope)stackFrame.getScope();
2447
2448        if (globalScope.getSqlEnv() != null){
2449           // System.out.println(globalScope.getSqlEnv().toString());
2450
2451            return globalScope.getSqlEnv().columnInTable(pDatabase+"."+pSchema+"."+pTable,pColumn);
2452        }else{
2453            return  false;
2454        }
2455
2456//        return lcResult;
2457    }
2458
2459    public TTable getFirstPhysicalTable(){
2460        TTable ret = null;
2461        if (tables.size() == 0) return null;
2462        for(int i=0;i<tables.size();i++){
2463            if (tables.getTable(i).isBaseTable()) {
2464                ret = tables.getTable(i);
2465                break;
2466            }
2467        }
2468        return ret;
2469    }
2470    private TObjectNameList orphanColumns = null;
2471
2472    public TObjectNameList getOrphanColumns() {
2473        if (orphanColumns == null) orphanColumns = new TObjectNameList();
2474        return orphanColumns;
2475    }
2476
2477    protected boolean linkToFirstTable(TObjectName pColumn,int pCandidateTableCnt){
2478        boolean lcResult = false;
2479        if ((dbvendor == EDbVendor.dbvteradata)&&(pColumn.isQualified())&&(pColumn.getTableToken().getDbObjectType() != EDbObjectType.subquery_alias)){
2480            // update table1 set col = 'value' where table1.id = table2.id2
2481            boolean isFoundLinkedTable = false;
2482            TCustomSqlStatement lcSql = this;
2483            while (lcSql != null){
2484                int i = 0;
2485                i = lcSql.tables.searchTableByNameOrAlias(pColumn.getTableToken().toString());
2486                isFoundLinkedTable = ( i != -1);
2487                if (isFoundLinkedTable) {
2488                    if (lcSql.tables.getTable(i).getEffectType() == ETableEffectType.tetImplicitLateralDerivedTable ){
2489                        // 如果不查重table,会导致 employee.first_name 中的 employee 被第二次加到 tables 中
2490//                        DELETE FROM foodmart.trimmed_employee ACT
2491//                        WHERE ACT.employee_id = employee.employee_id
2492//                        AND  employee.first_name = 'Walter'
2493//                        AND  trimmed_salary.employee_id = -1
2494
2495                        TTable newTable = lcSql.tables.getTable(i);
2496                        newTable.getLinkedColumns().addObjectName(pColumn);
2497                        pColumn.setSourceTable(newTable);
2498                        pColumn.setValidate_column_status(TBaseType.COLUMN_LINKED_TO_TABLE_IN_OLD_ALGORITHM);
2499                    }
2500                    break;
2501                }
2502                lcSql = lcSql.getParentStmt();
2503            }
2504            if (!isFoundLinkedTable){
2505                TTable newTable = null;
2506                if (pColumn.getDatabaseToken() == null){
2507
2508                    //newTable = new TTable(new TObjectName(EDbObjectType.table,pColumn.getTableToken()));
2509                    newTable = new TTable(TObjectName.createObjectName (this.dbvendor, EDbObjectType.table,pColumn.getTableToken()));
2510                    newTable.setStartToken(pColumn.getTableToken());
2511                    newTable.setEndToken(pColumn.getTableToken());
2512                }else{
2513
2514                    //newTable = new TTable(new TObjectName(EDbObjectType.table,pColumn.getSchemaToken(), pColumn.getTableToken()));
2515                    newTable = new TTable(TObjectName.createObjectName (this.dbvendor,EDbObjectType.table,pColumn.getDatabaseToken(), pColumn.getTableToken()));
2516                    newTable.setStartToken(pColumn.getSchemaToken());
2517                    newTable.setEndToken(pColumn.getTableToken());
2518                }
2519
2520                newTable.setTableType(ETableSource.objectname);
2521                newTable.setEffectType(ETableEffectType.tetImplicitLateralDerivedTable);
2522                newTable.getLinkedColumns().addObjectName(pColumn);
2523                pColumn.setSourceTable(newTable);
2524                pColumn.setValidate_column_status(TBaseType.COLUMN_LINKED_TO_TABLE_IN_OLD_ALGORITHM);
2525                this.addToTables(newTable);
2526
2527                // 2024 年
2528                // 不能加入到 relations 中,否则会导致 下面 SQL 中 star column 同时链接到 SPCOMM.L_FIXED_RATE_PLAN_REF, ipshare_ofccplv.cprof_d_period_dates_ref
2529                // 从而导致 本来不该有的歧义产生
2530
2531                // UPDATE b_rate_plan
2532                //FROM
2533                //(
2534                //SELECT * FROM SPCOMM.L_FIXED_RATE_PLAN_REF
2535                //WHERE rate_plan_ref_eff_dt<= ipshare_ofccplv.cprof_d_period_dates_ref.PERIOD
2536                //) AS ref
2537                //SET accs_fee = REF.accs_fee,
2538                //SVC_TYPE = REF.prod_grp_lvl_1,
2539                //rate_plan_lvl3 = REF.rate_plan_lvl_3,
2540                //prod_grp_lvl3 = REF.prod_grp_lvl_2
2541                //WHERE b_rate_plan.svc_type IS NULL
2542
2543                // 2025/2/25, v3.0.4.8
2544                // 需要加入到 relations 中,新的 gudusoft.gsqlparser.resolver package 中的算法会处理这种情况
2545                // teradata 的隐式横向派生表不能加入到关系解析器中
2546                // this.getRelations().add(newTable);
2547            }
2548            return true;
2549        }
2550        if (pColumn.getCandidateTables().size() == 1){
2551            TTable table = pColumn.getCandidateTables().getTable(0);
2552            table.getLinkedColumns().addObjectName(pColumn);
2553            pColumn.setSourceTable(table);
2554            lcResult = true;
2555        }
2556        else if ((tables.size() == 1) || (pCandidateTableCnt == 1)){
2557            TTable table = tables.getTable(0);
2558
2559            if(table.getTableType() == ETableSource.function){
2560                //lcResult = linkToFunctionTable(table, pColumn);
2561                int iRet = table.getFuncCall().isColumnInThisTableFunction(this.getSqlEnv(),this.dbvendor,pColumn);
2562                if ( iRet == TBaseType.COLUMN_IN_TABEL_FUNCTION_YES){
2563                    lcResult = true;
2564                    table.getLinkedColumns().addObjectName(pColumn);
2565                    pColumn.setSourceTable(table);
2566                    lcResult = true;
2567                }else if ( iRet == TBaseType.COLUMN_IN_TABEL_FUNCTION_NO){
2568                    lcResult = false;
2569                }else{
2570                    table.getLinkedColumns().addObjectName(pColumn);
2571                    pColumn.setSourceTable(table);
2572                    lcResult = true;
2573                }
2574            }else if(table.getTableType() == ETableSource.subquery){
2575                if (! table.getSubquery().searchColumnInResultSet(pColumn,(tables.size()==1))){
2576                    getOrphanColumns().addObjectName(pColumn);
2577                    pColumn.setOrphanColumn(true);
2578                    pColumn.setOwnStmt(this);
2579                    TSourceToken st = pColumn.getStartToken();
2580                    if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){
2581                        TBaseType.log(String.format("Add orphan column <%s> to statement in old algorithm in subquery %s",pColumn.toString(),table.getAliasName()),TLog.WARNING,table);
2582                    }
2583                    this.parseerrormessagehandle(new TSyntaxError(st.getAstext(), st.lineNo, st.columnNo
2584                            ,"find orphan column", EErrorType.sphint
2585                            , TBaseType.MSG_HINT_FIND_ORPHAN_COLUMN,this,st.posinlist,pColumn));
2586                }
2587            }
2588            else{
2589                table.getLinkedColumns().addObjectName(pColumn);
2590                pColumn.setSourceTable(table);
2591                lcResult = true;
2592                if ((dbvendor == EDbVendor.dbvbigquery)&&(pCandidateTableCnt == 0) && (pColumn.isQualified())){
2593                    // bigquery struct column used in query
2594//                    create view test as (SELECT rollNo,
2595//                            info.name as n1,
2596//                    info2.name as n2,
2597//                            info.age from my_first_dataset.student_records);
2598
2599                    pColumn.columnToProperty();
2600                }
2601            }
2602        }else if (tables.size() > 1){
2603            // if there is only a table without table alias, then, link to this table
2604            boolean foundOnlyOneTable = false;
2605            TTable tableWithoutAlias = null;
2606            for(TTable table:tables){
2607                if (table.isCTEName()) continue; // CTE 即便没有 指定alias,也不作为考虑对象
2608                if (table.getAliasClause() == null){
2609                    tableWithoutAlias = table;
2610                    if (foundOnlyOneTable){
2611                        foundOnlyOneTable = false;
2612                        break;
2613                    }else{
2614                        foundOnlyOneTable = true;
2615                    }
2616                }
2617            }
2618
2619            if (foundOnlyOneTable){
2620                tableWithoutAlias.getLinkedColumns().addObjectName(pColumn);
2621                pColumn.setSourceTable(tableWithoutAlias);
2622                lcResult = true;
2623            }else{
2624                getOrphanColumns().addObjectName(pColumn);
2625                pColumn.setOrphanColumn(true);
2626                pColumn.setOwnStmt(this);
2627                TSourceToken st = pColumn.getStartToken();
2628                if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){
2629                    TBaseType.log(String.format("Add orphan column <%s> to statement in old algorithm ",pColumn.toString()),TLog.WARNING,this);
2630                }
2631
2632                this.parseerrormessagehandle(new TSyntaxError(st.getAstext(), st.lineNo, st.columnNo
2633                        ,"find orphan column", EErrorType.sphint
2634                        , TBaseType.MSG_HINT_FIND_ORPHAN_COLUMN,this,st.posinlist,pColumn));
2635            }
2636
2637        }
2638        return  lcResult;
2639    }
2640
2641    private boolean linkToFunctionTable(TTable table, TObjectName pColumn) {
2642        if(table.getTableName().toString().toUpperCase().equals("STRING_SPLIT")){
2643            if(pColumn.getColumnNameOnly().toUpperCase().equals("VALUE")){
2644                table.getLinkedColumns().addObjectName(pColumn);
2645                pColumn.setSourceTable(table);
2646                return true;
2647            }
2648            else return false;
2649        }
2650        else {
2651            table.getLinkedColumns().addObjectName(pColumn);
2652            pColumn.setSourceTable(table);
2653            return true;
2654        }
2655    }
2656
2657
2658    private TTable findInsertedOrDeleteTable(TTable table) {
2659        if (table == null) return null;
2660        
2661        // Check if this table is 'inserted' by examining both name and toString()
2662        // The inserted table might have different representations
2663        String tableName = table.getName();
2664        String tableString = table.toString();
2665        
2666        if ("inserted".equalsIgnoreCase(tableName) || 
2667            "inserted".equalsIgnoreCase(tableString) ||
2668            "deleted".equalsIgnoreCase(tableName) ||
2669            "deleted".equalsIgnoreCase(tableString)) {
2670            return table;
2671        }
2672        
2673        // If this is a join table, recursively check its components
2674        if (table.getTableType() == ETableSource.join && table.getJoinExpr() != null) {
2675            TJoinExpr joinExpr = table.getJoinExpr();
2676            
2677            // Check left side recursively
2678            TTable leftResult = findInsertedOrDeleteTable(joinExpr.getLeftTable());
2679            if (leftResult != null) {
2680                return leftResult;
2681            }
2682            
2683            // Check right side recursively  
2684            TTable rightResult = findInsertedOrDeleteTable(joinExpr.getRightTable());
2685            if (rightResult != null) {
2686                return rightResult;
2687            }
2688        }
2689        
2690        return null;
2691    }
2692   
2693
2694    boolean isSQLServerInsertedDelete(TObjectName pColumn){
2695        if (dbvendor != EDbVendor.dbvmssql) return false;
2696
2697        // only process sql server inserted delete column, if not then return false
2698       // if (!((pColumn.toString().toUpperCase().startsWith("INSERTED"))||(pColumn.toString().toUpperCase().startsWith("DELETED")))) return false;
2699        if (!((pColumn.getStartToken().tokencode == TBaseType.rrw_sqlserver_INSERTED )
2700                    ||(pColumn.getStartToken().tokencode == TBaseType.rrw_sqlserver_DELETED ))) return false;
2701
2702        // 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
2703        // to find the target table in the literal as 'inserted' or 'deleted'.
2704
2705        TTable lcTargetTable = null;
2706        for(TTable table : this.getRelations()){
2707            lcTargetTable = findInsertedOrDeleteTable(table);
2708            if (lcTargetTable != null){
2709                break;
2710            }
2711        }
2712
2713        if (lcTargetTable == null) return false;
2714
2715
2716        if (lcTargetTable.getLinkTable() != null){
2717            lcTargetTable.getLinkTable().getLinkedColumns().addObjectName(pColumn);
2718            pColumn.setSourceTable(lcTargetTable.getLinkTable());
2719
2720            pColumn.setResolveStatus(TBaseType.RESOLVED_AND_FOUND); // 避免在 // TAttributeResolver 中再次进行处理,关联到其他 table
2721
2722        }else{
2723            lcTargetTable.getLinkedColumns().addObjectName(pColumn);
2724            pColumn.setSourceTable(lcTargetTable);
2725            pColumn.setResolveStatus(TBaseType.RESOLVED_AND_FOUND); // 避免在 // TAttributeResolver 中再次进行处理,关联到其他 table
2726        }
2727        return true;
2728
2729    }
2730
2731    boolean isOracleNewOldTable(TObjectName pColumn){
2732        boolean ret = false;
2733        if (dbvendor != EDbVendor.dbvoracle) return false;
2734        if (!(pColumn.isQualified())) return false;
2735        if ((pColumn.getTableString().equalsIgnoreCase(":new"))
2736            ||(pColumn.getTableString().equalsIgnoreCase(":old"))){
2737            if (getAncestorStmt().tables != null){
2738                if (getAncestorStmt().tables.size() > 0){
2739                    getAncestorStmt().tables.getTable(0).getLinkedColumns().addObjectName(pColumn);
2740                    pColumn.setSourceTable(getAncestorStmt().tables.getTable(0));
2741                    ret = true;
2742                }
2743            }
2744        }
2745
2746        return ret;
2747    }
2748
2749    public boolean searchDaxVariableInStack(TObjectName pName){
2750        boolean ret = false;
2751        if (getVariableStack().size() == 0) return false;
2752        if (pName.getDbObjectType() == EDbObjectType.column) return false;
2753        for(int i=0;i<variableStack.size();i++){
2754            if (pName.toString().equalsIgnoreCase(((TObjectName) variableStack.get(i)).toString())){
2755                ret = true;
2756                break;
2757            }
2758        }
2759        return ret;
2760    }
2761
2762    boolean linkColumnToTableDax(TObjectName pColumn, ESqlClause pLocation){
2763        boolean lcResult = true ;
2764        TDaxFunction daxFunction = null;
2765        if (searchDaxVariableInStack(pColumn)) return false;
2766        if (getDaxFunctionStack().size() > 0){
2767            daxFunction = daxFunctionStack.peek();
2768        }
2769
2770        if (pColumn.getTableToken() != null){
2771            //TTable sourceTable = new TTable(new TObjectName(EDbObjectType.table,pColumn.getTableToken()));
2772            TTable sourceTable = new TTable(TObjectName.createObjectName (this.dbvendor,EDbObjectType.table,pColumn.getTableToken()));
2773            sourceTable.getLinkedColumns().addObjectName(pColumn);
2774            addToTables(sourceTable);
2775            if ((daxFunction != null) && (daxFunction.getDefaultTable() == null)){
2776                daxFunction.setDefaultTable(sourceTable);
2777            }
2778        }else{
2779            if ((daxFunction != null) &&(daxFunction.getDefaultTable() != null)){
2780                daxFunction.getDefaultTable().getLinkedColumns().addObjectName(pColumn);
2781            }else{
2782                ((TDaxStmt)this).getDefaultTable().getLinkedColumns().addObjectName(pColumn);
2783            }
2784        }
2785        return  lcResult;
2786    }
2787
2788    /**
2789     * 将列引用解析并绑定到其来源(表、子查询、CTE、表函数、OPENQUERY/UNNEST 等)。
2790     *
2791     * 功能概述:
2792     * 1) 针对 DAX 语法直接走 DAX 分支。
2793     * 2) 已绑定或标记“延迟到列解析器”的列直接返回。
2794     * 3) 设定列所在语法位置,并校验列名/保留字(含 MySQL true/false/default、内置函数等)。
2795     * 4) 处理厂商伪表/特殊前缀(Oracle :new/:old;SQL Server INSERTED/DELETED)。
2796     * 5) Insert All/VALUES 场景:优先在子查询结果集中/变量或过程参数中匹配。
2797     * 6) 在当前语句的 FROM 表集合中查找并建立绑定:
2798     *    - 限定列 table.col:按别名/表名匹配;对子查询/CTE/OPENQUERY 进一步在结果集中定位源列;
2799     *      命中后写入 linkedColumns,必要时将 TableToken 标记为 subquery_alias。
2800     *    - 非限定列 col:
2801     *      a. 先尝试同层 SELECT 列别名(支持 LATERAL 语义且位置在别名之后);
2802     *      b. 处理通配符“*”:收集所有来源表;
2803     *      c. 基础表通过元数据回调 fireOnMetaDatabaseTableColumn 校验;未命中则记录候选;
2804     *      d. 子查询/CTE/函数/UNNEST/PIVOT 分别按各自规则匹配。
2805     * 7) 命中后将列加入表的 linkedColumns 并设置 sourceTable/sourceColumn,必要时维持 isContinue 以继续匹配“*”。
2806     * 8) 若未命中:尝试变量/参数;再按条件(语句类型/位置/是否限定等)向上一层语句递归查找(维护 searchLevel)。
2807     * 9) 仍未命中:在顶层(searchLevel==0)按“候选唯一/或首表”兜底策略 {@link #linkToFirstTable(TObjectName, int)}。
2808     *
2809     * 参数:
2810     * @param pColumn   需要绑定的列名对象(方法会更新其 location、sourceTable、sourceColumn 等)
2811     * @param pLocation 列出现的语法位置(如 selectList、where、insertValues 等)
2812     *
2813     * 返回值:
2814     * @return 成功绑定到某个来源返回 true;未能绑定或被识别为变量/保留字等返回 false
2815     *
2816     * 厂商兼容:
2817     * - Oracle: 处理 :new/:old,Insert All 的 values 子句源自子查询的匹配
2818     * - SQL Server: 处理 INSERTED/DELETED 伪表
2819     * - MySQL: 对保留字/布尔字面量/内置函数名的特殊判断
2820     * - DAX: 委托 {@link #linkColumnToTableDax(TObjectName, ESqlClause)}
2821     *
2822     * 副作用:
2823     * - 更新 pColumn 的 location/searchLevel/sourceTable/sourceColumn/validate 状态
2824     * - 向命中的表写入 linkedColumns 或向别名列写入 targetColumns
2825     * - 对“*”列填充 sourceTableList;对子查询命中时可能将 TableToken 标为 subquery_alias
2826     * - 记录候选表数量并填充 pColumn.candidateTables,用于后续兜底
2827     *
2828     * 复杂度与顺序:
2829     * - 优先使用同层信息(别名/元数据/子查询结果),再逐层向外查找;避免无谓的上层搜索
2830     *
2831     * 注意:
2832     * - 本方法完成“旧算法”的快速联接,新的解析/消歧逻辑在解析器(如 TStmtScope/TAttributeResolver)中继续处理
2833     */
2834    public boolean linkColumnToTable(TObjectName pColumn, ESqlClause pLocation){
2835        boolean lcResult = false,isContinue = false;
2836        int candidateTableCnt = 0;
2837        if (pColumn == null) return false;
2838        if (dbvendor == EDbVendor.dbvdax){
2839            return linkColumnToTableDax(pColumn,pLocation);
2840        }
2841
2842        // Skip alias definition columns - they define column names in alias clauses, not column references
2843        // Example: In "AS x (numbers, animals)", numbers and animals are column_alias type
2844        if (pColumn.getDbObjectType() == EDbObjectType.column_alias) {
2845            return true;
2846        }
2847
2848        if (pColumn.getSourceTable() != null) {
2849            lcResult = true;
2850            return lcResult;
2851        }
2852
2853        if (pColumn.getResolveStatus() == TBaseType.RESOLVE_DELAY_TO_COLUMN_RESOLVER) return true;
2854
2855        pColumn.setLocation(pLocation);
2856
2857        if (! pColumn.isValidColumnName(dbvendor)) {
2858            if (pColumn.isReservedKeyword()){
2859                if (
2860                        ((pColumn.getStartToken().tokencode != TBaseType.rrw_mysql_true)&&(!(pColumn.getStartToken().toString().equalsIgnoreCase("true"))))
2861                        &&((pColumn.getStartToken().tokencode != TBaseType.rrw_mysql_false)&&(!(pColumn.getStartToken().toString().equalsIgnoreCase("false"))))
2862                        &&(pColumn.getStartToken().tokencode != TBaseType.rrw_mysql_default)
2863                        &&(pColumn.getStartToken().tokencode != TBaseType.rrw_on)&&(!(pColumn.getStartToken().toString().equalsIgnoreCase("on")))
2864                   ) {
2865                        boolean mysqlBuiltFunction = false;
2866                        if (dbvendor == EDbVendor.dbvmysql){
2867                            mysqlBuiltFunction = functionChecker.isBuiltInFunction(pColumn.toString(),EDbVendor.dbvmysql,"6.0");
2868                        }
2869                        if (!mysqlBuiltFunction){
2870                            TSourceToken st1 = pColumn.getStartToken();
2871                            TSyntaxError err = new TSyntaxError(st1.toString()
2872                                    , st1.lineNo, st1.columnNo
2873                                    , String.format("Reserved keyword can't be column name")
2874                                    , EErrorType.sperror
2875                                    , TBaseType.MSG_ERROR_RESERVED_KEYWORD_CANT_USED_AS_COLUMN_NAME
2876                            ,this,st1.posinlist);
2877                            this.parseerrormessagehandle(err);
2878                        }
2879                }
2880            }
2881            return false;
2882        }
2883
2884        if (isOracleNewOldTable(pColumn)) return true;
2885        if (isSQLServerInsertedDelete(pColumn)) return true;
2886
2887        // oracle insert all statement,
2888        // WHEN id <= 3 THEN INTO dest_tab1 VALUES(id, description1)
2889        // column in values clause must be in the subquery of insert all statement
2890        if (pColumn.getLocation() == ESqlClause.insertValues){
2891            if (this instanceof TInsertSqlStatement){
2892                TInsertSqlStatement insertSqlStatement = (TInsertSqlStatement)this;
2893                if (insertSqlStatement.isInsertAll()){
2894                   // if (pColumn.getStartToken().tokencode == TBaseType.rrw_snowflake_default) return true;
2895                    lcResult = insertSqlStatement.getSubQuery().searchColumnInResultSet(pColumn, true);
2896                }
2897            }
2898
2899            if (lcResult) return true;
2900
2901            // value in values clause maybe parameter of the procedure/function parameter
2902            lcResult = locateVariableOrParameter(pColumn,true);
2903            if (lcResult) return true;
2904        }
2905
2906        boolean foundInMetaData = false;
2907
2908        for(int i=0;i<tables.size();i++){
2909            TTable lcTable = tables.getTable(i);
2910            if (lcTable.getEffectType() == ETableEffectType.tetSelectInto) continue;
2911            if (lcTable.getEffectType() == ETableEffectType.tetImplicitLateralDerivedTable) continue;
2912
2913            if (pColumn.isQualified()){
2914                lcResult = pColumn.resolveWithThisTable(lcTable);
2915                if ((lcResult) && (lcTable.getTableType() == ETableSource.subquery)){
2916                    pColumn.getTableToken().setDbObjectType(EDbObjectType.subquery_alias);
2917
2918                    int lcPos = lcTable.searchColumnInAlias(pColumn);
2919                    lcResult = lcPos>=0;
2920                    if (lcResult){
2921                        // 在 alias 中找到 source column, 还需要对应到 subquery select 中的 select list
2922                        // sql 见 https://e.gitee.com/gudusoft/projects/151613/tasks/list?issue=I8JR0W#note_23051633
2923                        if ((lcTable.getSubquery() != null)&&(lcTable.getSubquery().getResultColumnList() != null)){
2924                            pColumn.setSourceColumn(lcTable.getSubquery().getResultColumnList().getResultColumn(lcPos));
2925                        }
2926                    }else{
2927                        lcResult =  lcTable.getSubquery().searchColumnInResultSet(pColumn,true);
2928                    }
2929               //     lcTable.getSubquery().searchColumnInResultSet(pColumn,true);
2930
2931                }else if ((lcResult) && (lcTable.isCTEName())){
2932                    lcTable.getCTE().searchColumnInResultSet(this,lcTable,pColumn,true);
2933                }else if ((lcResult) && (lcTable.getTableType() == ETableSource.openquery)&&(lcTable.getSubquery() != null)){
2934                    pColumn.getTableToken().setDbObjectType(EDbObjectType.subquery_alias);
2935                    lcTable.getSubquery().searchColumnInResultSet(pColumn,true);
2936//                }else if ((lcResult) && (lcTable.getTableType() == ETableSource.unnest)&&(lcTable.getUnnestClause() != null)){
2937//                    pColumn.getTableToken().setDbObjectType(EDbObjectType.subquery_alias);
2938//                    lcTable.getSubquery().searchColumnInResultSet(pColumn,true);
2939                }
2940                if (lcResult&&pColumn.toString().endsWith("*")){
2941                    pColumn.getSourceTableList().add(lcTable);
2942//                    ArrayList<String> lcColumns = getColumnsInTable(lcTable);
2943//                    if (lcColumns != null){
2944//                        pColumn.getColumnsLinkedToStarColumn().addAll(lcColumns);
2945//                    }
2946                }
2947            }else {
2948              // column not qualified
2949
2950                // check if this is the column alias in current select list.
2951                if((!lcResult)&& ((!pColumn.isQualified()) && (this instanceof TSelectSqlStatement)&&(getResultColumnList() !=null))
2952                        && (TBaseType.isSupportLateralColumn(dbvendor))
2953                ){
2954                    for(int j=0;j<getResultColumnList().size();j++){
2955                        TResultColumn lcField = getResultColumnList().getResultColumn(j);
2956                        lcResult = lcField.isMatchedUsingAlias(pColumn);
2957                        if (
2958                                 (!TSQLEnv.isAliasReferenceForbidden.get(this.dbvendor))&&
2959                                ((lcResult)&&(pColumn.getStartToken().posinlist > lcField.getAliasClause().getStartToken().posinlist))){
2960                            pColumn.setSourceColumn(lcField);
2961                            lcField.getTargetColumns().addObjectName(pColumn);
2962                            pColumn.setValidate_column_status(TBaseType.COLUMN_LINKED_TO_COLUMN_ALIAS_IN_OLD_ALGORITHM);
2963                            break;
2964                        }else{
2965                            lcResult = false;
2966                        }
2967                    }
2968
2969                    if (lcResult) return true;
2970                }
2971
2972                if (pColumn.getColumnNameOnly().equalsIgnoreCase("*")){
2973                    lcResult = true;
2974                    isContinue = true; // in order to match next table in the from clause
2975                    pColumn.getSourceTableList().add(lcTable);
2976//                    ArrayList<String> lcColumns = getColumnsInTable(lcTable);
2977//                    if (lcColumns != null){
2978//                        pColumn.getColumnsLinkedToStarColumn().addAll(lcColumns);
2979//                    }
2980                }else if (lcTable.isBaseTable()){
2981                    lcResult = fireOnMetaDatabaseTableColumn(
2982                                         lcTable.getPrefixServer()
2983                                        ,lcTable.getPrefixDatabase()
2984                                        ,lcTable.getPrefixSchema()
2985                                        ,lcTable.getName()
2986                                        ,pColumn.getColumnNameOnly());
2987                    if (! lcResult) {
2988                        candidateTableCnt++;
2989                        pColumn.getCandidateTables().addTable(lcTable);
2990                    }else{
2991                        foundInMetaData = true;
2992                        isContinue = false;
2993                    }
2994
2995                }else if ((lcTable.getTableType() == ETableSource.subquery)
2996                            ||((lcTable.getTableType() == ETableSource.openquery)&&(lcTable.getSubquery() != null))){
2997
2998                    lcResult = lcTable.searchColumnInAlias(pColumn)>=0;
2999                    if (!lcResult){
3000                        lcResult = lcTable.getSubquery().searchColumnInResultSet(pColumn,(tables.size() == 1)
3001                                &&(pColumn.getCandidateTables().size() == 0));
3002                        if (! lcResult) {
3003                            candidateTableCnt++;
3004                            pColumn.getCandidateTables().addTable(lcTable);
3005                        }
3006                    }
3007
3008
3009//                    if (lcTable.isIncludeColumnAlias()){
3010//                       // System.out.println("subquery with alias:"+lcTable.getAliasClause().toString()+", skip search:"+pColumn.toString());
3011//
3012//                    }else{
3013//                        lcResult = lcTable.getSubquery().searchColumnInResultSet(pColumn,(tables.size() == 1)&&(pColumn.getCandidateTables().size() == 0));
3014//                        if (! lcResult) candidateTableCnt++;
3015//                    }
3016                }else  if (lcTable.isCTEName()){
3017                    lcResult = lcTable.getCTE().searchColumnInResultSet(this,lcTable,pColumn,tables.size() == 1);
3018                    if (! lcResult) {
3019                        candidateTableCnt++;
3020                        pColumn.getCandidateTables().addTable(lcTable);
3021                    }
3022                }else if (lcTable.getTableType() == ETableSource.function){
3023                    //  search in this table function
3024                        if(tables.size() == 1){
3025                                lcResult = ( lcTable.getFuncCall().isColumnInThisTableFunction(this.getSqlEnv(),this.dbvendor,pColumn)
3026                                != TBaseType.COLUMN_IN_TABEL_FUNCTION_NO);
3027                        }
3028                        else{
3029                                lcResult = ( lcTable.getFuncCall().isColumnInThisTableFunction(this.getSqlEnv(),this.dbvendor,pColumn)
3030                                                                        == TBaseType.COLUMN_IN_TABEL_FUNCTION_YES);
3031                        }
3032                }else if (lcTable.getTableType() == ETableSource.tableExpr 
3033                                && lcTable.getTableExpr().getExpressionType() == EExpressionType.function_t
3034                                && lcTable.getTableExpr().getFunctionCall() != null){
3035                    //  search in this table function
3036                    lcResult = ( lcTable.getTableExpr().getFunctionCall().isColumnInThisTableFunction(this.getSqlEnv(),this.dbvendor,pColumn)
3037                                                                        == TBaseType.COLUMN_IN_TABEL_FUNCTION_YES);
3038                }else if (lcTable.getTableType() == ETableSource.pivoted_table){
3039                    lcResult = fireOnMetaDatabaseTableColumn(
3040                            lcTable.getPrefixServer()
3041                            ,lcTable.getPrefixDatabase()
3042                            ,lcTable.getPrefixSchema()
3043                            ,lcTable.getName()
3044                            ,pColumn.getColumnNameOnly());
3045                    if (lcResult){
3046                            foundInMetaData = true;
3047                            isContinue = false;
3048                    }
3049                }else if (lcTable.getTableType() == ETableSource.unnest){
3050                    for(TObjectName objectName:lcTable.getLinkedColumns()){
3051                        if (objectName.toString().equalsIgnoreCase(pColumn.toString())){
3052                            lcResult = true;
3053                            break;
3054                        }
3055                    }
3056
3057                    if (!lcResult){
3058                        if (lcTable.getAliasClause() == null){
3059                            // this unnest() clause generate column with default name: "value"
3060                            if (pColumn.toString().equalsIgnoreCase("value")){
3061                                lcResult = true;
3062                            }
3063                        }else{
3064                        }
3065                    }
3066                }//unnest
3067            }
3068
3069            if (lcResult) {
3070                lcTable.getLinkedColumns().addObjectName(pColumn);
3071                pColumn.setSourceTable(lcTable);
3072               // pColumn.setValidate_column_status(TBaseType.COLUMN_LINKED_TO_TABLE_IN_OLD_ALGORITHM);
3073                if (!isContinue) break;
3074            }
3075        }
3076
3077        if ((lcResult) && (foundInMetaData)) return true;
3078
3079        // check variable after metadata checking
3080        if (locateVariableOrParameter(pColumn)) return false;
3081
3082        // check if this is the column alias in current select list.
3083//        if((!lcResult)&& ((!pColumn.isPrefixed()) && (this instanceof TSelectSqlStatement)&&(getResultColumnList() !=null))){
3084//            for(int j=0;j<getResultColumnList().size();j++){
3085//                TResultColumn lcField = getResultColumnList().getResultColumn(j);
3086//                lcResult = lcField.isMatchedUsingAlias(pColumn);
3087//                if ((lcResult)&&(pColumn.getStartToken().posinlist > lcField.getAliasClause().getStartToken().posinlist)){
3088//                    pColumn.setSourceColumn(lcField);
3089//                    lcField.getTargetColumns().addObjectName(pColumn);
3090//                    break;
3091//                }else{
3092//                    lcResult = false;
3093//                }
3094//            }
3095//        }
3096
3097        if (lcResult) return true;
3098
3099        boolean isSearchUpLevel = (this.parentStmt != null);
3100
3101        if ((isSearchUpLevel) && (sqlstatementtype == ESqlStatementType.sstselect)){
3102            isSearchUpLevel = (pColumn.isQualified()
3103                                || (
3104//                                        (((TSelectSqlStatement)(this)).getLocation() != ESqlClause.elTable) &&
3105                                         (! ((TSelectSqlStatement)(this)).isQueryOfCTE())
3106                                    )
3107                               )
3108                            && (parentStmt.sqlstatementtype != ESqlStatementType.sstinsert)
3109                            && (!((pColumn.getLocation() == ESqlClause.selectList)&&(((TSelectSqlStatement)(this)).getLocation() == ESqlClause.join)))
3110                            && ((((TSelectSqlStatement)(this)).getLocation() != ESqlClause.pivot_in))
3111                            && (!((parentStmt.sqlstatementtype == ESqlStatementType.sstcreatetable)))
3112                            && (!((parentStmt.sqlstatementtype == ESqlStatementType.sstcreateview)))
3113//                            && (!((pColumn.getLocation() == ESqlClause.selectList)&&(parentStmt.sqlstatementtype == ESqlStatementType.sstcreatetable)))
3114//                            && (!((pColumn.getLocation() == ESqlClause.selectList)&&(parentStmt.sqlstatementtype == ESqlStatementType.sstcreateview)))
3115                            && (! ((pColumn.getLocation() == ESqlClause.selectList)
3116                                    &&(candidateTableCnt == 1) && (this instanceof TSelectSqlStatement)
3117                                    && (((TSelectSqlStatement)(this)).getLocation() == ESqlClause.elTable)
3118                                    ) ) // ref:mantis: #2628
3119                           // && ( ((TSelectSqlStatement)(this.parentStmt)).getSetOperatorType() == ESetOperatorType.none)
3120            ;
3121
3122            if (isSearchUpLevel){
3123                isSearchUpLevel = !((!pColumn.isQualified())&&(((TSelectSqlStatement) this).getLocation() == ESqlClause.where));
3124            }
3125        }
3126
3127        if (isSearchUpLevel&&(pColumn.isContinueToSearch())){ // only search one level up, c:\prg\gsp_sqlfiles\TestCases\java\oracle\dbobject\berger_sqltest_04.sql
3128            boolean increaseLevel = true;
3129            if (parentStmt instanceof TSelectSqlStatement){
3130                if( ((TSelectSqlStatement)parentStmt).getSetOperatorType() != ESetOperatorType.none){
3131                   increaseLevel = false;
3132                }
3133            }
3134            if (increaseLevel){
3135                pColumn.searchLevel++;
3136            }
3137
3138            lcResult = parentStmt.linkColumnToTable(pColumn,pLocation);
3139
3140            if (increaseLevel){
3141                pColumn.searchLevel--;
3142            }
3143        }
3144
3145        if ((! lcResult) && (pColumn.searchLevel == 0)) {
3146            if (this.sqlstatementtype == ESqlStatementType.sstselect){
3147                if( ((TSelectSqlStatement)this).getSetOperatorType() == ESetOperatorType.none){
3148                    //                    USING _spVV0 (INTEGER)
3149                    //                            INSERT INTO table3
3150                    //                    SELECT :_spVV0,x. *,m.col3
3151                    //                    from ((           select table1.col1, (table1.col1 + table5.col2) c from table1
3152                    //                            union all select col3,col4 from table2) x
3153                    //                    cross join (select id from table2) m )
3154
3155                    // table5 in the above sql only link to the nearest level sql, but not to up-level which is union all
3156
3157                    linkToFirstTable(pColumn,candidateTableCnt);
3158                }
3159            }else{
3160                linkToFirstTable(pColumn,candidateTableCnt);
3161            }
3162        }
3163
3164        return lcResult;
3165    }
3166
3167
3168    /**
3169     *
3170     * @deprecated As of v1.6.0.1, use  {@link #linkColumnToTable} instead
3171     */
3172    public void linkColumnReferenceToTable(TObjectName cr, ESqlClause plocation){
3173        // this is the column name, link it to table
3174        if (cr == null) return;
3175        cr.setLocation(plocation);
3176        if (cr.getObjectType() == TObjectName.ttobjVariable) return;
3177        if (cr.getObjectType() == TObjectName.ttobjColumnAlias) return;
3178        if (this.dbvendor == EDbVendor.dbvsybase){
3179            TSourceToken pt = cr.getPartToken();
3180            if ( pt != null){
3181                if (pt.tokentype == ETokenType.ttdqstring){
3182                //"0123", quoted string start with a number can't a column
3183                    if ((pt.toString().charAt(1) >= '0')
3184                        &&(pt.toString().charAt(1) <= '9')){
3185                        return;
3186                    }else if (pt.toString().length() == 2){
3187                        //"", empty
3188                        return;
3189                    }else if (pt.toString().substring(1,pt.toString().length()-1).trim().length() == 0){
3190                        //"  "
3191                        return;
3192                    }
3193                }
3194            }
3195        }
3196
3197
3198        if (cr.getPartToken() != null){
3199            if (cr.getPartToken().tokentype == ETokenType.ttkeyword){
3200                boolean reservedKeyword = false;
3201                switch (dbvendor){
3202                    case dbvmssql:
3203                        //reservedKeyword = ! this.getGsqlparser().getFlexer().canBeColumnName(cr.getPartToken().tokencode);
3204                        reservedKeyword = ! TLexerMssql.canBeColumnName(cr.getPartToken().tokencode);
3205                        break;
3206                    case dbvsybase:
3207                        reservedKeyword = keywordChecker.isKeyword(cr.getPartToken().toString(), EDbVendor.dbvsybase, "15.7", true);
3208                        break;
3209                    default:
3210                        break;
3211                }
3212                if (reservedKeyword) return;
3213            }
3214        }
3215
3216        // let's check is this columnreference is variable or parameter of plsql function/procedure
3217      //  if (locateVariableOrParameter(cr)) return;
3218
3219//        if ((cr.getPartToken() != null)&&((dbvendor == EDbVendor.dbvmssql)||(dbvendor == EDbVendor.dbvsybase))){
3220//            if ((cr.getPartToken().tokentype == ETokenType.ttkeyword)&&(!(this.getGsqlparser().getFlexer().canBeColumnName(cr.getPartToken().tokencode)))){
3221//                // keyword can't be column name:
3222//                //select * From dbo.table Where DATEDIFF(day, create_date, expiry_date) < 14
3223//               return;
3224//            }
3225//        }
3226
3227        if ((cr.toString().startsWith("@")))
3228//            if ((cr.toString().endsWith("*"))||(cr.toString().startsWith("@")))
3229        {
3230            cr.setObjectType(TObjectName.ttobjNotAObject);
3231            return;
3232        }
3233
3234        if (dbvendor == EDbVendor.dbvoracle){
3235            if ( //(cr.toString().compareToIgnoreCase ("rowid") == 0)||
3236                    (cr.toString().compareToIgnoreCase ("sysdate") == 0)
3237            || (cr.toString().compareToIgnoreCase ("nextval") == 0)
3238            || (cr.toString().compareToIgnoreCase ("rownum") == 0)
3239            || (cr.toString().compareToIgnoreCase ("level") == 0)
3240                    ){
3241                cr.setObjectType(TObjectName.ttobjNotAObject);
3242                if (cr.getDbObjectType() == EDbObjectType.unknown){
3243                    cr.setDbObjectType(EDbObjectType.notAColumn);
3244                }
3245                return;
3246            }
3247        }
3248
3249        if (((cr.toString().toUpperCase().startsWith("INSERTED"))||(cr.toString().toUpperCase().startsWith("DELETED")))&&(plocation == ESqlClause.output)&&(targetTable != null)){
3250            targetTable.getObjectNameReferences().addObjectName(cr);
3251            return;
3252        }
3253
3254        if ( ((cr.toString().toUpperCase().startsWith(":NEW"))
3255              ||(cr.toString().toUpperCase().startsWith(":OLD")))
3256             &&(this.getTopStatement() instanceof TPlsqlCreateTrigger)
3257             &&(dbvendor == EDbVendor.dbvoracle)){
3258             this.getTopStatement().tables.getTable(0).getObjectNameReferences().addObjectName(cr);
3259            return;
3260        }
3261
3262
3263
3264        int ret = this.tables.checkColumnReferenceInTables(cr);
3265         if (ret >= 0) {
3266             TTable lcTable = this.tables.getTable(ret);
3267             if (lcTable.isBaseTable()){
3268                lcTable.getObjectNameReferences().addObjectName(cr);
3269             }else if (lcTable.isCTEName()){
3270                //WITH temp
3271                //     AS (SELECT *
3272                //         FROM   sysibm.systables),
3273                //     temp1
3274                //     AS (SELECT *
3275                //         FROM   sysibm.syscolumns)
3276                //SELECT *
3277                //FROM   temp A
3278                //       INNER JOIN temp1 B
3279                //               ON A.creator = B.tbcreator
3280                //                  AND A.name = B.tbname
3281                 TCTE lccte = findCTEByName(lcTable.toString());
3282                 if (lccte != null){
3283                     TObjectName objectName = new TObjectName();
3284                     objectName.init(cr.getPartToken());
3285                     if (lccte.getSubquery() != null){
3286                         lccte.getSubquery().linkColumnReferenceToTable(objectName,plocation);
3287                     }
3288                 }
3289             }else if (lcTable.getTableType() == ETableSource.subquery){
3290                // link s2t1a1 to  subselect2table1 via s2
3291                //select
3292                //       s2.s2t1a1
3293                //from
3294                //    (
3295                //       select s2t1.*
3296                //          from subselect2table1 s2t1
3297                //    ) s2
3298               TSelectSqlStatement subquery = lcTable.getSubquery();
3299
3300                 if(((subquery.getValueClause() == null))&&(!subquery.isCombinedQuery())&&(subquery.getResultColumnList() != null)&&(subquery.getResultColumnList().size() == 1)){
3301                     TResultColumn lcColumn = subquery.getResultColumnList().getResultColumn(0);
3302                     if (lcColumn.toString().endsWith("*")){
3303                        boolean isfound = false;
3304
3305                        for(int i=0;i<subquery.tables.size();i++){
3306                            if (subquery.tables.getTable(i).getTableType() == ETableSource.subquery) continue;
3307                            String columnStr = null;
3308                            if (cr.getPartToken() != null){
3309                                //cr.getObjectType() is not ttObjColumn, so we can't use
3310                                // getColumnToken, this is a bug, need to check it later.
3311                                columnStr = cr.getPartToken().toString();
3312                            }
3313                            if (this.fireOnMetaDatabaseTableColumn(
3314                                    subquery.tables.getTable(i).getTableName().getServerString(),
3315                                    subquery.tables.getTable(i).getTableName().getDatabaseString(),
3316                                    subquery.tables.getTable(i).getTableName().getSchemaString(),
3317                                    subquery.tables.getTable(i).getName(),columnStr)){
3318                                subquery.tables.getTable(i).getObjectNameReferences().addObjectName(cr);
3319                                isfound = true;
3320                                break;
3321                            }
3322                        }
3323
3324
3325
3326                         if (!isfound)
3327                         {
3328                             if(subquery.tables.size() > 1){
3329                                 cr.setTableDetermined(false);
3330                             }
3331                           for(int i=0;i<subquery.tables.size();i++){
3332                             subquery.tables.getTable(i).getObjectNameReferences().addObjectName(cr);
3333                           }
3334                         }
3335
3336                     } // "*"
3337                 }
3338             }
3339         }else if (ret == -2){
3340           // no qualifier before column, check is this column of a cte, if not,set it to non-cte table
3341           boolean isfound = false;
3342             for (int i=0;i<this.tables.size();i++){
3343                 if ((this.tables.getTable(i).isCTEName()) &&(this.tables.getTable(i).getCteColomnReferences() != null)){
3344                     if (this.tables.getTable(i).getCteColomnReferences().searchColumnReference(cr) >= 0){
3345                        this.tables.getTable(i).getObjectNameReferences().addObjectName(cr);
3346                         isfound = true;
3347                         break;
3348                     }
3349                 }
3350             }
3351
3352           // no qualifier before column, but we still need to check uplevel table like this:
3353            //SELECT
3354            //       col1 ,
3355            //
3356            //           (    SELECT col2
3357            //                FROM tab1
3358            //                WHERE col2 = col1      )
3359            //   FROM tab2
3360           // we need to link col1 to tab2 in up level, but not to tab1
3361            if ((!isfound) &&(
3362                            (cr.getLocation() != ESqlClause.resultColumn)
3363                          &&(cr.getLocation() != ESqlClause.insertColumn)
3364                                    &&(cr.getLocation() != ESqlClause.mergeInsert)
3365                                    &&(cr.getLocation() != ESqlClause.selectList)
3366            ) ){  // code #111
3367                TCustomSqlStatement lcParent = null;
3368                lcParent = this.getParentStmt();
3369               while ( lcParent != null) {
3370                   TTable lcTable;
3371                 //ret = lcParent.tables.checkColumnReferenceInTables(cr);
3372                   if (lcParent.sqlstatementtype != ESqlStatementType.sstselect) {
3373                       break;
3374                   }
3375                   for (int i=0;i<lcParent.tables.size();i++){
3376                       lcTable = lcParent.tables.getTable(i);
3377                       if (lcTable.getTableType() == ETableSource.objectname) {
3378                           for(int k = 0; k< lcTable.getObjectNameReferences().size();k++){
3379                               if (lcTable.getObjectNameReferences().getObjectName(k).isTableDetermined()){
3380                                   if (cr.toString().equalsIgnoreCase(lcTable.getObjectNameReferences().getObjectName(k).toString())){
3381                                      isfound = true;
3382                                       break;
3383                                   }
3384                               }
3385                           }
3386                       if (isfound) break;
3387                       }
3388                   }
3389
3390                 if (isfound){
3391                     break;
3392                 }else{
3393                     lcParent = lcParent.getParentStmt();
3394                 }
3395               } // while
3396
3397             } // end of code #111
3398
3399            if (!isfound){
3400                isfound = checkNonQualifiedColumnReferenceInSubQueryOfUplevelStmt(cr
3401                        , ((plocation == ESqlClause.resultColumn)
3402                            ||(plocation == ESqlClause.insertColumn)
3403                                ||(plocation == ESqlClause.mergeInsert)
3404                                ||(plocation == ESqlClause.selectList)
3405                        )
3406                );
3407            }
3408
3409             if ((!isfound)&&(this.tables.size() > 0)){
3410                 int candidate = 0, firstCandidate = -1;
3411                 // add this column reference to first non-cte( or cte with column list is null) and non-subquery table
3412                 for (int i=0;i<this.tables.size();i++){
3413                     // 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
3414                     //if (this.tables.getTable(i).aliasClause != null) continue;
3415                     if (
3416                             (
3417                                     (!this.tables.getTable(i).isCTEName())
3418                                ||((this.tables.getTable(i).isCTEName())&&(this.tables.getTable(i).getCteColomnReferences() == null))
3419                             )&&((this.tables.getTable(i).getTableType() != ETableSource.subquery))
3420                     )
3421                     {
3422                         candidate++;
3423                         if (firstCandidate == -1) firstCandidate = i;
3424                         if (this.fireOnMetaDatabaseTableColumn(
3425                                    this.tables.getTable(i).getTableName().getServerString(),
3426                                    this.tables.getTable(i).getTableName().getDatabaseString(),
3427                                    this.tables.getTable(i).getTableName().getSchemaString(),
3428                                    this.tables.getTable(i).getName(),cr.toString())){
3429                                this.tables.getTable(i).getObjectNameReferences().addObjectName(cr);
3430                                isfound = true;
3431                                break;
3432                            }
3433                         else{
3434                             this.tables.getTable(i).getObjectNameReferences().addObjectName(cr);
3435                             if (this.tables.size() > 1){
3436                                 cr.setTableDetermined(false);
3437                             }
3438                             isfound = true;
3439                             break;
3440                         }
3441                     }
3442                  }
3443                 if ((!isfound) && (candidate == 1)){
3444                     this.tables.getTable(firstCandidate).getObjectNameReferences().addObjectName(cr);
3445                 }
3446             }
3447         }else if (ret == -1){
3448             TCustomSqlStatement lcParent = null;
3449             lcParent = this.getParentStmt();
3450            while ( lcParent != null) {
3451              ret = lcParent.tables.checkColumnReferenceInTables(cr);
3452              if (ret >= 0){
3453               lcParent.tables.getTable(ret).getObjectNameReferences().addObjectName(cr);
3454               break;
3455              }else{
3456                  lcParent = lcParent.getParentStmt();
3457              }
3458            } // while
3459         } //-1
3460
3461    }
3462
3463    /**
3464     * Found out is a non qualified column is a column in uplevel subquery table like this:
3465     * take ma_parkey for example: ma_parkey is not a physical column
3466     * 
3467        SELECT c_mandant
3468             , CASE WHEN EXISTS (SELECT 1
3469                                   FROM CDS_H_GRUPPE  GRP1
3470                                  WHERE GRP1.c_mandant = c_mandant
3471                                    AND GRP1.parkey1       = ma_parkey)
3472                      THEN 1
3473                  ELSE NULL
3474               END MA_ME
3475          FROM (SELECT c_mandant
3476                     , CASE WHEN funktionscode = 'U'
3477                              THEN parkey1
3478                          ELSE parkey2
3479                       END MA_PARKEY
3480                  FROM
3481                       CDS_H_GRUPPE
3482               )
3483     */
3484    public boolean checkNonQualifiedColumnReferenceInSubQueryOfUplevelStmt(TObjectName crf,boolean sameLevelOnly){
3485        boolean ret = false;
3486
3487        TCustomSqlStatement lcParent = null;
3488        lcParent = this;//getParentStmt();
3489       while ( lcParent != null) {
3490           TTable lcTable;
3491             for (int i=0;i<lcParent.tables.size();i++){
3492                 lcTable = lcParent.tables.getTable(i);
3493
3494                 if ((lcTable.getTableType() != ETableSource.subquery)) {continue;}
3495
3496                 ret = isColumnNameInSelectList(crf.toString(),lcTable.subquery);
3497                if (ret) {break;}
3498
3499             }
3500          if (ret) {break;}
3501           else{
3502              if (sameLevelOnly){
3503                  lcParent = null;
3504              }else{
3505                lcParent = lcParent.getParentStmt();
3506              }
3507          }
3508       } // while
3509
3510        return ret;
3511    }
3512
3513    private boolean isColumnNameInSelectList(String pColumn, TSelectSqlStatement pSelect){
3514        boolean ret = false;
3515        TResultColumn lcColumn;
3516        if (pSelect.isCombinedQuery()){
3517            ret = isColumnNameInSelectList(pColumn,pSelect.getLeftStmt());
3518            if (!ret){
3519                ret = isColumnNameInSelectList(pColumn,pSelect.getRightStmt());
3520            }
3521        }else{
3522            if (pSelect.getResultColumnList() != null){ //if it's a db2 value row, then pSelect.getResultColumnList() will be null 
3523                for(int j=0;j<pSelect.getResultColumnList().size();j++){
3524                   lcColumn = pSelect.getResultColumnList().getResultColumn(j);
3525                   if (lcColumn.getAliasClause() != null){
3526                       ret = pColumn.equalsIgnoreCase(lcColumn.getAliasClause().toString());
3527                   }
3528                   if (ret)  break;
3529                   ret = pColumn.equalsIgnoreCase(lcColumn.getExpr().toString());
3530                   if (ret)  break;
3531                }
3532            }
3533        }
3534        return ret;
3535    }
3536
3537    public TCustomSqlStatement getTopStatement(){
3538        TCustomSqlStatement ret = this;
3539        while (ret.getParentStmt() != null){
3540            ret = ret.getParentStmt();
3541        }
3542        return ret;
3543    }
3544
3545
3546//    public String toScript(){
3547//        if (!isChanged()){
3548//            return this.toString();
3549//        }
3550//        return super.toScript();
3551//    }
3552
3553}
3554
3555class constantVisitor extends TParseTreeVisitor {
3556    private boolean inWhere = false,inExprList = false;
3557    public void preVisit(TWhereClause node){
3558        inWhere = true;
3559    }
3560
3561    public void postVisit(TWhereClause node){
3562        inWhere = false;
3563    }
3564
3565    public void preVisit(TExpression node){
3566        if (inWhere){
3567            switch (node.getExpressionType()){
3568                case list_t:
3569                    inExprList = true;
3570                    boolean isNumber = true;
3571                    if (node.getExprList().size() > 0){
3572                        // check the type of the constant in the expr list
3573                        TExpression expr = node.getExprList().getExpression(0);
3574                        if (expr.getExpressionType() == EExpressionType.simple_constant_t){
3575                            if (expr.getConstantOperand().getLiteralType() == ELiteralType.etString){
3576                                isNumber = false;
3577                            }
3578                        }
3579                    }
3580
3581                    TSourceToken lcStartToken = node.getStartToken();
3582                    TSourceToken lcEndToken = node.getEndToken();
3583                    int tokenPos = 0;
3584                    if ((lcEndToken != null) && (lcStartToken != null)){
3585                        TSourceToken lcCurrentToken = lcStartToken;
3586                        while (lcCurrentToken != null){
3587
3588                            if (lcCurrentToken.equals(lcEndToken)){
3589                                break;
3590                            }else{
3591
3592                                if (tokenPos == 1){
3593                                    if (isNumber){
3594                                        lcCurrentToken.setTextWithBackup("999");
3595                                    }else{
3596                                        lcCurrentToken.setTextWithBackup("'placeholder_str'");
3597                                    }
3598                                }else if (tokenPos > 1){
3599                                    lcCurrentToken.tokenstatus = ETokenStatus.tsdeleted;
3600                                }
3601
3602                                lcCurrentToken = lcCurrentToken.getNextTokenInChain();
3603                                tokenPos++;
3604                            }
3605                        }
3606                    }
3607
3608                    break;
3609            }
3610        } // where
3611    }
3612
3613    public void postVisit(TExpression node){
3614        if (inWhere){
3615            switch (node.getExpressionType()){
3616                case list_t:
3617                    inExprList = false;
3618                    break;
3619            }
3620        }
3621    }
3622
3623    public void preVisit(TConstant node){
3624        if (inWhere&&(!inExprList)){
3625            switch (node.getLiteralType()){
3626                case etNumber:
3627                case etFloat:
3628                    node.getStartToken().setTextWithBackup("999");
3629                    break;
3630                case etString:
3631                    node.getStartToken().setTextWithBackup("'placeholder_str'");
3632                    break;
3633            }
3634        }
3635    }
3636
3637    public void preVisit(TFunctionCall node){
3638        if (TBaseType.as_canonical_f_decrypt_replace_password){
3639            int i = TBaseType.searchCryptFunction(node.getFunctionName().toString());
3640
3641            if (i>0){ // find this function
3642                if (node.getArgs().size() >= i){
3643                    TExpression secondArg = node.getArgs().getExpression(i-1);
3644                    if (secondArg.getExpressionType() == EExpressionType.simple_constant_t){
3645                        TConstant constant = secondArg.getConstantOperand();
3646                        constant.getValueToken().setTextWithBackup("'***'");
3647                        //System.out.println(node.toString()+":"+constant.toString());
3648                    }else if (secondArg.getExpressionType() == EExpressionType.simple_object_name_t){
3649                        TObjectName objectName = secondArg.getObjectOperand();
3650                        objectName.getStartToken().setTextWithBackup("'***'");
3651                        //System.out.println(node.toString()+":"+constant.toString());
3652                    }
3653                }
3654            }
3655
3656        }
3657    }
3658
3659    void processConstant(TConstant node){
3660        switch (node.getLiteralType()){
3661            case etNumber:
3662            case etFloat:
3663                node.getStartToken().setTextWithBackup("999");
3664                break;
3665            case etString:
3666                node.getStartToken().setTextWithBackup("'placeholder_str'");
3667                break;
3668        }
3669    }
3670
3671}