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                ;
1166    }
1167
1168    public boolean isdatabricksplsql(){
1169        return (this instanceof TCommonBlock)
1170                ;
1171    }
1172
1173    public boolean isgreeplumplsql(){
1174        return (this instanceof TCommonBlock)
1175                ||(this.sqlstatementtype == ESqlStatementType.sstcreateprocedure)
1176                ||(this.sqlstatementtype == ESqlStatementType.sstcreatefunction)
1177                ||(this.sqlstatementtype == ESqlStatementType.sstDoExecuteBlock)
1178                ;
1179    }
1180
1181    public boolean isathenaplsql(){
1182        return (this instanceof TCommonBlock)
1183                ;
1184    }
1185
1186    public boolean isprestoplsql(){
1187        return (this instanceof TCommonBlock)
1188                ;
1189    }
1190
1191    public boolean issnowflakeplsql(){
1192        return ((this instanceof TCommonBlock)
1193                ||(this.sqlstatementtype == ESqlStatementType.sstcreateprocedure)
1194        );
1195    }
1196
1197    public boolean isBigQueryplsql(){
1198        return ((this instanceof TCommonBlock)
1199                ||(this.sqlstatementtype == ESqlStatementType.sstcreateprocedure)
1200        );
1201    }
1202
1203    public boolean isverticaplsql(){
1204        return  (
1205                        (this.sqlstatementtype == ESqlStatementType.sstcreatefunction)
1206        );
1207    }
1208
1209    public boolean isoracleplsql(){
1210        return  (
1211                (this.sqlstatementtype == ESqlStatementType.sst_plsql_block)
1212                ||(this.sqlstatementtype == ESqlStatementType.sstplsql_createprocedure)
1213                        ||(this.sqlstatementtype == ESqlStatementType.sstplsql_createfunction)
1214                        ||(this.sqlstatementtype == ESqlStatementType.sstplsql_createpackage)
1215                        ||(this.sqlstatementtype == ESqlStatementType.sstplsql_createtype_placeholder)
1216                        ||(this.sqlstatementtype == ESqlStatementType.sstoraclecreatepackagebody)
1217                        ||(this.sqlstatementtype == ESqlStatementType.sstplsql_createtrigger)
1218                        ||(this.sqlstatementtype == ESqlStatementType.sstplsql_createtypebody)
1219                        ||(this.sqlstatementtype == ESqlStatementType.sstplsql_tabletypedef)
1220                        ||(this.sqlstatementtype == ESqlStatementType.sstplsql_varraytypedef)
1221                        ||(this.sqlstatementtype == ESqlStatementType.sstplsql_createprocedure)
1222                        ||(this.sqlstatementtype == ESqlStatementType.sstplsql_execimmestmt)
1223                        ||(this.sqlstatementtype == ESqlStatementType.sstoraclecreatelibrary)
1224                );
1225    }
1226
1227    int checksyntax(TCustomSqlStatement psql){
1228        return dochecksyntax(psql);
1229    }
1230
1231    protected int dochecksyntax(TCustomSqlStatement psql){
1232        int ret = -1;
1233        clear();
1234        if (sourcetokenlist.size() == 0) return ret;
1235
1236//        TCustomParser lcparser;
1237//        lcparser = new TLzParserOracleSql(sourcetokenlist);
1238//        lcparser.sql = this;
1239//        ret = lcparser.yyparse();
1240
1241
1242        if ((this.dbvendor == EDbVendor.dbvoracle)&&(this.isoracleplsql()&&(plsqlparser!=null))
1243//                || ((this.dbvendor == EDbVendor.dbvgaussdb) // gaussdb 中用 oracle plsql 写的存储过程,用 oracle plsql parser 来解析
1244//                        &&(
1245//                            ((this instanceof TCreateFunctionStmt)&&(((TCreateFunctionStmt)this).isGaussDBSpInOracle()))
1246//                            ||((this instanceof TCreateProcedureStmt)&&(((TCreateProcedureStmt)this).isGaussDBSpInOracle()))
1247//                            ||(this instanceof TPlsqlCreatePackage)
1248//                          )
1249//                )
1250        ){
1251            plsqlparser.sql = this;
1252//            if (this.dbvendor == EDbVendor.dbvgaussdb){
1253//                // 原来用 gaussDB lexer tokenize 的 token 需要用 Oracle lexer 重新 tokenize 一边
1254//                String sqlText="";
1255//                for(int k = 0;k<sourcetokenlist.size();k++){
1256//                    sqlText = sqlText + sourcetokenlist.get(k).toString();
1257//                }
1258//                // TODO, need to use singleton pattern to get a single instance of Oracle parser.
1259//                TGSqlParser sqlParser = new TGSqlParser(EDbVendor.dbvoracle);
1260//                // keep coordinate of the origin query
1261//                long originalLineNo = sourcetokenlist.get(0).lineNo;
1262//                long originalColumnNo = sourcetokenlist.get(0).columnNo;
1263//                sqlParser.sqltext = TBaseType.stringBlock((int) originalLineNo - 1,(int) originalColumnNo - 1)+ sqlText;;
1264//
1265//                int r = sqlParser.getrawsqlstatements();
1266//                sourcetokenlist.clear();
1267//                for(int k=0;k<sqlParser.sourcetokenlist.size();k++){
1268//                    sourcetokenlist.add(sqlParser.sourcetokenlist.get(k));
1269//                }
1270//            }
1271
1272            plsqlparser.sourcetokenlist = sourcetokenlist;
1273
1274            if ((this instanceof TCommonStoredProcedureSqlStatement)
1275                &&((TCommonStoredProcedureSqlStatement)this).isWrapped()){
1276                // don't parse wrapped oracle plsql
1277                ret = 0;
1278                this.rootNode = this;
1279            }else {
1280                ret = plsqlparser.yyparse();
1281                this.rootNode = plsqlparser.rootNode;
1282            }
1283        }
1284        else{
1285            if ((this.sqlstatementtype == ESqlStatementType.sstExplain)&&(dbvendor != EDbVendor.dbvhana)){
1286                    // EXPLAIN PLAN ... FOR statement; only parse token after FOR keyword
1287                    boolean isFoundStopToken = false;
1288
1289                    for(int k=0;k<sourcetokenlist.size();k++){
1290                        TSourceToken st = sourcetokenlist.get(k);
1291                        switch (dbvendor){
1292                            case dbvoracle:
1293                                if (st.tokencode == TBaseType.rrw_for) {
1294                                    st.tokencode = TBaseType.sqlpluscmd;
1295                                    isFoundStopToken = true;
1296                                }
1297                                break;
1298                            case dbvredshift:
1299                                if (st.tokencode == TBaseType.rrw_explain){
1300                                    st.tokencode = TBaseType.sqlpluscmd;
1301                                    TSourceToken nextst = st.nextSolidToken();
1302                                    if (nextst.tokencode == TBaseType.rrw_redshift_verbose){
1303                                        nextst.tokencode = TBaseType.sqlpluscmd;
1304                                        //System.out.println("Found verbose after explain");
1305                                    }
1306                                    isFoundStopToken = true;
1307                                }
1308                                break;
1309                            case dbvvertica:
1310                                if ((st.tokencode == TBaseType.rrw_select)
1311                                        ||(st.tokencode == TBaseType.rrw_insert)
1312                                        ||(st.tokencode == TBaseType.rrw_update)
1313                                        ||(st.tokencode == TBaseType.rrw_merge)
1314                                )
1315                                {
1316                                    isFoundStopToken = true;
1317                                }
1318                                break;
1319                            case dbvclickhouse:
1320                            case dbvmysql:
1321                            case dbvsparksql:
1322                            case dbvdatabricks:
1323                                if ((st.tokencode == TBaseType.rrw_select)
1324                                        ||(st.tokencode == TBaseType.rrw_insert)
1325                                        ||(st.tokencode == TBaseType.rrw_update)
1326                                        ||(st.tokencode == TBaseType.rrw_delete)
1327                                        ||(st.tokencode == TBaseType.rrw_replace)
1328                                        ||(st.tokencode == TBaseType.rrw_with)
1329                                        ||(st.tokencode == TBaseType.rrw_create)
1330                                        ||(st.tokencode == '(')
1331                                )
1332                                {
1333                                    isFoundStopToken = true;
1334                                }
1335                                break;
1336                            case dbvpostgresql:
1337                                if ((st.tokencode == TBaseType.rrw_select)
1338                                        ||(st.tokencode == TBaseType.rrw_insert)
1339                                        ||(st.tokencode == TBaseType.rrw_update)
1340                                        ||(st.tokencode == TBaseType.rrw_delete)
1341                                        ||(st.tokencode == TBaseType.rrw_replace)
1342                                        ||(st.tokencode == TBaseType.rrw_with)
1343                                        ||(st.tokencode == TBaseType.rrw_create)
1344                                        ||(st.tokencode == TBaseType.rrw_execute)
1345                                )
1346                                {
1347                                    isFoundStopToken = true;
1348                                }else if (st.tokencode == '('){
1349                                    // Check if this '(' starts EXPLAIN options like (COSTS FALSE)
1350                                    // or a subquery like (SELECT ...)
1351                                    TSourceToken nextSolid = sourcetokenlist.nextsolidtoken(k, 1, false);
1352                                    if (nextSolid != null
1353                                            && nextSolid.tokencode != TBaseType.rrw_select
1354                                            && nextSolid.tokencode != TBaseType.rrw_insert
1355                                            && nextSolid.tokencode != TBaseType.rrw_update
1356                                            && nextSolid.tokencode != TBaseType.rrw_delete
1357                                            && nextSolid.tokencode != TBaseType.rrw_with){
1358                                        // Options list: skip past closing ')'
1359                                        int depth = 1;
1360                                        st.tokencode = TBaseType.sqlpluscmd;
1361                                        for (k = k + 1; k < sourcetokenlist.size() && depth > 0; k++){
1362                                            TSourceToken inner = sourcetokenlist.get(k);
1363                                            if (inner.tokencode == '(') depth++;
1364                                            else if (inner.tokencode == ')') depth--;
1365                                            inner.tokencode = TBaseType.sqlpluscmd;
1366                                        }
1367                                        k--; // adjust for loop increment
1368                                    }else{
1369                                        isFoundStopToken = true;
1370                                    }
1371                                }
1372                                break;
1373                            case dbvflink:
1374                                // Flink EXPLAIN can have: EXPLAIN (options) stmt or EXPLAIN options stmt
1375                                // Don't stop at '(' because it might be part of EXPLAIN (ESTIMATED_COST, ...)
1376                                if ((st.tokencode == TBaseType.rrw_select)
1377                                        ||(st.tokencode == TBaseType.rrw_insert)
1378                                        ||(st.tokencode == TBaseType.rrw_update)
1379                                        ||(st.tokencode == TBaseType.rrw_delete)
1380                                        ||(st.tokencode == TBaseType.rrw_replace)
1381                                        ||(st.tokencode == TBaseType.rrw_with)
1382                                        ||(st.tokencode == TBaseType.rrw_create)
1383                                )
1384                                {
1385                                    isFoundStopToken = true;
1386                                }
1387                                break;
1388                            case dbvcouchbase:
1389                                if (st.tokencode == TBaseType.rrw_explain){
1390                                    st.tokencode = TBaseType.sqlpluscmd;
1391                                    isFoundStopToken = true;
1392                                }
1393                                break;
1394                            case dbvpresto:
1395                            case dbvathena:
1396                            case dbvnetezza:
1397                                if ((st.tokencode == TBaseType.rrw_select)
1398                                        ||(st.tokencode == TBaseType.rrw_insert)
1399                                        ||(st.tokencode == TBaseType.rrw_update)
1400                                        ||(st.tokencode == TBaseType.rrw_delete)
1401                                )
1402                                {
1403                                    isFoundStopToken = true;
1404                                }
1405                                break;
1406                            case dbvteradata:
1407                                if ((st.tokencode == TBaseType.rrw_select)
1408                                        ||(st.tokencode == TBaseType.rrw_insert)
1409                                        ||(st.tokencode == TBaseType.rrw_update)
1410                                        ||(st.tokencode == TBaseType.rrw_delete)
1411                                        ||(st.tokencode == TBaseType.rrw_teradata_collect)
1412                                )
1413                                {
1414                                    isFoundStopToken = true;
1415                                }
1416                                break;
1417                        }//switch
1418
1419                        if (isFoundStopToken) break;
1420                        st.tokencode = TBaseType.sqlpluscmd;
1421                    }
1422            }else if (this.sqlstatementtype == ESqlStatementType.sstProfile){
1423                for(int k=0;k<sourcetokenlist.size();k++) {
1424                    TSourceToken st = sourcetokenlist.get(k);
1425                    if (dbvendor == EDbVendor.dbvvertica){
1426                        if ((st.tokencode == TBaseType.rrw_select)
1427                                ||(st.tokencode == TBaseType.rrw_insert)
1428                                ||(st.tokencode == TBaseType.rrw_update)
1429                                ||(st.tokencode == TBaseType.rrw_merge)
1430                                )
1431                        {
1432                            break;
1433                        }
1434                    }
1435                    st.tokencode = TBaseType.sqlpluscmd;
1436                }
1437            }else if (this.sqlstatementtype == ESqlStatementType.sstprepare){
1438                if ((dbvendor == EDbVendor.dbvcouchbase)||(dbvendor == EDbVendor.dbvpresto)||(dbvendor == EDbVendor.dbvathena)){
1439                    int keywordCount = 0;
1440                    for(int k=0;k<sourcetokenlist.size();k++) {
1441                        TSourceToken st = sourcetokenlist.get(k);
1442                            if (st.tokencode == TBaseType.rrw_prepare)
1443                            {
1444                                keywordCount++;
1445                            }else if ((st.tokentype == ETokenType.ttkeyword)
1446                                    &&(st.tokencode != TBaseType.rrw_from)
1447                                    &&(st.tokencode != TBaseType.rrw_as)){
1448                                keywordCount++;
1449                                if (keywordCount > 1) break;
1450                            }
1451                        st.tokencode = TBaseType.sqlpluscmd;
1452                    }
1453                }
1454            }
1455
1456            if (parser == null){
1457                // statement such as select/insert and etc  inside plsql
1458                if (psql != null){
1459                    parser = psql.getTopStatement().parser;
1460                    this.setParentStmt(psql);
1461                }
1462                // parser =  new TParserOracleSql(null);
1463                //parser.lexer = new TLexerOracle();
1464                //parser.lexer.delimiterchar = '/';
1465            }
1466            parser.sql = this;
1467            parser.sourcetokenlist = sourcetokenlist;
1468            ret = parser.yyparse();
1469            this.rootNode = parser.rootNode;
1470        }
1471
1472        if (ret == 0){
1473            ret = syntaxErrors.size();
1474        }
1475       // if (rootNode == null) {
1476       if (rootNode == null) {
1477            // EXPLAIN FOR CONNECTION has no inner statement to parse - this is valid
1478            if (this.sqlstatementtype == ESqlStatementType.sstExplain && isExplainForConnection()) {
1479                ret = 0;
1480            } else {
1481                ret = TBaseType.MSG_ERROR_NO_ROOT_NODE;
1482                // todo , uncomment next line when all sql statements in .y file was processed
1483                 parseerrormessagehandle( new TSyntaxError("no root node",0,0,"no_root_node",EErrorType.sperror,TBaseType.MSG_ERROR_NO_ROOT_NODE,this,-1));
1484            }
1485        }
1486        return ret;
1487    }
1488
1489    private boolean isExplainForConnection() {
1490        if (sourcetokenlist == null) return false;
1491        for (int i = 0; i < sourcetokenlist.size() - 1; i++) {
1492            TSourceToken st = sourcetokenlist.get(i);
1493            if (st.toString().equalsIgnoreCase("for")) {
1494                TSourceToken next = sourcetokenlist.nextsolidtoken(i, 1, false);
1495                if (next != null && next.toString().equalsIgnoreCase("connection")) {
1496                    return true;
1497                }
1498            }
1499        }
1500        return false;
1501    }
1502
1503    public void clearError(){
1504        syntaxErrors.clear();
1505        syntaxHints.clear();
1506    }
1507
1508    void clear(){
1509        syntaxErrors.clear();
1510        syntaxHints.clear();
1511// todo all subclass should add super()       
1512    }
1513
1514    public void setStmtScope(TStmtScope stmtScope) {
1515        this.stmtScope = stmtScope;
1516    }
1517
1518    public TStmtScope getStmtScope() {
1519        return stmtScope;
1520    }
1521
1522    /**
1523     * Original SQL fragment of this statement.
1524     * @return   Original statement text.
1525     */
1526
1527    /*
1528    public String toString(){
1529       StringBuffer sb = new StringBuffer("");
1530       for(int i=0; i<sourcetokenlist.size();i++){
1531          sb.append(sourcetokenlist.get(i).toString());  
1532        }
1533       return sb.toString();
1534    }
1535    */
1536    protected TStmtScope stmtScope = null;
1537    void buildsql(){}
1538    public int doParseStatement(TCustomSqlStatement psql){
1539        if (psql != null){
1540            this.setParentStmt(psql);
1541            this.setFrameStack(psql.getFrameStack());
1542            psql.stmtScope.incrementCurrentStmtIndex();
1543            this.queryId = String.format("%s#stmt_%d_%s", psql.getQueryId(),psql.stmtScope.getCurrentStmtIndex(), this.sqlstatementtype);
1544            stmtScope = new TStmtScope(psql.stmtScope,this);
1545            // psql.statements.add(this);
1546        }else{
1547            stmtScope = new TStmtScope(this);
1548
1549            // global scope
1550            this.getFrameStack().peek().getScope().incrementCurrentStmtIndex();
1551            this.queryId = String.format("stmt_%d_%s",this.getFrameStack().peek().getScope().getCurrentStmtIndex(), this.sqlstatementtype);
1552        }
1553
1554        if ((this.getStartToken() == null)&&(rootNode != null)){
1555            this.setStartToken(rootNode.getStartToken());
1556        }
1557        if ((this.getEndToken() == null)&&(rootNode != null)){
1558            this.setEndToken(rootNode.getEndToken());
1559        }
1560
1561        if(this.getGsqlparser() == null){
1562            if (rootNode != null){
1563                this.setGsqlparser(rootNode.getGsqlparser());
1564            }
1565        }
1566        return 0;
1567    }
1568
1569    void addtokentolist(TSourceToken st){
1570       st.stmt = this;
1571       sourcetokenlist.add(st);
1572    }
1573
1574    public TTable analyzeTablename(TObjectName tableName){
1575        TTable lcTable = new TTable();
1576        lcTable.setTableType(ETableSource.objectname);
1577        lcTable.setStartToken(tableName.getStartToken());
1578        lcTable.setEndToken(tableName.getEndToken());
1579        lcTable.setGsqlparser(this.getGsqlparser());
1580        lcTable.setTableName(tableName);
1581
1582        tables.addTable(lcTable);
1583        return lcTable;
1584    }
1585
1586    protected boolean isTableACTE(TTable pTable){
1587        boolean lcResult = false;
1588        TCTEList cteList1 = getCteList();
1589        if (cteList1 == null){
1590            TCustomSqlStatement lcStmt = getParentStmt();
1591            while (lcStmt != null){
1592                if (lcStmt.getCteList() != null){
1593                    cteList1 = lcStmt.getCteList();
1594                    break;
1595                }else {
1596                    lcStmt = lcStmt.getParentStmt();
1597                }
1598            }
1599        }
1600        if (cteList1 == null) return  false;
1601       // TCTE lcCTE = cteList1.cteNames.get(TBaseType.getTextWithoutQuoted(pTable.toString()).toUpperCase());
1602        if (pTable.toString() == null) return false;
1603
1604        int searchPos = pTable.getStartToken().posinlist;
1605        if (this.getCteIncludeThisStmt() != null){
1606            searchPos = this.getCteIncludeThisStmt().getStartToken().posinlist;
1607        }
1608        TCTE lcCTE = cteList1.searchCTEByName(TBaseType.getTextWithoutQuoted(pTable.toString()).toUpperCase(),searchPos);
1609        if ( lcCTE != null ){
1610            if (pTable.setCTE(lcCTE)){
1611                pTable.setCTEName(true);
1612                lcResult = true;
1613            }
1614        }
1615//        for (int i=0;i<cteList1.size();i++){
1616//            lcCTE = cteList1.getCTE(i);
1617//            if (TBaseType.getTextWithoutQuoted(lcCTE.getTableName().toString()).equalsIgnoreCase(TBaseType.getTextWithoutQuoted(pTable.toString()))){
1618//                pTable.setCTEName(true);
1619//                pTable.setCTE(lcCTE);
1620//                lcResult = true;
1621//                break;
1622//            }
1623//        }
1624
1625        return lcResult;
1626
1627    }
1628
1629    public TTable findTable(ETableEffectType[] tableEffectTypes){
1630        TTable lcResult = null;
1631        for(int i=0;i<tables.size();i++){
1632            for(int j=0;j<tableEffectTypes.length;j++){
1633                if (tables.getTable(i).getEffectType() == tableEffectTypes[j]){
1634                    lcResult = tables.getTable(i);
1635                    return  lcResult;
1636                }
1637            }
1638        }
1639        return  lcResult;
1640    }
1641    public void addToTables(TTable pTable){
1642        tables.addTable(pTable);
1643        if (isTableACTE(pTable)) return;
1644
1645        if (pTable.getTableName() == null) return;
1646        if (pTable.getTableName().getTableToken() == null) return;
1647        if ((pTable.getTableName().getTableString().toString().equalsIgnoreCase("inserted"))||(pTable.getTableName().getTableString().toString().equalsIgnoreCase("deleted"))){
1648           if ((getAncestorStmt().sqlstatementtype == ESqlStatementType.sstcreatetrigger)
1649               ||(getAncestorStmt().sqlstatementtype == ESqlStatementType.sstmssqlaltertrigger)){
1650               //pTable.setLinkTable(true);
1651               ETableEffectType[] effectTypes = new ETableEffectType[]{
1652                       ETableEffectType.tetTriggerOn,ETableEffectType.tetTriggerInsert,ETableEffectType.tetTriggerDelete,ETableEffectType.tetTriggerUpdate,ETableEffectType.tetTriggerInsteadOf
1653               };
1654               pTable.setLinkTable(getAncestorStmt().findTable(effectTypes));
1655           }
1656        }
1657
1658    }
1659
1660    public TJoin analyzeTableOrJoin(TFromTable pfromTable){
1661        TFromTable lcFromTable = pfromTable;
1662        TJoin lcJoin;
1663        TTable lcTable;
1664
1665        if (lcFromTable.getFromtableType() != ETableSource.join){
1666            lcJoin = new TJoin();
1667            lcTable = analyzeFromTable(lcFromTable,true);
1668            lcTable.setEffectType(ETableEffectType.tetSelect);
1669            lcJoin.setTable(lcTable);
1670            lcJoin.setStartToken(lcJoin.getTable().getStartToken());
1671            lcJoin.setEndToken(lcJoin.getTable().getEndToken());
1672            lcJoin.setGsqlparser(getGsqlparser());
1673            this.fromSourceTable = lcTable;
1674            this.getRelations().add(lcTable);
1675        }else{
1676            this.fromSourceJoin = lcFromTable.getJoinExpr();
1677
1678            this.fromSourceTable = new TTable();
1679            this.fromSourceTable.setTableType(ETableSource.join);
1680            this.fromSourceTable.setAliasClause(lcFromTable.getJoinExpr().getAliasClause());
1681            this.fromSourceTable.setStartToken(lcFromTable.getStartToken());
1682            this.fromSourceTable.setEndToken(lcFromTable.getEndToken());
1683            this.fromSourceTable.setGsqlparser(lcFromTable.getGsqlparser());
1684            this.fromSourceTable.setJoinExpr(this.fromSourceJoin);
1685            this.getRelations().add(this.fromSourceTable);
1686
1687            lcJoin = analyzeJoin(lcFromTable.getJoinExpr(),null,true);
1688            lcJoin.doParse(this, ESqlClause.join);
1689
1690            if (lcFromTable.getLateralViewList() != null){
1691                for(TLateralView lateralView:lcFromTable.getLateralViewList()){
1692                    TTable t = lateralView.createATable(this);
1693                    addToTables(t);
1694                    this.relations.add(t);
1695                }
1696            }
1697        }
1698
1699        return lcJoin;
1700    }
1701
1702    public TTable analyzeFromTable(TFromTable pfromTable, Boolean addToTableList){
1703        return analyzeFromTable(pfromTable,addToTableList,ESqlClause.unknown);
1704    }
1705
1706    public TTable analyzeFromTable(TFromTable pfromTable, Boolean addToTableList, ESqlClause pLocation){
1707        TTable lcTable = new TTable();
1708        lcTable.setTableType(pfromTable.getFromtableType());
1709        lcTable.setAliasClause(pfromTable.getAliasClause());
1710        lcTable.setStartToken(pfromTable.getStartToken());
1711        lcTable.setEndToken(pfromTable.getEndToken());
1712        lcTable.setGsqlparser(pfromTable.getGsqlparser());
1713        lcTable.setTableHintList(pfromTable.getTableHintList());
1714        lcTable.setTableSample(pfromTable.getTableSample());
1715        lcTable.setLateralViewList(pfromTable.getLateralViewList());
1716        lcTable.setTableProperties(pfromTable.getTableProperties());
1717        lcTable.setPivotedTable(pfromTable.getPivotedTable());
1718        lcTable.setParenthesisCount(pfromTable.getParenthesisCount());
1719        lcTable.setParenthesisAfterAliasCount(pfromTable.getParenthesisAfterAliasCount());
1720        lcTable.setTableKeyword(pfromTable.isTableKeyword());
1721        lcTable.setOnlyKeyword(pfromTable.isOnlyKeyword());
1722        lcTable.setFlashback(pfromTable.getFlashback());
1723        lcTable.setPxGranule(pfromTable.getPxGranule());
1724        lcTable.setTimeTravelClause(pfromTable.getTimeTravelClause());
1725        //lcTable.setPartitionClause(pfromTable.getPartitionClause());
1726
1727        if(getFrameStack().firstElement() != null){
1728            TFrame stackFrame = getFrameStack().firstElement();
1729            TGlobalScope globalScope = (TGlobalScope)stackFrame.getScope();
1730            lcTable.setSqlEnv(globalScope.getSqlEnv());
1731        }
1732
1733       switch(lcTable.getTableType()){
1734           case objectname:{
1735              // tables.addTableByTableRefernce(pfromTable.getTableObjectName());
1736               boolean insertedInTrigger = false;
1737               if (getTopStatement().sqlstatementtype == ESqlStatementType.sstcreatetrigger){
1738                  insertedInTrigger = (pfromTable.getTableObjectName().toString().compareToIgnoreCase("inserted")==0);
1739               }
1740
1741               if (insertedInTrigger){
1742                   // change table name from inserted to onTable name in create trigger 
1743                 lcTable.setTableName(((TCreateTriggerStmt)getTopStatement()).getOnTable().getTableName());
1744                 //lcTable.setLinkTable(true);
1745                 lcTable.setLinkTable(((TCreateTriggerStmt)getTopStatement()).getOnTable());
1746
1747               }else{
1748                    lcTable.setTableName(pfromTable.getTableObjectName());
1749                   lcTable.getTableName().setSqlEnv(getSqlEnv());
1750
1751//                   if (getSqlEnv().getDefaultCatalogName() != null){
1752//                       if (lcTable.getTableName().getDatabaseToken() == null){
1753//                           lcTable.getTableName().setDatabaseToken(new TSourceToken(getSqlEnv().getDefaultCatalogName()),true);
1754//                       }
1755//                   }
1756
1757//                   if ((lcTable.getTableName().getSchemaToken() == null)&&(TSQLEnv.supportSchema(this.dbvendor))){
1758//                       // let find schema name for this table in env
1759//                       TSQLTable t = getSqlEnv().searchTable(".."+lcTable.getFullName());
1760//                       if (t != null){
1761//                           TSQLSchema s = t.getSchema();
1762//                           if (s != null){
1763//                               lcTable.getTableName().setSchemaToken(new TSourceToken(s.getName()),true);
1764//                           }
1765//                       }
1766//                   }
1767
1768               }
1769               // let's check is it cte name or ordinary table name
1770               TCTEList lcCteList = searchCTEList(false);
1771               TCTE lcCte = null;
1772               if (lcCteList != null){
1773                 for(int i=0;i<lcCteList.size();i++){
1774                    lcCte = lcCteList.getCTE(i);
1775                    if (lcCte.getTableName().toString().compareToIgnoreCase(TBaseType.getTextWithoutQuoted(lcTable.getTableName().toString()))==0){
1776                        // this is cte name
1777                        if (lcTable.setCTE(lcCte)){
1778                            lcTable.setCTEName(true);
1779                            lcTable.setCteColomnReferences(lcCte.getColumnList());
1780                            break;
1781                        }
1782                    }
1783                 }
1784               }
1785
1786               break;
1787           }
1788           case tableExpr:{
1789               ESqlClause location =  ESqlClause.tableExpr; //ESqlClause.resultColumn;
1790               if (sqlstatementtype == ESqlStatementType.sstinsert ){
1791                   // change location here
1792               }
1793               lcTable.setTableExpr(pfromTable.getTableExpr());
1794               lcTable.getTableExpr().doParse(this,location);
1795               // 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;
1796               // RETURNS (outkey integer, tokennum integer, token varchar(20))
1797               lcTable.setColumnDefinitions(pfromTable.getColumnDefinitions());
1798               // Teradata table function HASH BY and LOCAL ORDER BY clauses
1799               lcTable.setHashByClause(pfromTable.getHashByClause());
1800               lcTable.setLocalOrderBy(pfromTable.getLocalOrderBy());
1801               break;
1802           }
1803           case subquery:{
1804//               if (pfromTable.getSubquerynode().isHiveFromQuery()){
1805//                   THiveFromQuery fromQuery  = new THiveFromQuery(dbvendor);
1806//                   lcTable.setHiveFromQuery(fromQuery);
1807//                   fromQuery.rootNode = pfromTable.getSubquerynode();
1808//                   fromQuery.setStartToken(pfromTable.getSubquerynode());
1809//                   fromQuery.setEndToken(pfromTable.getSubquerynode());
1810//                   fromQuery.setLabelName(this.labelName);
1811//                   fromQuery.doParseStatement(this);
1812//               }else{
1813//                   lcTable.subquery = new TSelectSqlStatement(dbvendor);
1814//                   lcTable.subquery.rootNode = pfromTable.getSubquerynode();
1815//                   lcTable.subquery.setLocation(ESqlClause.elTable);
1816//                   //lcTable.subquery.resultColumnList = ((TSelectSqlNode)lcTable.subquery.rootNode).getResultColumnList();
1817//                   lcTable.subquery.doParseStatement(this);
1818//               }
1819
1820               lcTable.subquery = new TSelectSqlStatement(dbvendor);
1821               lcTable.subquery.rootNode = pfromTable.getSubquerynode();
1822               if (pLocation == ESqlClause.unknown){
1823                   lcTable.subquery.setLocation(ESqlClause.elTable);
1824               }else{
1825                   lcTable.subquery.setLocation(pLocation);
1826               }
1827               //lcTable.subquery.resultColumnList = ((TSelectSqlNode)lcTable.subquery.rootNode).getResultColumnList();
1828               lcTable.subquery.doParseStatement(this);
1829
1830               break;
1831           }
1832           case function:{
1833               ESqlClause location = ESqlClause.tableFunction;// resultColumn;
1834               if (sqlstatementtype == ESqlStatementType.sstinsert ){
1835                   // change location here
1836               }
1837               lcTable.setFuncCall(pfromTable.getFuncCall());
1838               lcTable.getFuncCall().doParse(this,location);
1839               break;
1840           }
1841           case containsTable:{
1842               ESqlClause location = ESqlClause.containsTable;//resultColumn;
1843               if (sqlstatementtype == ESqlStatementType.sstinsert ){
1844                   // change location here
1845               }
1846               lcTable.setContainsTable(pfromTable.getContainsTable());
1847               lcTable.getContainsTable().doParse(this,location);
1848               break;
1849           }
1850
1851           case openrowset:{
1852               ESqlClause location = ESqlClause.openrowset;//resultColumn;
1853               if (sqlstatementtype == ESqlStatementType.sstinsert ){
1854                   // change location here
1855               }
1856               lcTable.setOpenRowSet(pfromTable.getOpenRowSet());
1857               lcTable.getOpenRowSet().doParse(this,location);
1858               break;
1859           }
1860
1861           case openxml:{
1862               ESqlClause location = ESqlClause.openxml;//resultColumn;
1863               if (sqlstatementtype == ESqlStatementType.sstinsert ){
1864                   // change location here
1865               }
1866               lcTable.setOpenXML(pfromTable.getOpenXML());
1867               lcTable.getOpenXML().doParse(this,location);
1868               break;
1869           }
1870
1871           case opendatasource:{
1872               ESqlClause location = ESqlClause.opendatasource;//resultColumn;
1873               if (sqlstatementtype == ESqlStatementType.sstinsert ){
1874                   // change location here
1875               }
1876               lcTable.setOpenDatasource(pfromTable.getOpenDatasource());
1877               lcTable.getOpenDatasource().doParse(this,location);
1878               break;
1879           }
1880
1881           case openquery:{
1882               ESqlClause location = ESqlClause.openquery;//resultColumn;
1883               if (sqlstatementtype == ESqlStatementType.sstinsert ){
1884                   // change location here
1885               }
1886               lcTable.setOpenquery(pfromTable.getOpenQuery());
1887               lcTable.getOpenquery().doParse(this,location);
1888               lcTable.setSubquery(lcTable.getOpenquery().getSubquery());
1889               break;
1890           }
1891
1892           case datachangeTable:{
1893               ESqlClause location = ESqlClause.datachangeTable;//resultColumn;
1894               if (sqlstatementtype == ESqlStatementType.sstinsert ){
1895                   // change location here
1896               }
1897               lcTable.setDatachangeTable(pfromTable.getDatachangeTable());
1898               lcTable.getDatachangeTable().doParse(this,location);
1899               break;
1900           }
1901           case rowList:{
1902               ESqlClause location = ESqlClause.rowList;//resultColumn;
1903               lcTable.setValueClause(pfromTable.getValueClause());
1904               lcTable.getValueClause().doParse(this,location);
1905               break;
1906           }
1907           case pivoted_table:{
1908               ESqlClause location = ESqlClause.pivoted_table;//resultColumn;
1909               lcTable.getPivotedTable().doParse(this,location);
1910               addToTableList = false;
1911               targetTable = lcTable;
1912               break;
1913           }
1914           case xmltable:{
1915               ESqlClause location = ESqlClause.xmltable;//resultColumn;
1916               lcTable.setXmlTable(pfromTable.getXmlTable());
1917               lcTable.getXmlTable().doParse(this,location);
1918               break;
1919           }
1920
1921           case informixOuter:{
1922               ESqlClause location = ESqlClause.outerTable;//resultColumn;
1923               lcTable.setOuterClause(pfromTable.getOuterClause());
1924               lcTable.getOuterClause().doParse(this,location);
1925               break;
1926           }
1927
1928           case table_ref_list:{
1929               lcTable.setFromTableList(pfromTable.getFromTableList());
1930               break;
1931           }
1932//           case hiveFromQuery:{
1933//               THiveFromQuery fromQuery = new THiveFromQuery(EDbVendor.dbvhive);
1934//               fromQuery.rootNode = pfromTable.getFromQuerySqlNode();
1935//               fromQuery.doParseStatement(this);
1936//               lcTable.setHiveFromQuery(fromQuery);
1937//               break;
1938//           }
1939           case output_merge:{
1940               TMergeSqlStatement outputMerge = new TMergeSqlStatement(EDbVendor.dbvmssql);
1941               outputMerge.rootNode = pfromTable.getMergeSqlNode();
1942               outputMerge.doParseStatement(this);
1943               lcTable.setOutputMerge(outputMerge);
1944               break;
1945           }
1946           case td_unpivot:{
1947               // Set the TD_UNPIVOT output table before doParse so that VALUE_COLUMNS and
1948               // UNPIVOT_COLUMN can be linked to it (they are output columns of TD_UNPIVOT)
1949               pfromTable.getTdUnpivot().setTdUnpivotOutputTable(lcTable);
1950               pfromTable.getTdUnpivot().doParse(this,ESqlClause.tdUnPivot);
1951               lcTable.setTdUnpivot(pfromTable.getTdUnpivot());
1952               break;
1953           }
1954           case unnest:{
1955               pfromTable.getUnnestClause().doParse(this,ESqlClause.elTable);
1956               lcTable.setUnnestClause(pfromTable.getUnnestClause());
1957               if (lcTable.getAliasClause() != null){
1958                   if (lcTable.getAliasClause().getColumns() != null){
1959                       for(TObjectName pColumn:lcTable.getAliasClause().getColumns()){
1960                           lcTable.getLinkedColumns().addObjectName(pColumn);
1961                           pColumn.setSourceTable(lcTable);
1962                       }
1963                   }else if (lcTable.getAliasClause().getAliasName() != null){
1964//                       SELECT *
1965//                               FROM UNNEST(['foo', 'bar', 'baz', 'qux', 'corge', 'garply', 'waldo', 'fred']) AS element
1966//                       WITH OFFSET AS offset
1967
1968                       // add element as column of unnest table.
1969                       TObjectName newColumn = TObjectName.createObjectName(this.dbvendor,EDbObjectType.column,lcTable.getAliasClause().getAliasName().getStartToken());
1970                       lcTable.getLinkedColumns().addObjectName(newColumn);
1971                       newColumn.setSourceTable(lcTable);
1972                   }
1973               }
1974
1975               if (lcTable.getUnnestClause().getWithOffset() != null){
1976                   if (lcTable.getUnnestClause().getWithOffsetAlais() != null){
1977                       // with offset as offsetAlias
1978                       TAliasClause aliasClause = lcTable.getUnnestClause().getWithOffsetAlais();
1979                       if (aliasClause.getAliasName() != null){
1980                           TObjectName newColumn = TObjectName.createObjectName(this.dbvendor,EDbObjectType.column,aliasClause.getAliasName().getStartToken());
1981                           lcTable.getLinkedColumns().addObjectName(newColumn);
1982                           newColumn.setSourceTable(lcTable);
1983                       }
1984                   }else{
1985                       // with offset
1986                       TObjectName newColumn = TObjectName.createObjectName(this.dbvendor,EDbObjectType.column,new TSourceToken("offset"));
1987                       lcTable.getLinkedColumns().addObjectName(newColumn);
1988                       newColumn.setSourceTable(lcTable);
1989                   }
1990               }
1991
1992               // link columns in the select list to unnest()
1993               // select emp_id,name,state,city,zipcode from `absolute-runner-302907.gudu_sqlflow.ADDRESS_NESTED`, UNNEST(address)
1994               if (lcTable.getUnnestClause().getDerivedColumnList() != null){
1995                   TObjectNameList derivedColumns = lcTable.getUnnestClause().getDerivedColumnList();
1996                   for(int i=0;i<derivedColumns.size();i++){
1997                       //System.out.println(derivedColumns.getObjectName(i).toString());
1998                       lcTable.getLinkedColumns().addObjectName(derivedColumns.getObjectName(i));
1999                   }
2000               }
2001
2002               break;
2003           }
2004           case jsonTable:{
2005               ESqlClause location = ESqlClause.jsonTable;//resultColumn;
2006               lcTable.setJsonTable(pfromTable.getJsonTable());
2007               lcTable.getJsonTable().doParse(this,location);
2008               break;
2009           }
2010           case externalTable:
2011               lcTable.setTableName(pfromTable.getTableObjectName());
2012               lcTable.setColumnDefinitions(pfromTable.getColumnDefinitions());
2013               lcTable.getColumnDefinitions().doParse(this,pLocation);
2014               lcTable.setTableType(ETableSource.externalTable); // tableType is reset in setTableName() method, so we reset it here
2015               break;
2016           case caseJoin:
2017               lcTable.setCaseJoin(pfromTable.getCaseJoin());
2018               lcTable.getCaseJoin().doParse(this,pLocation);
2019               break;
2020           case stageReference:
2021               lcTable.setStageReference(pfromTable.getStageReference());
2022               lcTable.getStageReference().doParse(this,pLocation);
2023
2024               lcTable.setTableName(lcTable.getStageReference().getStageName());
2025               lcTable.setTableType(ETableSource.stageReference);
2026               lcTable.getTableName().setSqlEnv(getSqlEnv());
2027
2028               break;
2029
2030      }//switch
2031
2032//        if (pfromTable.getPivotClause() != null){
2033//            lcTable.setPivotClause(pfromTable.getPivotClause());
2034//            lcTable.getPivotClause().doParse(this,ESqlClause.resultColumn);
2035//        }
2036
2037        lcTable.setPartitionExtensionClause(pfromTable.getPartitionExtensionClause());
2038
2039        //tables.addTable(lcTable);
2040        if (addToTableList) {
2041            addToTables(lcTable);
2042        }
2043
2044        if (lcTable.getTableHintList() != null){
2045            for(int i=0;i<lcTable.getTableHintList().size();i++){
2046                TTableHint hint = lcTable.getTableHintList().getElement(i);
2047                hint.setOwnerTable(lcTable);
2048                hint.doParse(this,ESqlClause.tableHint);
2049            }
2050        }
2051
2052        if (lcTable.getLateralViewList() != null){
2053            for(TLateralView lateralView:lcTable.getLateralViewList()){
2054                TTable t = lateralView.createATable(this);
2055                addToTables(t);
2056                this.relations.add(t);
2057            }
2058        }
2059
2060        if (lcTable.getAliasClause() != null){
2061            if (lcTable.getAliasClause().toString().equalsIgnoreCase("and")){
2062                // end keyword can't be alias name
2063                TSourceToken st1 = lcTable.getAliasClause().getStartToken();
2064                TSyntaxError err = new TSyntaxError(st1.toString()
2065                        ,st1.lineNo,st1.columnNo
2066                        ,String.format("AND keyword can't be table alias")
2067                        ,EErrorType.sperror
2068                        ,TBaseType.MSG_ERROR_AND_KEYWORD_CANT_USED_AS_TABLE_ALIAS
2069                        ,this,st1.posinlist);
2070                this.parseerrormessagehandle( err);
2071
2072            }
2073        }
2074
2075        return lcTable;
2076    }
2077
2078   public TJoin analyzeJoin(TJoinExpr pJoinExpr,TJoin pJoin,Boolean isSub){
2079        TJoin retval = pJoin;
2080        TJoinItem lcJoinItem = null ;
2081
2082        if (pJoinExpr == null) {return retval;}
2083
2084        if (pJoinExpr.getJointype() == EJoinType.nested)
2085        {
2086            if (isSub)
2087            {
2088                if (retval == null) {  // top level, left side is a join
2089                  retval = new TJoin();
2090                  retval.setStartToken(pJoinExpr.getStartToken());
2091                  retval.setEndToken(pJoinExpr.getEndToken());
2092                }
2093
2094                pJoinExpr.setJointype(pJoinExpr.original_jontype);
2095                retval.setJoin(analyzeJoin(pJoinExpr,null,true));
2096                //retval =analyzeJoin(pJoinExpr,null,true);
2097                retval.setKind(TBaseType.join_source_join);
2098                retval.getJoin().setAliasClause(pJoinExpr.getAliasClause());
2099                retval.getJoin().setWithParen(true);
2100                retval.getJoin().setNestedParen(pJoinExpr.getNestedParen());
2101            }
2102           else
2103            {
2104                if (retval == null)
2105                {
2106                    retval = new TJoin();
2107                    retval.setStartToken(pJoinExpr.getStartToken());
2108                    retval.setEndToken(pJoinExpr.getEndToken());
2109                    retval.setGsqlparser(this.getGsqlparser());
2110                }
2111                else
2112                {
2113                }
2114                pJoinExpr.setJointype(pJoinExpr.original_jontype);
2115                retval = analyzeJoin(pJoinExpr,retval,false);
2116                //retval.setJoin(analyzeJoin(pJoinExpr,retval,false));
2117                //retval = analyzeJoin(pJoinExpr,retval,false);
2118                //retval.setKind(TBaseType.join_source_join);
2119                //retval.setKind(TBaseType.join_source_table);
2120                //retval.setAliasClause(pJoinExpr.getAliasClause());
2121                retval.setAliasClause(pJoinExpr.getAliasClause());
2122                retval.setWithParen(true);
2123                retval.setNestedParen(pJoinExpr.getNestedParen());
2124            }
2125            return retval;
2126        }
2127
2128        if (pJoinExpr.getLeftOperand().getFromtableType() != ETableSource.join){
2129            if (retval == null) {
2130              retval = new TJoin();
2131              retval.setStartToken(pJoinExpr.getStartToken());
2132              retval.setEndToken(pJoinExpr.getEndToken());
2133              retval.setGsqlparser(this.getGsqlparser());
2134
2135              //  retval.setStartToken(pJoinExpr.getLeftOperand().getStartToken());
2136              //  retval.setEndToken(pJoinExpr.getLeftOperand().getEndToken());
2137            }
2138            TTable lcTable = analyzeFromTable(pJoinExpr.getLeftOperand(),true,ESqlClause.join);
2139            lcTable.setEffectType(ETableEffectType.tetSelect);
2140            retval.setTable(lcTable);
2141            //retval.joinTable.OwnerJoin = result;
2142            retval.setKind(TBaseType.join_source_table);
2143            pJoinExpr.setLeftTable(lcTable);
2144        }else{
2145            TJoinExpr lcJoinItemJoinExpr = pJoinExpr.getLeftOperand().getJoinExpr();
2146            //if (lcJoinItemJoinExpr.getJointype() == TBaseType.join_nested){
2147            //    lcJoinItemJoinExpr.setJointype(lcJoinItemJoinExpr.original_jontype);
2148            //}
2149
2150            if (retval != null) {
2151              retval = analyzeJoin(lcJoinItemJoinExpr,retval,true);
2152            } else {
2153              retval = analyzeJoin(lcJoinItemJoinExpr,retval,isSub);
2154            }
2155            retval.setStartToken(lcJoinItemJoinExpr.getStartToken());
2156            retval.setEndToken(lcJoinItemJoinExpr.getEndToken());
2157
2158
2159            TTable lcTable = new TTable();
2160            lcTable.setTableType(pJoinExpr.getLeftOperand().getFromtableType());
2161            lcTable.setAliasClause(lcJoinItemJoinExpr.getAliasClause());
2162            lcTable.setStartToken(lcJoinItemJoinExpr.getStartToken());
2163            lcTable.setEndToken(lcJoinItemJoinExpr.getEndToken());
2164            pJoinExpr.setLeftTable(lcTable);
2165            lcTable.setJoinExpr(lcJoinItemJoinExpr);
2166        }
2167
2168        if (pJoinExpr.getRightOperand().getFromtableType() != ETableSource.join){
2169            if (retval != null)
2170            {
2171                lcJoinItem = new TJoinItem();
2172                TTable lcTable = analyzeFromTable(pJoinExpr.getRightOperand(),true,ESqlClause.join);
2173                lcTable.setEffectType(ETableEffectType.tetSelect);
2174                lcJoinItem.setTable(lcTable);
2175                lcJoinItem.setStartToken(lcJoinItem.getTable().getStartToken());
2176                lcJoinItem.setEndToken(lcJoinItem.getTable().getEndToken());
2177               // lcJoinItem.JoinItemTable.OwnerJoinItem := lcJoinItem;
2178                lcJoinItem.setKind(TBaseType.join_source_table);
2179                retval.getJoinItems().addJoinItem(lcJoinItem);
2180                pJoinExpr.setRightTable(lcTable);
2181            }
2182        }else{
2183            if (retval != null)
2184            {
2185                lcJoinItem = new TJoinItem();
2186                lcJoinItem.setKind(TBaseType.join_source_join);
2187                TJoinExpr lcJoinItemJoinExpr = pJoinExpr.getRightOperand().getJoinExpr();
2188                //if (lcJoinItemJoinExpr.getJointype() == TBaseType.join_nested){
2189                //    lcJoinItemJoinExpr.setJointype(lcJoinItemJoinExpr.original_jontype);
2190                //}
2191                lcJoinItem.setJoin(analyzeJoin(pJoinExpr.getRightOperand().getJoinExpr(),null,false));
2192                lcJoinItem.getJoin().setAliasClause(lcJoinItemJoinExpr.getAliasClause());
2193                lcJoinItem.setStartToken(lcJoinItem.getJoin().getStartToken());
2194                lcJoinItem.setEndToken(lcJoinItem.getJoin().getEndToken());
2195                retval.getJoinItems().addJoinItem(lcJoinItem);
2196
2197                TTable lcTable = new TTable();
2198                lcTable.setTableType(pJoinExpr.getRightOperand().getFromtableType());
2199                lcTable.setAliasClause(lcJoinItemJoinExpr.getAliasClause());
2200                lcTable.setStartToken(lcJoinItemJoinExpr.getStartToken());
2201                lcTable.setEndToken(lcJoinItemJoinExpr.getEndToken());
2202                pJoinExpr.setRightTable(lcTable);
2203                lcTable.setJoinExpr(lcJoinItemJoinExpr);
2204            }
2205        }
2206
2207        if (lcJoinItem == null) return retval;
2208
2209        lcJoinItem.setJoinType(pJoinExpr.getJointype());
2210        lcJoinItem.setUsingColumns(pJoinExpr.usingColumns);
2211        if ((lcJoinItem.getUsingColumns() != null) && (tables.size()>1)){
2212            TObjectName crf ;
2213            for (int i=0;i<lcJoinItem.getUsingColumns().size();i++){
2214                crf = lcJoinItem.getUsingColumns().getObjectName(i);
2215                // link this column to last 2 tables
2216                tables.getTable(tables.size()-1).getObjectNameReferences().addObjectName(crf);
2217                tables.getTable(tables.size()-2).getObjectNameReferences().addObjectName(crf);
2218
2219                tables.getTable(tables.size()-1).getLinkedColumns().addObjectName(crf);
2220                crf.setSourceTable(tables.getTable(tables.size()-1));
2221                tables.getTable(tables.size()-2).getLinkedColumns().addObjectName(crf);
2222                crf.setSourceTable(tables.getTable(tables.size()-2));
2223
2224            }
2225            lcJoinItem.setEndToken(lcJoinItem.getUsingColumns().getEndToken());
2226        }
2227        lcJoinItem.setOnCondition(pJoinExpr.onCondition);
2228        if (lcJoinItem.getOnCondition() != null)
2229        {
2230            lcJoinItem.getOnCondition().doParse(this,ESqlClause.joinCondition);
2231            lcJoinItem.setEndToken(lcJoinItem.getOnCondition().getEndToken());
2232        }
2233
2234
2235        return retval;
2236    }
2237
2238    public boolean locateVariableOrParameter(TObjectName cr){
2239        return locateVariableOrParameter(cr,false);
2240    }
2241
2242    public boolean locateVariableOrParameter(TObjectName cr, boolean checkVariableDeclaredInProcedure){
2243      boolean ret =  false;
2244      if (cr.getDbObjectType() == EDbObjectType.variable) return true;
2245      if (cr.toString().equalsIgnoreCase("*")) return  false;
2246      //search variable in framestack
2247
2248      TVariable symbolVariable = null;
2249
2250      if (cr.getTableToken() != null){
2251          // record_variable.column
2252          symbolVariable =  TSymbolTableManager.searchSymbolVariable(this.getFrameStack(),cr.getTableToken().toString());
2253          if (symbolVariable != null){
2254              cr.getTableToken().setDbObjectType(EDbObjectType.variable);
2255              //TTable sourceTable = new TTable(new TObjectName(EDbObjectType.table,symbolVariable.getVariableName().getStartToken()));
2256              TTable sourceTable = new TTable(TObjectName.createObjectName (this.dbvendor, EDbObjectType.variable,symbolVariable.getVariableName().getStartToken()));
2257              sourceTable.getLinkedColumns().addObjectName(cr);
2258              cr.setSourceTable(sourceTable);
2259             // symbolVariable.getVariableName().getReferencedObjects().addObjectName(cr);
2260             // System.out.println("find variable:"+cr.toString());
2261              cr.setResolveStatus(TBaseType.RESOLVED_AND_FOUND); // set resolve status to resolved,避免在 TAttributeResolver 中关联到其他 table
2262              return true;
2263          }
2264
2265      }else{
2266          // variable
2267          symbolVariable =  TSymbolTableManager.searchSymbolVariable(this.getFrameStack(),cr.toString());
2268          if (symbolVariable != null){
2269              cr.setDbObjectType(EDbObjectType.variable);
2270              symbolVariable.getVariableName().getReferencedObjects().addObjectName(cr);
2271              return true;
2272          }
2273      }
2274
2275        // check parameters in plsql only, may add support for sql server later.
2276      if(! ((dbvendor == EDbVendor.dbvoracle)||(dbvendor == EDbVendor.dbvmysql))) return false;
2277      if (cr.getObjectType() == TObjectName.ttobjVariable) return true;
2278
2279        Stack symbolTable = this.getTopStatement().getSymbolTable();
2280        TSymbolTableItem item = null;
2281        TObjectName objName = null;
2282        TObjectName qualifiedName = null; // function/procedure name or label name of plsql block
2283        for (int i = symbolTable.size()-1;i>=0;i--){
2284            item = (TSymbolTableItem)symbolTable.get(i);
2285            if (item.getData() instanceof TParameterDeclaration){
2286                objName = ((TParameterDeclaration)item.getData()).getParameterName();
2287            }else if (item.getData() instanceof TVarDeclStmt){
2288                objName = ((TVarDeclStmt)item.getData()).getElementName();
2289            }else  if (item.getData() instanceof TObjectName){
2290                objName = (TObjectName)item.getData();
2291            }
2292
2293            if (objName == null) continue;
2294
2295            if (cr.toString().compareToIgnoreCase(objName.toString()) == 0){
2296                ret = true;
2297                if (checkVariableDeclaredInProcedure) break; // return true if variable declared in procedure
2298                for(int j=0;i<tables.size();i++){
2299                    TTable lcTable = tables.getTable(j);
2300                    if (lcTable.isBaseTable()){
2301                        if (fireOnMetaDatabaseTableColumn(
2302                                             lcTable.getPrefixServer()
2303                                            ,lcTable.getPrefixDatabase()
2304                                            ,lcTable.getPrefixSchema()
2305                                            ,lcTable.getName()
2306                                            ,cr.getColumnNameOnly())){
2307                            ret = false;
2308                            break;
2309                        }
2310                    }
2311                }
2312
2313                if (ret)  break;
2314            }else if (cr.toString().indexOf(".")>0){
2315                // qualified object reference, compare it with procedure/function/block label prefixed
2316                if (item.getStmt() instanceof TPlsqlCreateFunction){
2317                   qualifiedName = ((TPlsqlCreateFunction)item.getStmt()).getFunctionName();
2318                }else if (item.getStmt() instanceof TPlsqlCreateProcedure){
2319                   qualifiedName = ((TPlsqlCreateProcedure)item.getStmt()).getProcedureName();
2320                }else if (item.getStmt() instanceof TCommonBlock){
2321                   qualifiedName = ((TCommonBlock)item.getStmt()).getLabelName();
2322                }
2323
2324                if (qualifiedName != null){
2325                    if (cr.toString().compareToIgnoreCase(qualifiedName.toString()+'.'+objName.toString()) == 0){
2326                        ret = true;
2327                    }
2328                }
2329
2330                if (ret ) break;
2331            }
2332
2333        }
2334          if (ret){
2335              //add this parameter or variable reference to original parameter/variable
2336                  objName.getReferencedObjects().addObjectName(cr);
2337                  cr.setObjectType(TObjectName.ttobjVariable);
2338          }
2339          return ret;
2340    }
2341
2342    TCTE findCTEByName(String cteName){
2343        TCTEList lcCteList = searchCTEList(false);
2344        TCTE lcCte = null;
2345        if (lcCteList != null){
2346          for(int i=0;i<lcCteList.size();i++){
2347             if (lcCteList.getCTE(i).getTableName().toString().compareToIgnoreCase(cteName)==0){
2348                 lcCte = lcCteList.getCTE(i);
2349                 break;
2350             }
2351          }
2352        }
2353     return lcCte;
2354    }
2355
2356    /**
2357     * @deprecated since 2.3.8.2, use {@link TTable#getExpandedStarColumns()} instead.
2358     *
2359     * @param lcTable
2360     * @return
2361     */
2362    public ArrayList<String> getColumnsInTable(TTable lcTable){
2363        if (lcTable.isCTEName()){
2364            ArrayList<String> columns = new ArrayList<>();
2365            if (lcTable.getCteColomnReferences()!=null){
2366                for(TObjectName n:lcTable.getCteColomnReferences()){
2367                    columns.add(n.toString());
2368                }
2369            }else if (lcTable.getCTE().getSubquery() != null && lcTable.getCTE().getSubquery().getResultColumnList() != null){
2370                for(TResultColumn resultColumn:lcTable.getCTE().getSubquery().getResultColumnList()){
2371                    columns.add(resultColumn.getDisplayName());
2372                }
2373            }
2374            return columns;
2375        }else{
2376            return getColumnsInTable(
2377                    lcTable.getPrefixServer()
2378                    ,lcTable.getPrefixDatabase()
2379                    ,lcTable.getPrefixSchema()
2380                    ,lcTable.getName()
2381            );
2382        }
2383    }
2384
2385
2386    /**
2387     * @deprecated since 2.3.8.2, use {@link TTable#getExpandedStarColumns()} instead.
2388     *
2389     * @param pServer
2390     * @param pDatabase
2391     * @param pSchema
2392     * @param pTable
2393     * @return
2394     */
2395    public ArrayList<String> getColumnsInTable(String pServer,String pDatabase,String pSchema,String pTable){
2396        TFrame stackFrame = getFrameStack().firstElement();
2397        TGlobalScope globalScope = (TGlobalScope)stackFrame.getScope();
2398
2399        if (globalScope.getSqlEnv() != null){
2400            return globalScope.getSqlEnv().getColumnsInTable(pDatabase+"."+pSchema+"."+pTable,false);
2401        }else{
2402            return  null;
2403        }
2404    }
2405
2406
2407    public boolean fireOnMetaDatabaseTableColumn(String pServer,String pDatabase,String pSchema,String pTable,String pColumn){
2408//        boolean lcResult = false;
2409//        if (this.getGsqlparser().getMetaDatabase() != null){
2410//            lcResult = this.getGsqlparser().getMetaDatabase().checkColumn(pServer,pDatabase,pSchema,pTable,pColumn);
2411//        }
2412
2413        TFrame stackFrame = getFrameStack().firstElement();
2414        TGlobalScope globalScope = (TGlobalScope)stackFrame.getScope();
2415
2416        if (globalScope.getSqlEnv() != null){
2417           // System.out.println(globalScope.getSqlEnv().toString());
2418
2419            return globalScope.getSqlEnv().columnInTable(pDatabase+"."+pSchema+"."+pTable,pColumn);
2420        }else{
2421            return  false;
2422        }
2423
2424//        return lcResult;
2425    }
2426
2427    public TTable getFirstPhysicalTable(){
2428        TTable ret = null;
2429        if (tables.size() == 0) return null;
2430        for(int i=0;i<tables.size();i++){
2431            if (tables.getTable(i).isBaseTable()) {
2432                ret = tables.getTable(i);
2433                break;
2434            }
2435        }
2436        return ret;
2437    }
2438    private TObjectNameList orphanColumns = null;
2439
2440    public TObjectNameList getOrphanColumns() {
2441        if (orphanColumns == null) orphanColumns = new TObjectNameList();
2442        return orphanColumns;
2443    }
2444
2445    protected boolean linkToFirstTable(TObjectName pColumn,int pCandidateTableCnt){
2446        boolean lcResult = false;
2447        if ((dbvendor == EDbVendor.dbvteradata)&&(pColumn.isQualified())&&(pColumn.getTableToken().getDbObjectType() != EDbObjectType.subquery_alias)){
2448            // update table1 set col = 'value' where table1.id = table2.id2
2449            boolean isFoundLinkedTable = false;
2450            TCustomSqlStatement lcSql = this;
2451            while (lcSql != null){
2452                int i = 0;
2453                i = lcSql.tables.searchTableByNameOrAlias(pColumn.getTableToken().toString());
2454                isFoundLinkedTable = ( i != -1);
2455                if (isFoundLinkedTable) {
2456                    if (lcSql.tables.getTable(i).getEffectType() == ETableEffectType.tetImplicitLateralDerivedTable ){
2457                        // 如果不查重table,会导致 employee.first_name 中的 employee 被第二次加到 tables 中
2458//                        DELETE FROM foodmart.trimmed_employee ACT
2459//                        WHERE ACT.employee_id = employee.employee_id
2460//                        AND  employee.first_name = 'Walter'
2461//                        AND  trimmed_salary.employee_id = -1
2462
2463                        TTable newTable = lcSql.tables.getTable(i);
2464                        newTable.getLinkedColumns().addObjectName(pColumn);
2465                        pColumn.setSourceTable(newTable);
2466                        pColumn.setValidate_column_status(TBaseType.COLUMN_LINKED_TO_TABLE_IN_OLD_ALGORITHM);
2467                    }
2468                    break;
2469                }
2470                lcSql = lcSql.getParentStmt();
2471            }
2472            if (!isFoundLinkedTable){
2473                TTable newTable = null;
2474                if (pColumn.getDatabaseToken() == null){
2475
2476                    //newTable = new TTable(new TObjectName(EDbObjectType.table,pColumn.getTableToken()));
2477                    newTable = new TTable(TObjectName.createObjectName (this.dbvendor, EDbObjectType.table,pColumn.getTableToken()));
2478                    newTable.setStartToken(pColumn.getTableToken());
2479                    newTable.setEndToken(pColumn.getTableToken());
2480                }else{
2481
2482                    //newTable = new TTable(new TObjectName(EDbObjectType.table,pColumn.getSchemaToken(), pColumn.getTableToken()));
2483                    newTable = new TTable(TObjectName.createObjectName (this.dbvendor,EDbObjectType.table,pColumn.getDatabaseToken(), pColumn.getTableToken()));
2484                    newTable.setStartToken(pColumn.getSchemaToken());
2485                    newTable.setEndToken(pColumn.getTableToken());
2486                }
2487
2488                newTable.setTableType(ETableSource.objectname);
2489                newTable.setEffectType(ETableEffectType.tetImplicitLateralDerivedTable);
2490                newTable.getLinkedColumns().addObjectName(pColumn);
2491                pColumn.setSourceTable(newTable);
2492                pColumn.setValidate_column_status(TBaseType.COLUMN_LINKED_TO_TABLE_IN_OLD_ALGORITHM);
2493                this.addToTables(newTable);
2494
2495                // 2024 年
2496                // 不能加入到 relations 中,否则会导致 下面 SQL 中 star column 同时链接到 SPCOMM.L_FIXED_RATE_PLAN_REF, ipshare_ofccplv.cprof_d_period_dates_ref
2497                // 从而导致 本来不该有的歧义产生
2498
2499                // UPDATE b_rate_plan
2500                //FROM
2501                //(
2502                //SELECT * FROM SPCOMM.L_FIXED_RATE_PLAN_REF
2503                //WHERE rate_plan_ref_eff_dt<= ipshare_ofccplv.cprof_d_period_dates_ref.PERIOD
2504                //) AS ref
2505                //SET accs_fee = REF.accs_fee,
2506                //SVC_TYPE = REF.prod_grp_lvl_1,
2507                //rate_plan_lvl3 = REF.rate_plan_lvl_3,
2508                //prod_grp_lvl3 = REF.prod_grp_lvl_2
2509                //WHERE b_rate_plan.svc_type IS NULL
2510
2511                // 2025/2/25, v3.0.4.8
2512                // 需要加入到 relations 中,新的 gudusoft.gsqlparser.resolver package 中的算法会处理这种情况
2513                // teradata 的隐式横向派生表不能加入到关系解析器中
2514                // this.getRelations().add(newTable);
2515            }
2516            return true;
2517        }
2518        if (pColumn.getCandidateTables().size() == 1){
2519            TTable table = pColumn.getCandidateTables().getTable(0);
2520            table.getLinkedColumns().addObjectName(pColumn);
2521            pColumn.setSourceTable(table);
2522            lcResult = true;
2523        }
2524        else if ((tables.size() == 1) || (pCandidateTableCnt == 1)){
2525            TTable table = tables.getTable(0);
2526
2527            if(table.getTableType() == ETableSource.function){
2528                //lcResult = linkToFunctionTable(table, pColumn);
2529                int iRet = table.getFuncCall().isColumnInThisTableFunction(this.getSqlEnv(),this.dbvendor,pColumn);
2530                if ( iRet == TBaseType.COLUMN_IN_TABEL_FUNCTION_YES){
2531                    lcResult = true;
2532                    table.getLinkedColumns().addObjectName(pColumn);
2533                    pColumn.setSourceTable(table);
2534                    lcResult = true;
2535                }else if ( iRet == TBaseType.COLUMN_IN_TABEL_FUNCTION_NO){
2536                    lcResult = false;
2537                }else{
2538                    table.getLinkedColumns().addObjectName(pColumn);
2539                    pColumn.setSourceTable(table);
2540                    lcResult = true;
2541                }
2542            }else if(table.getTableType() == ETableSource.subquery){
2543                if (! table.getSubquery().searchColumnInResultSet(pColumn,(tables.size()==1))){
2544                    getOrphanColumns().addObjectName(pColumn);
2545                    pColumn.setOrphanColumn(true);
2546                    pColumn.setOwnStmt(this);
2547                    TSourceToken st = pColumn.getStartToken();
2548                    if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){
2549                        TBaseType.log(String.format("Add orphan column <%s> to statement in old algorithm in subquery %s",pColumn.toString(),table.getAliasName()),TLog.WARNING,table);
2550                    }
2551                    this.parseerrormessagehandle(new TSyntaxError(st.getAstext(), st.lineNo, st.columnNo
2552                            ,"find orphan column", EErrorType.sphint
2553                            , TBaseType.MSG_HINT_FIND_ORPHAN_COLUMN,this,st.posinlist,pColumn));
2554                }
2555            }
2556            else{
2557                table.getLinkedColumns().addObjectName(pColumn);
2558                pColumn.setSourceTable(table);
2559                lcResult = true;
2560                if ((dbvendor == EDbVendor.dbvbigquery)&&(pCandidateTableCnt == 0) && (pColumn.isQualified())){
2561                    // bigquery struct column used in query
2562//                    create view test as (SELECT rollNo,
2563//                            info.name as n1,
2564//                    info2.name as n2,
2565//                            info.age from my_first_dataset.student_records);
2566
2567                    pColumn.columnToProperty();
2568                }
2569            }
2570        }else if (tables.size() > 1){
2571            // if there is only a table without table alias, then, link to this table
2572            boolean foundOnlyOneTable = false;
2573            TTable tableWithoutAlias = null;
2574            for(TTable table:tables){
2575                if (table.isCTEName()) continue; // CTE 即便没有 指定alias,也不作为考虑对象
2576                if (table.getAliasClause() == null){
2577                    tableWithoutAlias = table;
2578                    if (foundOnlyOneTable){
2579                        foundOnlyOneTable = false;
2580                        break;
2581                    }else{
2582                        foundOnlyOneTable = true;
2583                    }
2584                }
2585            }
2586
2587            if (foundOnlyOneTable){
2588                tableWithoutAlias.getLinkedColumns().addObjectName(pColumn);
2589                pColumn.setSourceTable(tableWithoutAlias);
2590                lcResult = true;
2591            }else{
2592                getOrphanColumns().addObjectName(pColumn);
2593                pColumn.setOrphanColumn(true);
2594                pColumn.setOwnStmt(this);
2595                TSourceToken st = pColumn.getStartToken();
2596                if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){
2597                    TBaseType.log(String.format("Add orphan column <%s> to statement in old algorithm ",pColumn.toString()),TLog.WARNING,this);
2598                }
2599
2600                this.parseerrormessagehandle(new TSyntaxError(st.getAstext(), st.lineNo, st.columnNo
2601                        ,"find orphan column", EErrorType.sphint
2602                        , TBaseType.MSG_HINT_FIND_ORPHAN_COLUMN,this,st.posinlist,pColumn));
2603            }
2604
2605        }
2606        return  lcResult;
2607    }
2608
2609    private boolean linkToFunctionTable(TTable table, TObjectName pColumn) {
2610        if(table.getTableName().toString().toUpperCase().equals("STRING_SPLIT")){
2611            if(pColumn.getColumnNameOnly().toUpperCase().equals("VALUE")){
2612                table.getLinkedColumns().addObjectName(pColumn);
2613                pColumn.setSourceTable(table);
2614                return true;
2615            }
2616            else return false;
2617        }
2618        else {
2619            table.getLinkedColumns().addObjectName(pColumn);
2620            pColumn.setSourceTable(table);
2621            return true;
2622        }
2623    }
2624
2625
2626    private TTable findInsertedOrDeleteTable(TTable table) {
2627        if (table == null) return null;
2628        
2629        // Check if this table is 'inserted' by examining both name and toString()
2630        // The inserted table might have different representations
2631        String tableName = table.getName();
2632        String tableString = table.toString();
2633        
2634        if ("inserted".equalsIgnoreCase(tableName) || 
2635            "inserted".equalsIgnoreCase(tableString) ||
2636            "deleted".equalsIgnoreCase(tableName) ||
2637            "deleted".equalsIgnoreCase(tableString)) {
2638            return table;
2639        }
2640        
2641        // If this is a join table, recursively check its components
2642        if (table.getTableType() == ETableSource.join && table.getJoinExpr() != null) {
2643            TJoinExpr joinExpr = table.getJoinExpr();
2644            
2645            // Check left side recursively
2646            TTable leftResult = findInsertedOrDeleteTable(joinExpr.getLeftTable());
2647            if (leftResult != null) {
2648                return leftResult;
2649            }
2650            
2651            // Check right side recursively  
2652            TTable rightResult = findInsertedOrDeleteTable(joinExpr.getRightTable());
2653            if (rightResult != null) {
2654                return rightResult;
2655            }
2656        }
2657        
2658        return null;
2659    }
2660   
2661
2662    boolean isSQLServerInsertedDelete(TObjectName pColumn){
2663        if (dbvendor != EDbVendor.dbvmssql) return false;
2664
2665        // only process sql server inserted delete column, if not then return false
2666       // if (!((pColumn.toString().toUpperCase().startsWith("INSERTED"))||(pColumn.toString().toUpperCase().startsWith("DELETED")))) return false;
2667        if (!((pColumn.getStartToken().tokencode == TBaseType.rrw_sqlserver_INSERTED )
2668                    ||(pColumn.getStartToken().tokencode == TBaseType.rrw_sqlserver_DELETED ))) return false;
2669
2670        // 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
2671        // to find the target table in the literal as 'inserted' or 'deleted'.
2672
2673        TTable lcTargetTable = null;
2674        for(TTable table : this.getRelations()){
2675            lcTargetTable = findInsertedOrDeleteTable(table);
2676            if (lcTargetTable != null){
2677                break;
2678            }
2679        }
2680
2681        if (lcTargetTable == null) return false;
2682
2683
2684        if (lcTargetTable.getLinkTable() != null){
2685            lcTargetTable.getLinkTable().getLinkedColumns().addObjectName(pColumn);
2686            pColumn.setSourceTable(lcTargetTable.getLinkTable());
2687
2688            pColumn.setResolveStatus(TBaseType.RESOLVED_AND_FOUND); // 避免在 // TAttributeResolver 中再次进行处理,关联到其他 table
2689
2690        }else{
2691            lcTargetTable.getLinkedColumns().addObjectName(pColumn);
2692            pColumn.setSourceTable(lcTargetTable);
2693            pColumn.setResolveStatus(TBaseType.RESOLVED_AND_FOUND); // 避免在 // TAttributeResolver 中再次进行处理,关联到其他 table
2694        }
2695        return true;
2696
2697    }
2698
2699    boolean isOracleNewOldTable(TObjectName pColumn){
2700        boolean ret = false;
2701        if (dbvendor != EDbVendor.dbvoracle) return false;
2702        if (!(pColumn.isQualified())) return false;
2703        if ((pColumn.getTableString().equalsIgnoreCase(":new"))
2704            ||(pColumn.getTableString().equalsIgnoreCase(":old"))){
2705            if (getAncestorStmt().tables != null){
2706                if (getAncestorStmt().tables.size() > 0){
2707                    getAncestorStmt().tables.getTable(0).getLinkedColumns().addObjectName(pColumn);
2708                    pColumn.setSourceTable(getAncestorStmt().tables.getTable(0));
2709                    ret = true;
2710                }
2711            }
2712        }
2713
2714        return ret;
2715    }
2716
2717    public boolean searchDaxVariableInStack(TObjectName pName){
2718        boolean ret = false;
2719        if (getVariableStack().size() == 0) return false;
2720        if (pName.getDbObjectType() == EDbObjectType.column) return false;
2721        for(int i=0;i<variableStack.size();i++){
2722            if (pName.toString().equalsIgnoreCase(((TObjectName) variableStack.get(i)).toString())){
2723                ret = true;
2724                break;
2725            }
2726        }
2727        return ret;
2728    }
2729
2730    boolean linkColumnToTableDax(TObjectName pColumn, ESqlClause pLocation){
2731        boolean lcResult = true ;
2732        TDaxFunction daxFunction = null;
2733        if (searchDaxVariableInStack(pColumn)) return false;
2734        if (getDaxFunctionStack().size() > 0){
2735            daxFunction = daxFunctionStack.peek();
2736        }
2737
2738        if (pColumn.getTableToken() != null){
2739            //TTable sourceTable = new TTable(new TObjectName(EDbObjectType.table,pColumn.getTableToken()));
2740            TTable sourceTable = new TTable(TObjectName.createObjectName (this.dbvendor,EDbObjectType.table,pColumn.getTableToken()));
2741            sourceTable.getLinkedColumns().addObjectName(pColumn);
2742            addToTables(sourceTable);
2743            if ((daxFunction != null) && (daxFunction.getDefaultTable() == null)){
2744                daxFunction.setDefaultTable(sourceTable);
2745            }
2746        }else{
2747            if ((daxFunction != null) &&(daxFunction.getDefaultTable() != null)){
2748                daxFunction.getDefaultTable().getLinkedColumns().addObjectName(pColumn);
2749            }else{
2750                ((TDaxStmt)this).getDefaultTable().getLinkedColumns().addObjectName(pColumn);
2751            }
2752        }
2753        return  lcResult;
2754    }
2755
2756    /**
2757     * 将列引用解析并绑定到其来源(表、子查询、CTE、表函数、OPENQUERY/UNNEST 等)。
2758     *
2759     * 功能概述:
2760     * 1) 针对 DAX 语法直接走 DAX 分支。
2761     * 2) 已绑定或标记“延迟到列解析器”的列直接返回。
2762     * 3) 设定列所在语法位置,并校验列名/保留字(含 MySQL true/false/default、内置函数等)。
2763     * 4) 处理厂商伪表/特殊前缀(Oracle :new/:old;SQL Server INSERTED/DELETED)。
2764     * 5) Insert All/VALUES 场景:优先在子查询结果集中/变量或过程参数中匹配。
2765     * 6) 在当前语句的 FROM 表集合中查找并建立绑定:
2766     *    - 限定列 table.col:按别名/表名匹配;对子查询/CTE/OPENQUERY 进一步在结果集中定位源列;
2767     *      命中后写入 linkedColumns,必要时将 TableToken 标记为 subquery_alias。
2768     *    - 非限定列 col:
2769     *      a. 先尝试同层 SELECT 列别名(支持 LATERAL 语义且位置在别名之后);
2770     *      b. 处理通配符“*”:收集所有来源表;
2771     *      c. 基础表通过元数据回调 fireOnMetaDatabaseTableColumn 校验;未命中则记录候选;
2772     *      d. 子查询/CTE/函数/UNNEST/PIVOT 分别按各自规则匹配。
2773     * 7) 命中后将列加入表的 linkedColumns 并设置 sourceTable/sourceColumn,必要时维持 isContinue 以继续匹配“*”。
2774     * 8) 若未命中:尝试变量/参数;再按条件(语句类型/位置/是否限定等)向上一层语句递归查找(维护 searchLevel)。
2775     * 9) 仍未命中:在顶层(searchLevel==0)按“候选唯一/或首表”兜底策略 {@link #linkToFirstTable(TObjectName, int)}。
2776     *
2777     * 参数:
2778     * @param pColumn   需要绑定的列名对象(方法会更新其 location、sourceTable、sourceColumn 等)
2779     * @param pLocation 列出现的语法位置(如 selectList、where、insertValues 等)
2780     *
2781     * 返回值:
2782     * @return 成功绑定到某个来源返回 true;未能绑定或被识别为变量/保留字等返回 false
2783     *
2784     * 厂商兼容:
2785     * - Oracle: 处理 :new/:old,Insert All 的 values 子句源自子查询的匹配
2786     * - SQL Server: 处理 INSERTED/DELETED 伪表
2787     * - MySQL: 对保留字/布尔字面量/内置函数名的特殊判断
2788     * - DAX: 委托 {@link #linkColumnToTableDax(TObjectName, ESqlClause)}
2789     *
2790     * 副作用:
2791     * - 更新 pColumn 的 location/searchLevel/sourceTable/sourceColumn/validate 状态
2792     * - 向命中的表写入 linkedColumns 或向别名列写入 targetColumns
2793     * - 对“*”列填充 sourceTableList;对子查询命中时可能将 TableToken 标为 subquery_alias
2794     * - 记录候选表数量并填充 pColumn.candidateTables,用于后续兜底
2795     *
2796     * 复杂度与顺序:
2797     * - 优先使用同层信息(别名/元数据/子查询结果),再逐层向外查找;避免无谓的上层搜索
2798     *
2799     * 注意:
2800     * - 本方法完成“旧算法”的快速联接,新的解析/消歧逻辑在解析器(如 TStmtScope/TAttributeResolver)中继续处理
2801     */
2802    public boolean linkColumnToTable(TObjectName pColumn, ESqlClause pLocation){
2803        boolean lcResult = false,isContinue = false;
2804        int candidateTableCnt = 0;
2805        if (pColumn == null) return false;
2806        if (dbvendor == EDbVendor.dbvdax){
2807            return linkColumnToTableDax(pColumn,pLocation);
2808        }
2809
2810        // Skip alias definition columns - they define column names in alias clauses, not column references
2811        // Example: In "AS x (numbers, animals)", numbers and animals are column_alias type
2812        if (pColumn.getDbObjectType() == EDbObjectType.column_alias) {
2813            return true;
2814        }
2815
2816        if (pColumn.getSourceTable() != null) {
2817            lcResult = true;
2818            return lcResult;
2819        }
2820
2821        if (pColumn.getResolveStatus() == TBaseType.RESOLVE_DELAY_TO_COLUMN_RESOLVER) return true;
2822
2823        pColumn.setLocation(pLocation);
2824
2825        if (! pColumn.isValidColumnName(dbvendor)) {
2826            if (pColumn.isReservedKeyword()){
2827                if (
2828                        ((pColumn.getStartToken().tokencode != TBaseType.rrw_mysql_true)&&(!(pColumn.getStartToken().toString().equalsIgnoreCase("true"))))
2829                        &&((pColumn.getStartToken().tokencode != TBaseType.rrw_mysql_false)&&(!(pColumn.getStartToken().toString().equalsIgnoreCase("false"))))
2830                        &&(pColumn.getStartToken().tokencode != TBaseType.rrw_mysql_default)
2831                   ) {
2832                        boolean mysqlBuiltFunction = false;
2833                        if (dbvendor == EDbVendor.dbvmysql){
2834                            mysqlBuiltFunction = functionChecker.isBuiltInFunction(pColumn.toString(),EDbVendor.dbvmysql,"6.0");
2835                        }
2836                        if (!mysqlBuiltFunction){
2837                            TSourceToken st1 = pColumn.getStartToken();
2838                            TSyntaxError err = new TSyntaxError(st1.toString()
2839                                    , st1.lineNo, st1.columnNo
2840                                    , String.format("Reserved keyword can't be column name")
2841                                    , EErrorType.sperror
2842                                    , TBaseType.MSG_ERROR_RESERVED_KEYWORD_CANT_USED_AS_COLUMN_NAME
2843                            ,this,st1.posinlist);
2844                            this.parseerrormessagehandle(err);
2845                        }
2846                }
2847            }
2848            return false;
2849        }
2850
2851        if (isOracleNewOldTable(pColumn)) return true;
2852        if (isSQLServerInsertedDelete(pColumn)) return true;
2853
2854        // oracle insert all statement,
2855        // WHEN id <= 3 THEN INTO dest_tab1 VALUES(id, description1)
2856        // column in values clause must be in the subquery of insert all statement
2857        if (pColumn.getLocation() == ESqlClause.insertValues){
2858            if (this instanceof TInsertSqlStatement){
2859                TInsertSqlStatement insertSqlStatement = (TInsertSqlStatement)this;
2860                if (insertSqlStatement.isInsertAll()){
2861                   // if (pColumn.getStartToken().tokencode == TBaseType.rrw_snowflake_default) return true;
2862                    lcResult = insertSqlStatement.getSubQuery().searchColumnInResultSet(pColumn, true);
2863                }
2864            }
2865
2866            if (lcResult) return true;
2867
2868            // value in values clause maybe parameter of the procedure/function parameter
2869            lcResult = locateVariableOrParameter(pColumn,true);
2870            if (lcResult) return true;
2871        }
2872
2873        boolean foundInMetaData = false;
2874
2875        for(int i=0;i<tables.size();i++){
2876            TTable lcTable = tables.getTable(i);
2877            if (lcTable.getEffectType() == ETableEffectType.tetSelectInto) continue;
2878            if (lcTable.getEffectType() == ETableEffectType.tetImplicitLateralDerivedTable) continue;
2879
2880            if (pColumn.isQualified()){
2881                lcResult = pColumn.resolveWithThisTable(lcTable);
2882                if ((lcResult) && (lcTable.getTableType() == ETableSource.subquery)){
2883                    pColumn.getTableToken().setDbObjectType(EDbObjectType.subquery_alias);
2884
2885                    int lcPos = lcTable.searchColumnInAlias(pColumn);
2886                    lcResult = lcPos>=0;
2887                    if (lcResult){
2888                        // 在 alias 中找到 source column, 还需要对应到 subquery select 中的 select list
2889                        // sql 见 https://e.gitee.com/gudusoft/projects/151613/tasks/list?issue=I8JR0W#note_23051633
2890                        if ((lcTable.getSubquery() != null)&&(lcTable.getSubquery().getResultColumnList() != null)){
2891                            pColumn.setSourceColumn(lcTable.getSubquery().getResultColumnList().getResultColumn(lcPos));
2892                        }
2893                    }else{
2894                        lcResult =  lcTable.getSubquery().searchColumnInResultSet(pColumn,true);
2895                    }
2896               //     lcTable.getSubquery().searchColumnInResultSet(pColumn,true);
2897
2898                }else if ((lcResult) && (lcTable.isCTEName())){
2899                    lcTable.getCTE().searchColumnInResultSet(this,lcTable,pColumn,true);
2900                }else if ((lcResult) && (lcTable.getTableType() == ETableSource.openquery)&&(lcTable.getSubquery() != null)){
2901                    pColumn.getTableToken().setDbObjectType(EDbObjectType.subquery_alias);
2902                    lcTable.getSubquery().searchColumnInResultSet(pColumn,true);
2903//                }else if ((lcResult) && (lcTable.getTableType() == ETableSource.unnest)&&(lcTable.getUnnestClause() != null)){
2904//                    pColumn.getTableToken().setDbObjectType(EDbObjectType.subquery_alias);
2905//                    lcTable.getSubquery().searchColumnInResultSet(pColumn,true);
2906                }
2907                if (lcResult&&pColumn.toString().endsWith("*")){
2908                    pColumn.getSourceTableList().add(lcTable);
2909//                    ArrayList<String> lcColumns = getColumnsInTable(lcTable);
2910//                    if (lcColumns != null){
2911//                        pColumn.getColumnsLinkedToStarColumn().addAll(lcColumns);
2912//                    }
2913                }
2914            }else {
2915              // column not qualified
2916
2917                // check if this is the column alias in current select list.
2918                if((!lcResult)&& ((!pColumn.isQualified()) && (this instanceof TSelectSqlStatement)&&(getResultColumnList() !=null))
2919                        && (TBaseType.isSupportLateralColumn(dbvendor))
2920                ){
2921                    for(int j=0;j<getResultColumnList().size();j++){
2922                        TResultColumn lcField = getResultColumnList().getResultColumn(j);
2923                        lcResult = lcField.isMatchedUsingAlias(pColumn);
2924                        if (
2925                                 (!TSQLEnv.isAliasReferenceForbidden.get(this.dbvendor))&&
2926                                ((lcResult)&&(pColumn.getStartToken().posinlist > lcField.getAliasClause().getStartToken().posinlist))){
2927                            pColumn.setSourceColumn(lcField);
2928                            lcField.getTargetColumns().addObjectName(pColumn);
2929                            pColumn.setValidate_column_status(TBaseType.COLUMN_LINKED_TO_COLUMN_ALIAS_IN_OLD_ALGORITHM);
2930                            break;
2931                        }else{
2932                            lcResult = false;
2933                        }
2934                    }
2935
2936                    if (lcResult) return true;
2937                }
2938
2939                if (pColumn.getColumnNameOnly().equalsIgnoreCase("*")){
2940                    lcResult = true;
2941                    isContinue = true; // in order to match next table in the from clause
2942                    pColumn.getSourceTableList().add(lcTable);
2943//                    ArrayList<String> lcColumns = getColumnsInTable(lcTable);
2944//                    if (lcColumns != null){
2945//                        pColumn.getColumnsLinkedToStarColumn().addAll(lcColumns);
2946//                    }
2947                }else if (lcTable.isBaseTable()){
2948                    lcResult = fireOnMetaDatabaseTableColumn(
2949                                         lcTable.getPrefixServer()
2950                                        ,lcTable.getPrefixDatabase()
2951                                        ,lcTable.getPrefixSchema()
2952                                        ,lcTable.getName()
2953                                        ,pColumn.getColumnNameOnly());
2954                    if (! lcResult) {
2955                        candidateTableCnt++;
2956                        pColumn.getCandidateTables().addTable(lcTable);
2957                    }else{
2958                        foundInMetaData = true;
2959                        isContinue = false;
2960                    }
2961
2962                }else if ((lcTable.getTableType() == ETableSource.subquery)
2963                            ||((lcTable.getTableType() == ETableSource.openquery)&&(lcTable.getSubquery() != null))){
2964
2965                    lcResult = lcTable.searchColumnInAlias(pColumn)>=0;
2966                    if (!lcResult){
2967                        lcResult = lcTable.getSubquery().searchColumnInResultSet(pColumn,(tables.size() == 1)
2968                                &&(pColumn.getCandidateTables().size() == 0));
2969                        if (! lcResult) {
2970                            candidateTableCnt++;
2971                            pColumn.getCandidateTables().addTable(lcTable);
2972                        }
2973                    }
2974
2975
2976//                    if (lcTable.isIncludeColumnAlias()){
2977//                       // System.out.println("subquery with alias:"+lcTable.getAliasClause().toString()+", skip search:"+pColumn.toString());
2978//
2979//                    }else{
2980//                        lcResult = lcTable.getSubquery().searchColumnInResultSet(pColumn,(tables.size() == 1)&&(pColumn.getCandidateTables().size() == 0));
2981//                        if (! lcResult) candidateTableCnt++;
2982//                    }
2983                }else  if (lcTable.isCTEName()){
2984                    lcResult = lcTable.getCTE().searchColumnInResultSet(this,lcTable,pColumn,tables.size() == 1);
2985                    if (! lcResult) {
2986                        candidateTableCnt++;
2987                        pColumn.getCandidateTables().addTable(lcTable);
2988                    }
2989                }else if (lcTable.getTableType() == ETableSource.function){
2990                    //  search in this table function
2991                        if(tables.size() == 1){
2992                                lcResult = ( lcTable.getFuncCall().isColumnInThisTableFunction(this.getSqlEnv(),this.dbvendor,pColumn)
2993                                != TBaseType.COLUMN_IN_TABEL_FUNCTION_NO);
2994                        }
2995                        else{
2996                                lcResult = ( lcTable.getFuncCall().isColumnInThisTableFunction(this.getSqlEnv(),this.dbvendor,pColumn)
2997                                                                        == TBaseType.COLUMN_IN_TABEL_FUNCTION_YES);
2998                        }
2999                }else if (lcTable.getTableType() == ETableSource.tableExpr 
3000                                && lcTable.getTableExpr().getExpressionType() == EExpressionType.function_t
3001                                && lcTable.getTableExpr().getFunctionCall() != null){
3002                    //  search in this table function
3003                    lcResult = ( lcTable.getTableExpr().getFunctionCall().isColumnInThisTableFunction(this.getSqlEnv(),this.dbvendor,pColumn)
3004                                                                        == TBaseType.COLUMN_IN_TABEL_FUNCTION_YES);
3005                }else if (lcTable.getTableType() == ETableSource.pivoted_table){
3006                    lcResult = fireOnMetaDatabaseTableColumn(
3007                            lcTable.getPrefixServer()
3008                            ,lcTable.getPrefixDatabase()
3009                            ,lcTable.getPrefixSchema()
3010                            ,lcTable.getName()
3011                            ,pColumn.getColumnNameOnly());
3012                    if (lcResult){
3013                            foundInMetaData = true;
3014                            isContinue = false;
3015                    }
3016                }else if (lcTable.getTableType() == ETableSource.unnest){
3017                    for(TObjectName objectName:lcTable.getLinkedColumns()){
3018                        if (objectName.toString().equalsIgnoreCase(pColumn.toString())){
3019                            lcResult = true;
3020                            break;
3021                        }
3022                    }
3023
3024                    if (!lcResult){
3025                        if (lcTable.getAliasClause() == null){
3026                            // this unnest() clause generate column with default name: "value"
3027                            if (pColumn.toString().equalsIgnoreCase("value")){
3028                                lcResult = true;
3029                            }
3030                        }else{
3031                        }
3032                    }
3033                }//unnest
3034            }
3035
3036            if (lcResult) {
3037                lcTable.getLinkedColumns().addObjectName(pColumn);
3038                pColumn.setSourceTable(lcTable);
3039               // pColumn.setValidate_column_status(TBaseType.COLUMN_LINKED_TO_TABLE_IN_OLD_ALGORITHM);
3040                if (!isContinue) break;
3041            }
3042        }
3043
3044        if ((lcResult) && (foundInMetaData)) return true;
3045
3046        // check variable after metadata checking
3047        if (locateVariableOrParameter(pColumn)) return false;
3048
3049        // check if this is the column alias in current select list.
3050//        if((!lcResult)&& ((!pColumn.isPrefixed()) && (this instanceof TSelectSqlStatement)&&(getResultColumnList() !=null))){
3051//            for(int j=0;j<getResultColumnList().size();j++){
3052//                TResultColumn lcField = getResultColumnList().getResultColumn(j);
3053//                lcResult = lcField.isMatchedUsingAlias(pColumn);
3054//                if ((lcResult)&&(pColumn.getStartToken().posinlist > lcField.getAliasClause().getStartToken().posinlist)){
3055//                    pColumn.setSourceColumn(lcField);
3056//                    lcField.getTargetColumns().addObjectName(pColumn);
3057//                    break;
3058//                }else{
3059//                    lcResult = false;
3060//                }
3061//            }
3062//        }
3063
3064        if (lcResult) return true;
3065
3066        boolean isSearchUpLevel = (this.parentStmt != null);
3067
3068        if ((isSearchUpLevel) && (sqlstatementtype == ESqlStatementType.sstselect)){
3069            isSearchUpLevel = (pColumn.isQualified()
3070                                || (
3071//                                        (((TSelectSqlStatement)(this)).getLocation() != ESqlClause.elTable) &&
3072                                         (! ((TSelectSqlStatement)(this)).isQueryOfCTE())
3073                                    )
3074                               )
3075                            && (parentStmt.sqlstatementtype != ESqlStatementType.sstinsert)
3076                            && (!((pColumn.getLocation() == ESqlClause.selectList)&&(((TSelectSqlStatement)(this)).getLocation() == ESqlClause.join)))
3077                            && ((((TSelectSqlStatement)(this)).getLocation() != ESqlClause.pivot_in))
3078                            && (!((parentStmt.sqlstatementtype == ESqlStatementType.sstcreatetable)))
3079                            && (!((parentStmt.sqlstatementtype == ESqlStatementType.sstcreateview)))
3080//                            && (!((pColumn.getLocation() == ESqlClause.selectList)&&(parentStmt.sqlstatementtype == ESqlStatementType.sstcreatetable)))
3081//                            && (!((pColumn.getLocation() == ESqlClause.selectList)&&(parentStmt.sqlstatementtype == ESqlStatementType.sstcreateview)))
3082                            && (! ((pColumn.getLocation() == ESqlClause.selectList)
3083                                    &&(candidateTableCnt == 1) && (this instanceof TSelectSqlStatement)
3084                                    && (((TSelectSqlStatement)(this)).getLocation() == ESqlClause.elTable)
3085                                    ) ) // ref:mantis: #2628
3086                           // && ( ((TSelectSqlStatement)(this.parentStmt)).getSetOperatorType() == ESetOperatorType.none)
3087            ;
3088
3089            if (isSearchUpLevel){
3090                isSearchUpLevel = !((!pColumn.isQualified())&&(((TSelectSqlStatement) this).getLocation() == ESqlClause.where));
3091            }
3092        }
3093
3094        if (isSearchUpLevel&&(pColumn.isContinueToSearch())){ // only search one level up, c:\prg\gsp_sqlfiles\TestCases\java\oracle\dbobject\berger_sqltest_04.sql
3095            boolean increaseLevel = true;
3096            if (parentStmt instanceof TSelectSqlStatement){
3097                if( ((TSelectSqlStatement)parentStmt).getSetOperatorType() != ESetOperatorType.none){
3098                   increaseLevel = false;
3099                }
3100            }
3101            if (increaseLevel){
3102                pColumn.searchLevel++;
3103            }
3104
3105            lcResult = parentStmt.linkColumnToTable(pColumn,pLocation);
3106
3107            if (increaseLevel){
3108                pColumn.searchLevel--;
3109            }
3110        }
3111
3112        if ((! lcResult) && (pColumn.searchLevel == 0)) {
3113            if (this.sqlstatementtype == ESqlStatementType.sstselect){
3114                if( ((TSelectSqlStatement)this).getSetOperatorType() == ESetOperatorType.none){
3115                    //                    USING _spVV0 (INTEGER)
3116                    //                            INSERT INTO table3
3117                    //                    SELECT :_spVV0,x. *,m.col3
3118                    //                    from ((           select table1.col1, (table1.col1 + table5.col2) c from table1
3119                    //                            union all select col3,col4 from table2) x
3120                    //                    cross join (select id from table2) m )
3121
3122                    // table5 in the above sql only link to the nearest level sql, but not to up-level which is union all
3123
3124                    linkToFirstTable(pColumn,candidateTableCnt);
3125                }
3126            }else{
3127                linkToFirstTable(pColumn,candidateTableCnt);
3128            }
3129        }
3130
3131        return lcResult;
3132    }
3133
3134
3135    /**
3136     *
3137     * @deprecated As of v1.6.0.1, use  {@link #linkColumnToTable} instead
3138     */
3139    public void linkColumnReferenceToTable(TObjectName cr, ESqlClause plocation){
3140        // this is the column name, link it to table
3141        if (cr == null) return;
3142        cr.setLocation(plocation);
3143        if (cr.getObjectType() == TObjectName.ttobjVariable) return;
3144        if (cr.getObjectType() == TObjectName.ttobjColumnAlias) return;
3145        if (this.dbvendor == EDbVendor.dbvsybase){
3146            TSourceToken pt = cr.getPartToken();
3147            if ( pt != null){
3148                if (pt.tokentype == ETokenType.ttdqstring){
3149                //"0123", quoted string start with a number can't a column
3150                    if ((pt.toString().charAt(1) >= '0')
3151                        &&(pt.toString().charAt(1) <= '9')){
3152                        return;
3153                    }else if (pt.toString().length() == 2){
3154                        //"", empty
3155                        return;
3156                    }else if (pt.toString().substring(1,pt.toString().length()-1).trim().length() == 0){
3157                        //"  "
3158                        return;
3159                    }
3160                }
3161            }
3162        }
3163
3164
3165        if (cr.getPartToken() != null){
3166            if (cr.getPartToken().tokentype == ETokenType.ttkeyword){
3167                boolean reservedKeyword = false;
3168                switch (dbvendor){
3169                    case dbvmssql:
3170                        //reservedKeyword = ! this.getGsqlparser().getFlexer().canBeColumnName(cr.getPartToken().tokencode);
3171                        reservedKeyword = ! TLexerMssql.canBeColumnName(cr.getPartToken().tokencode);
3172                        break;
3173                    case dbvsybase:
3174                        reservedKeyword = keywordChecker.isKeyword(cr.getPartToken().toString(), EDbVendor.dbvsybase, "15.7", true);
3175                        break;
3176                    default:
3177                        break;
3178                }
3179                if (reservedKeyword) return;
3180            }
3181        }
3182
3183        // let's check is this columnreference is variable or parameter of plsql function/procedure
3184      //  if (locateVariableOrParameter(cr)) return;
3185
3186//        if ((cr.getPartToken() != null)&&((dbvendor == EDbVendor.dbvmssql)||(dbvendor == EDbVendor.dbvsybase))){
3187//            if ((cr.getPartToken().tokentype == ETokenType.ttkeyword)&&(!(this.getGsqlparser().getFlexer().canBeColumnName(cr.getPartToken().tokencode)))){
3188//                // keyword can't be column name:
3189//                //select * From dbo.table Where DATEDIFF(day, create_date, expiry_date) < 14
3190//               return;
3191//            }
3192//        }
3193
3194        if ((cr.toString().startsWith("@")))
3195//            if ((cr.toString().endsWith("*"))||(cr.toString().startsWith("@")))
3196        {
3197            cr.setObjectType(TObjectName.ttobjNotAObject);
3198            return;
3199        }
3200
3201        if (dbvendor == EDbVendor.dbvoracle){
3202            if ( //(cr.toString().compareToIgnoreCase ("rowid") == 0)||
3203                    (cr.toString().compareToIgnoreCase ("sysdate") == 0)
3204            || (cr.toString().compareToIgnoreCase ("nextval") == 0)
3205            || (cr.toString().compareToIgnoreCase ("rownum") == 0)
3206            || (cr.toString().compareToIgnoreCase ("level") == 0)
3207                    ){
3208                cr.setObjectType(TObjectName.ttobjNotAObject);
3209                if (cr.getDbObjectType() == EDbObjectType.unknown){
3210                    cr.setDbObjectType(EDbObjectType.notAColumn);
3211                }
3212                return;
3213            }
3214        }
3215
3216        if (((cr.toString().toUpperCase().startsWith("INSERTED"))||(cr.toString().toUpperCase().startsWith("DELETED")))&&(plocation == ESqlClause.output)&&(targetTable != null)){
3217            targetTable.getObjectNameReferences().addObjectName(cr);
3218            return;
3219        }
3220
3221        if ( ((cr.toString().toUpperCase().startsWith(":NEW"))
3222              ||(cr.toString().toUpperCase().startsWith(":OLD")))
3223             &&(this.getTopStatement() instanceof TPlsqlCreateTrigger)
3224             &&(dbvendor == EDbVendor.dbvoracle)){
3225             this.getTopStatement().tables.getTable(0).getObjectNameReferences().addObjectName(cr);
3226            return;
3227        }
3228
3229
3230
3231        int ret = this.tables.checkColumnReferenceInTables(cr);
3232         if (ret >= 0) {
3233             TTable lcTable = this.tables.getTable(ret);
3234             if (lcTable.isBaseTable()){
3235                lcTable.getObjectNameReferences().addObjectName(cr);
3236             }else if (lcTable.isCTEName()){
3237                //WITH temp
3238                //     AS (SELECT *
3239                //         FROM   sysibm.systables),
3240                //     temp1
3241                //     AS (SELECT *
3242                //         FROM   sysibm.syscolumns)
3243                //SELECT *
3244                //FROM   temp A
3245                //       INNER JOIN temp1 B
3246                //               ON A.creator = B.tbcreator
3247                //                  AND A.name = B.tbname
3248                 TCTE lccte = findCTEByName(lcTable.toString());
3249                 if (lccte != null){
3250                     TObjectName objectName = new TObjectName();
3251                     objectName.init(cr.getPartToken());
3252                     if (lccte.getSubquery() != null){
3253                         lccte.getSubquery().linkColumnReferenceToTable(objectName,plocation);
3254                     }
3255                 }
3256             }else if (lcTable.getTableType() == ETableSource.subquery){
3257                // link s2t1a1 to  subselect2table1 via s2
3258                //select
3259                //       s2.s2t1a1
3260                //from
3261                //    (
3262                //       select s2t1.*
3263                //          from subselect2table1 s2t1
3264                //    ) s2
3265               TSelectSqlStatement subquery = lcTable.getSubquery();
3266
3267                 if(((subquery.getValueClause() == null))&&(!subquery.isCombinedQuery())&&(subquery.getResultColumnList() != null)&&(subquery.getResultColumnList().size() == 1)){
3268                     TResultColumn lcColumn = subquery.getResultColumnList().getResultColumn(0);
3269                     if (lcColumn.toString().endsWith("*")){
3270                        boolean isfound = false;
3271
3272                        for(int i=0;i<subquery.tables.size();i++){
3273                            if (subquery.tables.getTable(i).getTableType() == ETableSource.subquery) continue;
3274                            String columnStr = null;
3275                            if (cr.getPartToken() != null){
3276                                //cr.getObjectType() is not ttObjColumn, so we can't use
3277                                // getColumnToken, this is a bug, need to check it later.
3278                                columnStr = cr.getPartToken().toString();
3279                            }
3280                            if (this.fireOnMetaDatabaseTableColumn(
3281                                    subquery.tables.getTable(i).getTableName().getServerString(),
3282                                    subquery.tables.getTable(i).getTableName().getDatabaseString(),
3283                                    subquery.tables.getTable(i).getTableName().getSchemaString(),
3284                                    subquery.tables.getTable(i).getName(),columnStr)){
3285                                subquery.tables.getTable(i).getObjectNameReferences().addObjectName(cr);
3286                                isfound = true;
3287                                break;
3288                            }
3289                        }
3290
3291
3292
3293                         if (!isfound)
3294                         {
3295                             if(subquery.tables.size() > 1){
3296                                 cr.setTableDetermined(false);
3297                             }
3298                           for(int i=0;i<subquery.tables.size();i++){
3299                             subquery.tables.getTable(i).getObjectNameReferences().addObjectName(cr);
3300                           }
3301                         }
3302
3303                     } // "*"
3304                 }
3305             }
3306         }else if (ret == -2){
3307           // no qualifier before column, check is this column of a cte, if not,set it to non-cte table
3308           boolean isfound = false;
3309             for (int i=0;i<this.tables.size();i++){
3310                 if ((this.tables.getTable(i).isCTEName()) &&(this.tables.getTable(i).getCteColomnReferences() != null)){
3311                     if (this.tables.getTable(i).getCteColomnReferences().searchColumnReference(cr) >= 0){
3312                        this.tables.getTable(i).getObjectNameReferences().addObjectName(cr);
3313                         isfound = true;
3314                         break;
3315                     }
3316                 }
3317             }
3318
3319           // no qualifier before column, but we still need to check uplevel table like this:
3320            //SELECT
3321            //       col1 ,
3322            //
3323            //           (    SELECT col2
3324            //                FROM tab1
3325            //                WHERE col2 = col1      )
3326            //   FROM tab2
3327           // we need to link col1 to tab2 in up level, but not to tab1
3328            if ((!isfound) &&(
3329                            (cr.getLocation() != ESqlClause.resultColumn)
3330                          &&(cr.getLocation() != ESqlClause.insertColumn)
3331                                    &&(cr.getLocation() != ESqlClause.mergeInsert)
3332                                    &&(cr.getLocation() != ESqlClause.selectList)
3333            ) ){  // code #111
3334                TCustomSqlStatement lcParent = null;
3335                lcParent = this.getParentStmt();
3336               while ( lcParent != null) {
3337                   TTable lcTable;
3338                 //ret = lcParent.tables.checkColumnReferenceInTables(cr);
3339                   if (lcParent.sqlstatementtype != ESqlStatementType.sstselect) {
3340                       break;
3341                   }
3342                   for (int i=0;i<lcParent.tables.size();i++){
3343                       lcTable = lcParent.tables.getTable(i);
3344                       if (lcTable.getTableType() == ETableSource.objectname) {
3345                           for(int k = 0; k< lcTable.getObjectNameReferences().size();k++){
3346                               if (lcTable.getObjectNameReferences().getObjectName(k).isTableDetermined()){
3347                                   if (cr.toString().equalsIgnoreCase(lcTable.getObjectNameReferences().getObjectName(k).toString())){
3348                                      isfound = true;
3349                                       break;
3350                                   }
3351                               }
3352                           }
3353                       if (isfound) break;
3354                       }
3355                   }
3356
3357                 if (isfound){
3358                     break;
3359                 }else{
3360                     lcParent = lcParent.getParentStmt();
3361                 }
3362               } // while
3363
3364             } // end of code #111
3365
3366            if (!isfound){
3367                isfound = checkNonQualifiedColumnReferenceInSubQueryOfUplevelStmt(cr
3368                        , ((plocation == ESqlClause.resultColumn)
3369                            ||(plocation == ESqlClause.insertColumn)
3370                                ||(plocation == ESqlClause.mergeInsert)
3371                                ||(plocation == ESqlClause.selectList)
3372                        )
3373                );
3374            }
3375
3376             if ((!isfound)&&(this.tables.size() > 0)){
3377                 int candidate = 0, firstCandidate = -1;
3378                 // add this column reference to first non-cte( or cte with column list is null) and non-subquery table
3379                 for (int i=0;i<this.tables.size();i++){
3380                     // 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
3381                     //if (this.tables.getTable(i).aliasClause != null) continue;
3382                     if (
3383                             (
3384                                     (!this.tables.getTable(i).isCTEName())
3385                                ||((this.tables.getTable(i).isCTEName())&&(this.tables.getTable(i).getCteColomnReferences() == null))
3386                             )&&((this.tables.getTable(i).getTableType() != ETableSource.subquery))
3387                     )
3388                     {
3389                         candidate++;
3390                         if (firstCandidate == -1) firstCandidate = i;
3391                         if (this.fireOnMetaDatabaseTableColumn(
3392                                    this.tables.getTable(i).getTableName().getServerString(),
3393                                    this.tables.getTable(i).getTableName().getDatabaseString(),
3394                                    this.tables.getTable(i).getTableName().getSchemaString(),
3395                                    this.tables.getTable(i).getName(),cr.toString())){
3396                                this.tables.getTable(i).getObjectNameReferences().addObjectName(cr);
3397                                isfound = true;
3398                                break;
3399                            }
3400                         else{
3401                             this.tables.getTable(i).getObjectNameReferences().addObjectName(cr);
3402                             if (this.tables.size() > 1){
3403                                 cr.setTableDetermined(false);
3404                             }
3405                             isfound = true;
3406                             break;
3407                         }
3408                     }
3409                  }
3410                 if ((!isfound) && (candidate == 1)){
3411                     this.tables.getTable(firstCandidate).getObjectNameReferences().addObjectName(cr);
3412                 }
3413             }
3414         }else if (ret == -1){
3415             TCustomSqlStatement lcParent = null;
3416             lcParent = this.getParentStmt();
3417            while ( lcParent != null) {
3418              ret = lcParent.tables.checkColumnReferenceInTables(cr);
3419              if (ret >= 0){
3420               lcParent.tables.getTable(ret).getObjectNameReferences().addObjectName(cr);
3421               break;
3422              }else{
3423                  lcParent = lcParent.getParentStmt();
3424              }
3425            } // while
3426         } //-1
3427
3428    }
3429
3430    /**
3431     * Found out is a non qualified column is a column in uplevel subquery table like this:
3432     * take ma_parkey for example: ma_parkey is not a physical column
3433     * 
3434        SELECT c_mandant
3435             , CASE WHEN EXISTS (SELECT 1
3436                                   FROM CDS_H_GRUPPE  GRP1
3437                                  WHERE GRP1.c_mandant = c_mandant
3438                                    AND GRP1.parkey1       = ma_parkey)
3439                      THEN 1
3440                  ELSE NULL
3441               END MA_ME
3442          FROM (SELECT c_mandant
3443                     , CASE WHEN funktionscode = 'U'
3444                              THEN parkey1
3445                          ELSE parkey2
3446                       END MA_PARKEY
3447                  FROM
3448                       CDS_H_GRUPPE
3449               )
3450     */
3451    public boolean checkNonQualifiedColumnReferenceInSubQueryOfUplevelStmt(TObjectName crf,boolean sameLevelOnly){
3452        boolean ret = false;
3453
3454        TCustomSqlStatement lcParent = null;
3455        lcParent = this;//getParentStmt();
3456       while ( lcParent != null) {
3457           TTable lcTable;
3458             for (int i=0;i<lcParent.tables.size();i++){
3459                 lcTable = lcParent.tables.getTable(i);
3460
3461                 if ((lcTable.getTableType() != ETableSource.subquery)) {continue;}
3462
3463                 ret = isColumnNameInSelectList(crf.toString(),lcTable.subquery);
3464                if (ret) {break;}
3465
3466             }
3467          if (ret) {break;}
3468           else{
3469              if (sameLevelOnly){
3470                  lcParent = null;
3471              }else{
3472                lcParent = lcParent.getParentStmt();
3473              }
3474          }
3475       } // while
3476
3477        return ret;
3478    }
3479
3480    private boolean isColumnNameInSelectList(String pColumn, TSelectSqlStatement pSelect){
3481        boolean ret = false;
3482        TResultColumn lcColumn;
3483        if (pSelect.isCombinedQuery()){
3484            ret = isColumnNameInSelectList(pColumn,pSelect.getLeftStmt());
3485            if (!ret){
3486                ret = isColumnNameInSelectList(pColumn,pSelect.getRightStmt());
3487            }
3488        }else{
3489            if (pSelect.getResultColumnList() != null){ //if it's a db2 value row, then pSelect.getResultColumnList() will be null 
3490                for(int j=0;j<pSelect.getResultColumnList().size();j++){
3491                   lcColumn = pSelect.getResultColumnList().getResultColumn(j);
3492                   if (lcColumn.getAliasClause() != null){
3493                       ret = pColumn.equalsIgnoreCase(lcColumn.getAliasClause().toString());
3494                   }
3495                   if (ret)  break;
3496                   ret = pColumn.equalsIgnoreCase(lcColumn.getExpr().toString());
3497                   if (ret)  break;
3498                }
3499            }
3500        }
3501        return ret;
3502    }
3503
3504    public TCustomSqlStatement getTopStatement(){
3505        TCustomSqlStatement ret = this;
3506        while (ret.getParentStmt() != null){
3507            ret = ret.getParentStmt();
3508        }
3509        return ret;
3510    }
3511
3512
3513//    public String toScript(){
3514//        if (!isChanged()){
3515//            return this.toString();
3516//        }
3517//        return super.toScript();
3518//    }
3519
3520}
3521
3522class constantVisitor extends TParseTreeVisitor {
3523    private boolean inWhere = false,inExprList = false;
3524    public void preVisit(TWhereClause node){
3525        inWhere = true;
3526    }
3527
3528    public void postVisit(TWhereClause node){
3529        inWhere = false;
3530    }
3531
3532    public void preVisit(TExpression node){
3533        if (inWhere){
3534            switch (node.getExpressionType()){
3535                case list_t:
3536                    inExprList = true;
3537                    boolean isNumber = true;
3538                    if (node.getExprList().size() > 0){
3539                        // check the type of the constant in the expr list
3540                        TExpression expr = node.getExprList().getExpression(0);
3541                        if (expr.getExpressionType() == EExpressionType.simple_constant_t){
3542                            if (expr.getConstantOperand().getLiteralType() == ELiteralType.etString){
3543                                isNumber = false;
3544                            }
3545                        }
3546                    }
3547
3548                    TSourceToken lcStartToken = node.getStartToken();
3549                    TSourceToken lcEndToken = node.getEndToken();
3550                    int tokenPos = 0;
3551                    if ((lcEndToken != null) && (lcStartToken != null)){
3552                        TSourceToken lcCurrentToken = lcStartToken;
3553                        while (lcCurrentToken != null){
3554
3555                            if (lcCurrentToken.equals(lcEndToken)){
3556                                break;
3557                            }else{
3558
3559                                if (tokenPos == 1){
3560                                    if (isNumber){
3561                                        lcCurrentToken.setTextWithBackup("999");
3562                                    }else{
3563                                        lcCurrentToken.setTextWithBackup("'placeholder_str'");
3564                                    }
3565                                }else if (tokenPos > 1){
3566                                    lcCurrentToken.tokenstatus = ETokenStatus.tsdeleted;
3567                                }
3568
3569                                lcCurrentToken = lcCurrentToken.getNextTokenInChain();
3570                                tokenPos++;
3571                            }
3572                        }
3573                    }
3574
3575                    break;
3576            }
3577        } // where
3578    }
3579
3580    public void postVisit(TExpression node){
3581        if (inWhere){
3582            switch (node.getExpressionType()){
3583                case list_t:
3584                    inExprList = false;
3585                    break;
3586            }
3587        }
3588    }
3589
3590    public void preVisit(TConstant node){
3591        if (inWhere&&(!inExprList)){
3592            switch (node.getLiteralType()){
3593                case etNumber:
3594                case etFloat:
3595                    node.getStartToken().setTextWithBackup("999");
3596                    break;
3597                case etString:
3598                    node.getStartToken().setTextWithBackup("'placeholder_str'");
3599                    break;
3600            }
3601        }
3602    }
3603
3604    public void preVisit(TFunctionCall node){
3605        if (TBaseType.as_canonical_f_decrypt_replace_password){
3606            int i = TBaseType.searchCryptFunction(node.getFunctionName().toString());
3607
3608            if (i>0){ // find this function
3609                if (node.getArgs().size() >= i){
3610                    TExpression secondArg = node.getArgs().getExpression(i-1);
3611                    if (secondArg.getExpressionType() == EExpressionType.simple_constant_t){
3612                        TConstant constant = secondArg.getConstantOperand();
3613                        constant.getValueToken().setTextWithBackup("'***'");
3614                        //System.out.println(node.toString()+":"+constant.toString());
3615                    }else if (secondArg.getExpressionType() == EExpressionType.simple_object_name_t){
3616                        TObjectName objectName = secondArg.getObjectOperand();
3617                        objectName.getStartToken().setTextWithBackup("'***'");
3618                        //System.out.println(node.toString()+":"+constant.toString());
3619                    }
3620                }
3621            }
3622
3623        }
3624    }
3625
3626    void processConstant(TConstant node){
3627        switch (node.getLiteralType()){
3628            case etNumber:
3629            case etFloat:
3630                node.getStartToken().setTextWithBackup("999");
3631                break;
3632            case etString:
3633                node.getStartToken().setTextWithBackup("'placeholder_str'");
3634                break;
3635        }
3636    }
3637
3638}