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