001package gudusoft.gsqlparser.stmt;
002
003import gudusoft.gsqlparser.*;
004import gudusoft.gsqlparser.compiler.TStmtScope;
005import gudusoft.gsqlparser.nodes.*;
006import gudusoft.gsqlparser.nodes.hana.TTimeTravel;
007import gudusoft.gsqlparser.nodes.hive.*;
008import gudusoft.gsqlparser.nodes.mssql.TOptionClause;
009import gudusoft.gsqlparser.nodes.THierarchical;
010import gudusoft.gsqlparser.nodes.teradata.TExpandOnClause;
011import gudusoft.gsqlparser.sqlenv.ESQLDataObjectType;
012import gudusoft.gsqlparser.sqlenv.IdentifierService;
013import gudusoft.gsqlparser.sqlenv.TSQLEnv;
014import gudusoft.gsqlparser.sqlenv.TSQLTable;
015
016import java.util.ArrayList;
017import java.util.Collections;
018import java.util.TreeMap;
019
020
021/**
022 * Class {@link gudusoft.gsqlparser.stmt.TSelectSqlStatement } represents query specification, query expression and select statement.
023 * <br><br><b>query specification</b> including following clause:
024 * <ul>
025 *     <li>select list {@link #getResultColumnList}</li>
026 *     <li>from clause {@link #joins}</li>
027 *     <li>where clause {@link #getWhereClause}</li>
028 *     <li>group clause {@link #getGroupByClause}</li>
029 *     <li>having clause {@link #getGroupByClause}</li>
030 * </ul>
031 * In some databases, query specification also known as sub-select.
032 *
033 * <br><br><b>query expression</b> including all elements in query specification and following clause:
034 * <ul>
035 *     <li>order by clause {@link #getOrderbyClause}</li>
036 *     <li>offset clause {@link #getOffsetClause}</li>
037 *     <li>for clause</li>
038 * </ul>
039 * In some databases, query expression also known as full-select.
040 * <br>It is a component of the select-statement, the INSERT statement, and the CREATE VIEW statement.
041 * And a full-select that is enclosed in parentheses is sometimes called a sub-query.
042 *
043 * <br><br>select statement including all elements in query expression and following clause:
044 * <ul>
045 *     <li>compute clause {@link #getComputeClause}</li>
046 *     <li>for update clause {@link #getForUpdateClause}</li>
047 *     <li>optimizer hint</li>
048 *     <li>ctes {@link #getCteList}</li>
049 * </ul>
050 * Lots of clauses on this level are db vendors specific.
051 *
052
053 * <br><br>
054 * Use {@link #isCombinedQuery} to check whether UNION, EXCEPT and INTERSECT operators is used.
055 * If returns true, use {@link #getLeftStmt} and {@link #getRightStmt} to get query expression.
056 * You need to check {@link #isCombinedQuery} recursively, all clauses of {@link gudusoft.gsqlparser.stmt.TSelectSqlStatement}
057 * only available when this function returns false.
058 *
059 */
060
061public class TSelectSqlStatement extends TCustomSqlStatement {
062
063    // 如果是 combined query, 并且当前select 没有预先设定的 endToken, 则取最右边 query 的 endToken
064    public TSourceToken getEndToken() {
065        if (super.getEndToken() != null) return super.getEndToken();
066        if (this.isCombinedQuery()){
067            return this.getRightStmt().getEndToken();
068        }else{
069            return super.getEndToken();
070        }
071    }
072
073    public void setSubQueryInFromClauseCanUseParentSelectAsEnclosingScope(boolean subQueryInFromClauseCanUseParentSelectAsEnclosingScope) {
074        this.subQueryInFromClauseCanUseParentSelectAsEnclosingScope = subQueryInFromClauseCanUseParentSelectAsEnclosingScope;
075    }
076
077    public boolean isSubQueryInFromClauseCanUseParentSelectAsEnclosingScope() {
078        return subQueryInFromClauseCanUseParentSelectAsEnclosingScope;
079    }
080
081    private boolean subQueryInFromClauseCanUseParentSelectAsEnclosingScope = false;
082
083    boolean starColumnPushedDown = false;
084
085    public boolean isStarColumnPushedDown() {
086        return starColumnPushedDown;
087    }
088
089    public void setStarColumnPushedDown(boolean starColumnPushedDown) {
090        this.starColumnPushedDown = starColumnPushedDown;
091    }
092
093    // StarRocks pipe operator support
094    private TPTNodeList<TParseTreeNode> pipeOperators;
095    private boolean isPipeQuery = false;
096
097    // Oracle 12c WITH FUNCTION/PROCEDURE support - inline PL/SQL definitions
098    private TStatementListSqlNode withPlsqlItems;
099
100    /**
101     * Get the inline PL/SQL function/procedure definitions from WITH clause.
102     * Oracle 12c+ allows defining PL/SQL functions inline in the WITH clause:
103     * WITH FUNCTION f(x NUMBER) RETURN NUMBER IS BEGIN RETURN x*2; END;
104     * SELECT f(1) FROM dual
105     *
106     * @return list of inline function/procedure definitions, or null if none
107     */
108    public TStatementListSqlNode getWithPlsqlItems() {
109        return withPlsqlItems;
110    }
111
112    /**
113     * Set the inline PL/SQL function/procedure definitions.
114     * @param withPlsqlItems list of inline function/procedure definitions
115     */
116    public void setWithPlsqlItems(TStatementListSqlNode withPlsqlItems) {
117        this.withPlsqlItems = withPlsqlItems;
118    }
119
120    /**
121     * Get the list of pipe operators for StarRocks pipe query syntax.
122     * Pipe queries use the |> operator to chain operations:
123     * FROM table |> SELECT * |> WHERE cond |> ORDER BY col
124     *
125     * @return list of TPipeOperator nodes, or null if not a pipe query
126     */
127    public TPTNodeList<TParseTreeNode> getPipeOperators() {
128        return pipeOperators;
129    }
130
131    /**
132     * Set the pipe operators list.
133     * @param pipeOperators list of pipe operator nodes
134     */
135    public void setPipeOperators(TPTNodeList<TParseTreeNode> pipeOperators) {
136        this.pipeOperators = pipeOperators;
137    }
138
139    /**
140     * Check if this is a StarRocks pipe query.
141     * Pipe queries use the syntax: FROM table |> SELECT * |> WHERE ...
142     *
143     * @return true if this is a pipe query
144     */
145    public boolean isPipeQuery() {
146        return isPipeQuery;
147    }
148
149    /**
150     * Set whether this is a pipe query.
151     * @param isPipeQuery true if this is a pipe query
152     */
153    public void setIsPipeQuery(boolean isPipeQuery) {
154        this.isPipeQuery = isPipeQuery;
155    }
156
157    /**
158     * 在 sql script 中,和该 relation 关联的 attribute。
159     * select a from t;
160     * attribute a 就是 relation t 的 referenceAttribute, 如果没有 metadata and ddl,
161     * relation t 的 getAttributes() 应该可以推断出包含 a, 但 t 是否还包含其他的 attribute 就无从得知。
162     */
163
164    @Override
165    public ArrayList<TAttributeNode> getAttributes(){
166        if (this.getFromClause() == null) return null;
167
168        return this.getFromClause().getAttributes();
169
170//        relationAttributes.clear();
171//        for(TTable table:getRelations()){
172//            relationAttributes.addAll(table.getAttributes());
173//        }
174//
175//        if (relationAttributes.size() > 0){
176//            TBaseType.log ("relationAttributes.size() in select is :"+relationAttributes.size()
177//                    +", relation size: "+getRelations().size()
178//                    +", relation 0 type:"+getRelations().get(0).getTableType()
179//                    ,1);
180//            // System.out.println(this.toString());
181//        }
182//
183//        return relationAttributes;
184    }
185
186    ArrayList<TAttributeNode> relationAttributes = new ArrayList<>();
187
188//    private TExpression skipOffset;
189//    private TExpression firstMax;
190//    private TExpression limitMax;
191//
192//    public void setSkipOffset(TExpression skipOffset) {
193//        this.skipOffset = skipOffset;
194//    }
195//
196//    public void setFirstMax(TExpression firstMax) {
197//        this.firstMax = firstMax;
198//    }
199//
200//    public void setLimitMax(TExpression limitMax) {
201//        this.limitMax = limitMax;
202//    }
203//
204//    /**
205//     * Informix skip offset
206//     * @return skip offset
207//     */
208//    public TExpression getSkipOffset() {
209//
210//        return skipOffset;
211//    }
212//
213//    /**
214//     * Informix first Max
215//     * @return first Max
216//     */
217//    public TExpression getFirstMax() {
218//        return firstMax;
219//    }
220//
221//    /**
222//     * Informix limit max
223//     * @return limit max
224//     */
225//    public TExpression getLimitMax() {
226//        return limitMax;
227//    }
228
229    private ArrayList<TSelectSqlStatement> multiSelectStatements;
230
231    /**
232     * Hive, from ... select, select,...
233     * <br> Multiple select statement in Hive from query syntax
234     *
235     * @return Multiple select statement in Hive from query syntax
236     */
237    public ArrayList<TSelectSqlStatement> getMultiSelectStatements() {
238        if(multiSelectStatements == null){
239            multiSelectStatements = new ArrayList<>();
240        }
241        return multiSelectStatements;
242    }
243
244    private TClusterBy clusterBy;
245
246    public void setClusterBy(TClusterBy clusterBy) {
247        this.clusterBy = clusterBy;
248    }
249
250    public TClusterBy getClusterBy() {
251        return clusterBy;
252    }
253
254    private boolean isConsume;
255
256    public void setConsume(boolean consume) {
257        isConsume = consume;
258    }
259
260    public boolean isConsume() {
261        return isConsume;
262    }
263
264    private TStatementList hiveBodyList = null;
265
266    /**
267     * @deprecated since 2.6.3.5, please use {@link #getMultiSelectStatements()} to retrieve
268     * multimple select statement in Hive from query ... select, select, ...
269     *
270     * @return
271     */
272    public TStatementList getHiveBodyList() {
273        if (hiveBodyList == null)
274            hiveBodyList = new TStatementList();
275        return hiveBodyList;
276    }
277
278    public void setSelectModifiers(ArrayList<TSelectModifier> selectModifiers) {
279        this.selectModifiers = selectModifiers;
280    }
281
282    public ArrayList<TSelectModifier> getSelectModifiers() {
283        return selectModifiers;
284    }
285
286    private ArrayList<TSelectModifier> selectModifiers;
287
288
289    private TTimeTravel timeTravel; // hana
290    private THintClause hintClause; //hana
291
292    public void setTimeTravel(TTimeTravel timeTravel) {
293        setNewSubNode(this.timeTravel,timeTravel,getAnchorNode());
294        this.timeTravel = timeTravel;
295    }
296
297    public void setHintClause(THintClause hintClause) {
298        setNewSubNode(this.hintClause,hintClause,getAnchorNode());
299        this.hintClause = hintClause;
300    }
301
302
303    public TTimeTravel getTimeTravel() {
304
305        return timeTravel;
306    }
307
308    public THintClause getHintClause() {
309        return hintClause;
310    }
311
312    private int parenthesisCount = 0;
313    private int parenthesisCountBeforeOrder = 0;
314
315    public void setFetchFirstClause(TFetchFirstClause fetchFirstClause) {
316        setNewSubNode(this.fetchFirstClause,fetchFirstClause,getAnchorNode());
317        this.fetchFirstClause = fetchFirstClause;
318    }
319
320
321    public void setParenthesisCount(int parenthesisCount) {
322        this.parenthesisCount = parenthesisCount;
323    }
324
325    public void setParenthesisCountBeforeOrder(int parenthesisCountBeforeOrder) {
326        this.parenthesisCountBeforeOrder = parenthesisCountBeforeOrder;
327    }
328
329    public void setAll(boolean all) {
330        this.all = all;
331    }
332
333    public void setSetOperatorType(ESetOperatorType setOperatorType) {
334        this.setOperatorType = setOperatorType;
335    }
336
337    public void setWindowClause(TWindowClause windowClause) {
338        setNewSubNode(this.windowClause,windowClause,getAnchorNode());
339        this.windowClause = windowClause;
340    }
341
342    public void setLockingClauses(TPTNodeList<TLockingClause> lockingClauses) {
343        this.lockingClauses = lockingClauses;
344    }
345
346    public void setSelectDistinct(TSelectDistinct selectDistinct) {
347        setNewSubNode(this.selectDistinct,selectDistinct,getAnchorNode());
348        this.selectDistinct = selectDistinct;
349    }
350
351    public void setExpandOnClause(TExpandOnClause expandOnClause) {
352        setNewSubNode(this.expandOnClause,expandOnClause,getAnchorNode());
353        this.expandOnClause = expandOnClause;
354    }
355
356
357    public void setSetOperator(int setOperator) {
358        this.setOperator = setOperator;
359    }
360
361    public void setLeftStmt(TSelectSqlStatement leftStmt) {
362        this.leftStmt = leftStmt;
363    }
364
365    public void setRightStmt(TSelectSqlStatement rightStmt) {
366        this.rightStmt = rightStmt;
367    }
368
369    public void setValueClause(TValueClause valueClause) {
370        setNewSubNode(this.valueClause,valueClause,getAnchorNode());
371        this.valueClause = valueClause;
372    }
373
374    public void setOracleHint(String oracleHint) {
375        this.oracleHint = oracleHint;
376    }
377
378    public void setHiveHintClause(THiveHintClause hiveHintClause) {
379        setNewSubNode(this.hiveHintClause,hiveHintClause,getAnchorNode());
380        this.hiveHintClause = hiveHintClause;
381    }
382
383
384    public void setTransformClause(THiveTransformClause transformClause) {
385        setNewSubNode(this.transformClause,transformClause,getAnchorNode());
386        this.transformClause = transformClause;
387    }
388
389    public void setDistributeBy(TDistributeBy distributeBy) {
390        setNewSubNode(this.distributeBy,distributeBy,getAnchorNode());
391        this.distributeBy = distributeBy;
392    }
393
394
395    public void setIntoClause(TIntoClause intoClause) {
396        setNewSubNode(this.intoClause,intoClause,getAnchorNode());
397        this.intoClause = intoClause;
398    }
399
400    public void setOrderbyClause(TOrderBy newOrderbyClause) {
401        setNewSubNode(this.orderbyClause,newOrderbyClause,getAnchorNode());
402        this.orderbyClause = newOrderbyClause;
403    }
404
405//    public void setOrderbyClause(TOrderBy newOrderbyClause, TParseTreeNode anchorNode) {
406//        if (newOrderbyClause == null){
407//            //remove
408//            this.orderbyClause.removeTokens();
409//            this.orderbyClause = null;
410//        }else{
411//            if (this.orderbyClause != null){
412//                this.orderbyClause.replaceWithNewNode(newOrderbyClause);
413//            }else{
414//                doAppendNewNode(newOrderbyClause,anchorNode,false);
415//            }
416//            this.orderbyClause = newOrderbyClause;
417//        }
418//    }
419
420    public void setQualifyClause(TQualifyClause qualifyClause) {
421        setNewSubNode(this.qualifyClause,qualifyClause,getAnchorNode());
422        this.qualifyClause = qualifyClause;
423    }
424
425    public void setSampleClause(TSampleClause sampleClause) {
426        setNewSubNode(this.sampleClause,sampleClause,getAnchorNode());
427        this.sampleClause = sampleClause;
428    }
429
430    public void setTeradataWithClause(TTeradataWithClause teradataWithClause) {
431        setNewSubNode(this.teradataWithClause,teradataWithClause,getAnchorNode());
432        this.teradataWithClause = teradataWithClause;
433    }
434
435    public void setForUpdateClause(TForUpdate forUpdateClause) {
436        setNewSubNode(this.forUpdateClause,forUpdateClause,getAnchorNode());
437        this.forUpdateClause = forUpdateClause;
438    }
439
440
441    public void setComputeClause(TComputeClause computeClause) {
442        this.computeClause = computeClause;
443    }
444
445    public void setGroupByClause(TGroupBy groupByClause) {
446        setNewSubNode(this.groupByClause,groupByClause,getAnchorNode());
447        this.groupByClause = groupByClause;
448    }
449
450
451    public void setHierarchicalClause(THierarchical hierarchicalClause) {
452        setNewSubNode(this.hierarchicalClause,hierarchicalClause,getAnchorNode());
453        this.hierarchicalClause = hierarchicalClause;
454    }
455
456
457    public void setLimitClause(TLimitClause limitClause) {
458        setNewSubNode(this.limitClause,limitClause,getAnchorNode());
459        this.limitClause = limitClause;
460    }
461
462    public void setOffsetClause(TOffsetClause offsetClause) {
463        setNewSubNode(this.offsetClause,offsetClause,getAnchorNode());
464        this.offsetClause = offsetClause;
465    }
466
467    public void setOptionClause(TOptionClause optionClause) {
468        setNewSubNode(this.optionClause,optionClause,getAnchorNode());
469        this.optionClause = optionClause;
470    }
471
472    public void setIsolationClause(TIsolationClause isolationClause) {
473        setNewSubNode(this.isolationClause,isolationClause,getAnchorNode());
474        this.isolationClause = isolationClause;
475    }
476
477//    public void setHiveClusterBy(THiveClusterBy hiveClusterBy) {
478//        setNewSubNode(this.hiveClusterBy,hiveClusterBy,getAnchorNode());
479//        this.hiveClusterBy = hiveClusterBy;
480//    }
481
482
483    public void setSortBy(TSortBy sortBy) {
484        setNewSubNode(this.sortBy,sortBy,getAnchorNode());
485        this.sortBy = sortBy;
486    }
487
488    public void setIntoTableClause(TIntoTableClause intoTableClause) {
489        setNewSubNode(this.intoTableClause,intoTableClause,getAnchorNode());
490        this.intoTableClause = intoTableClause;
491    }
492
493    public int getParenthesisCountBeforeOrder() {
494        return parenthesisCountBeforeOrder;
495    }
496
497    public int getParenthesisCount() {
498        return parenthesisCount;
499    }
500
501    private boolean queryOfCTE;
502
503    public void setQueryOfCTE(boolean queryOfCTE) {
504        this.queryOfCTE = queryOfCTE;
505    }
506
507    public boolean isQueryOfCTE() {
508        return queryOfCTE;
509    }
510
511    /**
512     * The SELECT-list alias of an ENCLOSING SELECT block that {@code reference}, an unqualified name of this block,
513     * stands for in SAP IQ and SQL Anywhere (Mantis 4752).
514     *
515     * <p>SQL Anywhere: "Aliases can be used anywhere in the SELECT block in which they are defined", and an alias
516     * "occludes" a column or variable of the same name (<i>Renamed columns in query results</i>). A subquery in the
517     * select list, WHERE, GROUP BY, HAVING or ORDER BY of a block is part of that block, so a name the subquery does
518     * not resolve itself is the alias of the block around it:</p>
519     * <pre>SELECT a AS x, (SELECT x) AS y FROM outer_t      -- the inner x is the alias x, that is a</pre>
520     *
521     * <p>Only what the SQL and the catalog prove is taken. Walking outward from this block, a block is passed only
522     * when it provably has no column of that name: it reads no table, or the catalog knows every table it reads, with
523     * all its columns, and none of them has that name. A table with no metadata, a table whose columns are not all
524     * known (no column list, or a {@code *} lineage could not expand), a derived table, a CTE or a table function may
525     * have it, so the walk stops there and the name stays a column. The walk leaves a block only from a subquery in one of
526     * those clauses or from a branch of a set operation, never from a derived table, a join condition or a CTE. In
527     * the enclosing block the alias must be defined before the reference (a forward reference is SQLCODE -831), which
528     * also keeps a name inside the alias's own definition a column; there the alias wins over a column of the same
529     * name.</p>
530     *
531     * <p>The single definition of this rule for the older resolver ({@code TCustomSqlStatement.linkColumnToTable})
532     * and resolver2 ({@code TSQLResolver2.handleSelectListAliasResolution}). An alias of the reference's own block is
533     * {@link TResultColumnList#findLateralAliasDefinition}.</p>
534     *
535     * @param reference an unqualified column reference of this block
536     * @param sqlEnv    the catalog to look a table up in when it has no {@link TTable#getResolvedTable() resolved
537     *                  table}, or null; it must be in the statement's own context (current database and schema), so
538     *                  resolver2, which reads the catalog after the whole script, passes null
539     * @return the defining result column of the enclosing block, or null
540     */
541    public TResultColumn findEnclosingAliasDefinition(TObjectName reference, TSQLEnv sqlEnv) {
542        if ((reference == null) || reference.isQualified()) return null;
543        if (!TSQLEnv.isAliasOccludingColumn(dbvendor)) return null;
544        // A set operation is no block of its own, and while it is parsed its select list is not there yet.
545        if (isCombinedQuery()) return null;
546        String columnName = reference.getColumnNameOnly();
547        if ((columnName == null) || columnName.isEmpty()) return null;
548        // An alias or a possible column of this block is this block's own business.
549        if (hasAliasMatching(this, reference) || mayExposeColumn(this, columnName, sqlEnv)) return null;
550
551        java.util.Set<TCustomSqlStatement> visited =
552                java.util.Collections.newSetFromMap(new java.util.IdentityHashMap<TCustomSqlStatement, Boolean>());
553        TSelectSqlStatement block = this;
554        while (visited.add(block)) {
555            TCustomSqlStatement parent = block.getParentStmt();
556            if (!(parent instanceof TSelectSqlStatement)) return null;
557            TSelectSqlStatement enclosing = (TSelectSqlStatement) parent;
558            if (enclosing.isCombinedQuery()) {
559                // A branch of UNION, INTERSECT or EXCEPT: the set operation is not a block of its own.
560                if ((enclosing.getLeftStmt() != block) && (enclosing.getRightStmt() != block)) return null;
561                block = enclosing;
562                continue;
563            }
564            if (block.isQueryOfCTE() || !isInAliasVisibleClause(block, enclosing)) return null;
565            if (hasAliasMatching(enclosing, reference)) {
566                // null when the alias is defined after the reference, or around it
567                return enclosing.getResultColumnList().findLateralAliasDefinition(dbvendor, reference);
568            }
569            if (mayExposeColumn(enclosing, columnName, sqlEnv)) return null;
570            block = enclosing;
571        }
572        return null;
573    }
574
575    /**
576     * Whether the subquery {@code block} is written in the select list, WHERE, GROUP BY, HAVING or ORDER BY of
577     * {@code enclosing}, where its aliases are visible; a derived table or a join condition is in the FROM clause.
578     * Decided by position: an EXISTS or IN subquery reports no clause of its own.
579     */
580    private static boolean isInAliasVisibleClause(TSelectSqlStatement block, TSelectSqlStatement enclosing) {
581        TSourceToken start = block.getStartToken();
582        if (start == null) return false;
583        TGroupBy groupBy = enclosing.getGroupByClause();
584        return spans(enclosing.getResultColumnList(), start)
585                || spans(enclosing.getWhereClause(), start)
586                || spans(groupBy, start)
587                || ((groupBy != null) && spans(groupBy.getHavingClause(), start))
588                || spans(enclosing.getOrderbyClause(), start);
589    }
590
591    private static boolean spans(TParseTreeNode clause, TSourceToken token) {
592        if ((clause == null) || (clause.getStartToken() == null) || (clause.getEndToken() == null)) return false;
593        return (token.posinlist >= clause.getStartToken().posinlist) && (token.posinlist <= clause.getEndToken().posinlist);
594    }
595
596    private static boolean hasAliasMatching(TSelectSqlStatement block, TObjectName reference) {
597        if (block.isCombinedQuery()) return false;
598        TResultColumnList resultColumns = block.getResultColumnList();
599        if (resultColumns == null) return false;
600        for (int i = 0; i < resultColumns.size(); i++) {
601            TResultColumn resultColumn = resultColumns.getResultColumn(i);
602            if ((resultColumn != null) && (resultColumn.getAliasClause() != null)
603                    && resultColumn.isMatchedUsingAlias(reference)) {
604                return true;
605            }
606        }
607        return false;
608    }
609
610    /**
611     * Whether this block may have a column {@code columnName} of its own: some table it reads may have it (a derived
612     * table, a CTE, a table function, a table whose columns the catalog does not all know, or knows to include it).
613     * False only when the block provably has none. {@code sqlEnv} as in {@link #findEnclosingAliasDefinition}.
614     */
615    public boolean mayExposeColumn(String columnName, TSQLEnv sqlEnv) {
616        return mayExposeColumn(this, columnName, sqlEnv);
617    }
618
619    /** False only when the block provably has no column of that name. */
620    private static boolean mayExposeColumn(TSelectSqlStatement block, String columnName, TSQLEnv sqlEnv) {
621        if (block.tables == null) return false;
622        for (int i = 0; i < block.tables.size(); i++) {
623            TTable table = block.tables.getTable(i);
624            if ((table == null) || (table.getEffectType() == ETableEffectType.tetSelectInto)) continue;
625            if (table.getTableType() == ETableSource.unnest) {
626                if (unnestMayExposeColumn(table, columnName)) return true;
627                continue;
628            }
629            TObjectNameList renamed = ((table.getAliasClause() != null) && (table.getAliasClause().getColumns() != null)
630                    && (table.getAliasClause().getColumns().size() > 0)) ? table.getAliasClause().getColumns() : null;
631            if ((table.getTableType() == ETableSource.subquery) && (table.getSubquery() != null)) {
632                if (derivedMayExposeColumn(table.dbvendor, table.getSubquery(), columnName, renamed, null)) return true;
633                continue;
634            }
635            if (table.isCTEName() && (table.getCTE() != null) && (table.getCTE().getSubquery() != null)) {
636                if (derivedMayExposeColumn(table.dbvendor, table.getCTE().getSubquery(), columnName, renamed,
637                        table.getCTE().getColumnList())) return true;
638                continue;
639            }
640            if (!table.isBaseTable()) return true;
641            // The table as bound in its own statement's context (database, schema, a routine's temporary tables), or
642            // a table the script declared before this block, while it is parsed (Mantis 4797).
643            TSQLTable metadata = tableMetadata(block, table, sqlEnv);
644            if (!hasAllColumnsKnown(metadata) || metadata.searchColumn(columnName)) return true;
645        }
646        return false;
647    }
648
649    /**
650     * The table of an enclosing query that the unqualified {@code name} in {@code subquery} reads, or null when the name
651     * is the subquery's own (Mantis 4797). A SELECT in an expression of a query reads a name from its own tables while
652     * they may have it: in {@code (select Jahr from m_version_table2 where Jahr234 = year(now()))} both names are
653     * m_version_table2's, though a derived table around the subquery names them too. The name is an enclosing query's
654     * when it is compared with a column of the subquery's own tables, the subquery's own tables do not prove it and the
655     * enclosing query does: in {@code (SELECT max(v) FROM customfieldvalues WHERE resourceid = resumeid)} under
656     * {@code FROM (SELECT id AS resumeid FROM resumes) r}, resumeid is r's. Parse-time linking
657     * ({@code TCustomSqlStatement.linkColumnToTable}) and resolver2 ({@code NameResolver}) both decide by this rule.
658     *
659     * @param sqlEnv the catalog the parse reads, or null when each table's {@link TTable#getResolvedTable()} is what
660     *               proves its columns, as after resolver2 bound the tables
661     */
662    public static TTable outerCorrelationTable(TObjectName name, TSelectSqlStatement subquery, TSQLEnv sqlEnv) {
663        if ((name == null) || (subquery == null) || name.isQualified()) return null;
664        TSelectSqlStatement root = setOperationRoot(subquery);
665        if (!isSubqueryInExpression(root)) return null;
666        String column = name.getColumnNameOnly();
667        TObjectName partner = comparedColumn(subquery, name);
668        if ((partner == null) || (provingTable(subquery, column, sqlEnv) != null)) return null;
669        TTable outer = enclosingProvingTable(root, column, sqlEnv);
670        if (outer == null) return null;
671        if (partner.isQualified()) return namesOwnTable(subquery, partner.getTableString()) ? outer : null;
672        String other = partner.getColumnNameOnly();
673        if (sameColumn(subquery.dbvendor, other, column) || (enclosingProvingTable(root, other, sqlEnv) != null)) return null;
674        return outer;
675    }
676
677    /** The statement a branch of a set operation belongs to: the outermost set operation holding it. */
678    private static TSelectSqlStatement setOperationRoot(TSelectSqlStatement select) {
679        TSelectSqlStatement root = select;
680        while (root.getParentStmt() instanceof TSelectSqlStatement) {
681            TSelectSqlStatement parent = (TSelectSqlStatement) root.getParentStmt();
682            if ((parent.getLeftStmt() != root) && (parent.getRightStmt() != root)) break;
683            root = parent;
684        }
685        return root;
686    }
687
688    /** A SELECT written in an expression of a query, which reads that query's FROM clause: not a derived table or CTE. */
689    private static boolean isSubqueryInExpression(TSelectSqlStatement root) {
690        return !root.isQueryOfCTE() && (root.getLocation() != ESqlClause.join) && (root.getLocation() != ESqlClause.elTable)
691                && (root.getParentStmt() instanceof TSelectSqlStatement);
692    }
693
694    /**
695     * The column {@code name} is compared with in a WHERE, HAVING or ON condition of {@code block}, or null. Read from
696     * the parse tree while the statement is parsed: its clauses are attached after their names are linked.
697     */
698    private static TObjectName comparedColumn(TSelectSqlStatement block, TObjectName name) {
699        java.util.Deque<TExpression> pending = new java.util.ArrayDeque<>();
700        TSelectSqlNode node = (block.rootNode instanceof TSelectSqlNode) ? (TSelectSqlNode) block.rootNode : null;
701        TWhereClause where = (block.getWhereClause() != null) ? block.getWhereClause()
702                : ((node != null) ? node.getWhereCondition() : null);
703        if ((where != null) && (where.getCondition() != null)) pending.push(where.getCondition());
704        TGroupBy groupBy = (block.getGroupByClause() != null) ? block.getGroupByClause()
705                : ((node != null) ? node.getGroupByClause() : null);
706        if ((groupBy != null) && (groupBy.getHavingClause() != null)) pending.push(groupBy.getHavingClause());
707        if (block.getJoins() != null) {
708            for (int i = 0; i < block.getJoins().size(); i++) {
709                TJoin join = block.getJoins().getJoin(i);
710                for (int k = 0; (join != null) && (join.getJoinItems() != null) && (k < join.getJoinItems().size()); k++) {
711                    TJoinItem item = join.getJoinItems().getJoinItem(k);
712                    if ((item != null) && (item.getOnCondition() != null)) pending.push(item.getOnCondition());
713                }
714            }
715        }
716        while (!pending.isEmpty()) {
717            TExpression expr = pending.pop();
718            if (expr.getExpressionType() == EExpressionType.subquery_t) continue;
719            if (expr.getExpressionType() == EExpressionType.simple_comparison_t) {
720                TObjectName left = columnOperand(expr.getLeftOperand());
721                TObjectName right = columnOperand(expr.getRightOperand());
722                if ((left == name) && (right != null)) return right;
723                if ((right == name) && (left != null)) return left;
724            }
725            if (expr.getLeftOperand() != null) pending.push(expr.getLeftOperand());
726            if (expr.getRightOperand() != null) pending.push(expr.getRightOperand());
727        }
728        return null;
729    }
730
731    private static TObjectName columnOperand(TExpression operand) {
732        return ((operand != null) && (operand.getExpressionType() == EExpressionType.simple_object_name_t))
733                ? operand.getObjectOperand() : null;
734    }
735
736    /** Whether {@code qualifier} names, by alias or by name, a table of {@code block}'s FROM clause. */
737    private static boolean namesOwnTable(TSelectSqlStatement block, String qualifier) {
738        if ((block.tables == null) || (qualifier == null) || qualifier.isEmpty()) return false;
739        for (int i = 0; i < block.tables.size(); i++) {
740            TTable table = block.tables.getTable(i);
741            if (table == null) continue;
742            String exposed = table.getAliasName().isEmpty() ? table.getName() : table.getAliasName();
743            if (gudusoft.gsqlparser.util.SQLUtil.compareIdentifier(block.dbvendor, ESQLDataObjectType.dotTable, exposed, qualifier)) {
744                return true;
745            }
746        }
747        return false;
748    }
749
750    /**
751     * The table of the nearest enclosing query that proves {@code column}, climbing while the queries passed only may
752     * have it through tables whose columns are not known; null when there is none, when an enclosing query proves it
753     * twice, or when a derived table, CTE or other source there may have it. A derived table or a CTE body sees no
754     * query around it.
755     */
756    private static TTable enclosingProvingTable(TSelectSqlStatement root, String column, TSQLEnv sqlEnv) {
757        TSelectSqlStatement block = root;
758        while (isSubqueryInExpression(block)) {
759            TSelectSqlStatement enclosing = (TSelectSqlStatement) block.getParentStmt();
760            TTable proving = provingTable(enclosing, column, sqlEnv);
761            if (proving == AMBIGUOUS) return null;
762            if (proving != null) return proving;
763            if (sourceMayExposeColumn(enclosing, column)) return null;
764            block = setOperationRoot(enclosing);
765        }
766        return null;
767    }
768
769    /** Marks a column that two tables of one query prove. */
770    private static final TTable AMBIGUOUS = new TTable();
771
772    /**
773     * The table of {@code block} that proves {@code column}: a table whose known columns include it, a derived table or
774     * CTE naming it in its select or column list, an UNNEST whose value, offset or STRUCT fields are named so. Null
775     * when none does; {@link #AMBIGUOUS} when two do.
776     */
777    private static TTable provingTable(TSelectSqlStatement block, String column, TSQLEnv sqlEnv) {
778        if (block.tables == null) return null;
779        TTable found = null;
780        for (int i = 0; i < block.tables.size(); i++) {
781            TTable table = block.tables.getTable(i);
782            if ((table == null) || (table.getEffectType() == ETableEffectType.tetSelectInto)
783                    || (table.getEffectType() == ETableEffectType.tetImplicitLateralDerivedTable)) {
784                continue;
785            }
786            if (tableNamesColumn(block, table, column, sqlEnv)) {
787                if (found != null) return AMBIGUOUS;
788                found = table;
789            }
790        }
791        return found;
792    }
793
794    private static boolean tableNamesColumn(TSelectSqlStatement block, TTable table, String column, TSQLEnv sqlEnv) {
795        EDbVendor vendor = table.dbvendor;
796        TObjectNameList renamed = ((table.getAliasClause() != null) && (table.getAliasClause().getColumns() != null)
797                && (table.getAliasClause().getColumns().size() > 0)) ? table.getAliasClause().getColumns() : null;
798        if (table.getTableType() == ETableSource.unnest) return unnestNamesColumn(table, column);
799        if ((table.getTableType() == ETableSource.subquery) && (table.getSubquery() != null)) {
800            return derivedNamesColumn(vendor, table.getSubquery(), column, renamed, null);
801        }
802        if (table.isCTEName() && (table.getCTE() != null) && (table.getCTE().getSubquery() != null)) {
803            return derivedNamesColumn(vendor, table.getCTE().getSubquery(), column, renamed, table.getCTE().getColumnList());
804        }
805        if (!table.isBaseTable()) return false;
806        TSQLTable metadata = tableMetadata(block, table, sqlEnv);
807        return (metadata != null) && metadata.searchColumn(column);
808    }
809
810    /** Whether a derived table, CTE or another source of {@code block} that is not a table may have {@code column}. */
811    private static boolean sourceMayExposeColumn(TSelectSqlStatement block, String column) {
812        if (block.tables == null) return false;
813        for (int i = 0; i < block.tables.size(); i++) {
814            TTable table = block.tables.getTable(i);
815            if ((table == null) || table.isBaseTable() || (table.getEffectType() == ETableEffectType.tetSelectInto)
816                    || (table.getEffectType() == ETableEffectType.tetImplicitLateralDerivedTable)) {
817                continue;
818            }
819            if (table.getTableType() == ETableSource.unnest) {
820                if (unnestMayExposeColumn(table, column)) return true;
821                continue;
822            }
823            TObjectNameList renamed = ((table.getAliasClause() != null) && (table.getAliasClause().getColumns() != null)
824                    && (table.getAliasClause().getColumns().size() > 0)) ? table.getAliasClause().getColumns() : null;
825            if ((table.getTableType() == ETableSource.subquery) && (table.getSubquery() != null)) {
826                if (derivedMayExposeColumn(table.dbvendor, table.getSubquery(), column, renamed, null)) return true;
827                continue;
828            }
829            if (table.isCTEName() && (table.getCTE() != null) && (table.getCTE().getSubquery() != null)) {
830                if (derivedMayExposeColumn(table.dbvendor, table.getCTE().getSubquery(), column, renamed,
831                        table.getCTE().getColumnList())) return true;
832                continue;
833            }
834            return true;
835        }
836        return false;
837    }
838
839    /** Whether a derived table or CTE names {@code column} among its columns (a star names none). */
840    private static boolean derivedNamesColumn(EDbVendor vendor, TSelectSqlStatement query, String column,
841            TObjectNameList renamed, TObjectNameList declared) {
842        if ((renamed != null) && namesColumn(vendor, renamed, column)) return true;
843        if ((declared != null) && namesColumn(vendor, declared, column)) return true;
844        TSelectSqlStatement first = query;
845        while ((first != null) && first.isCombinedQuery()) first = first.getLeftStmt();
846        if ((first == null) || (first.getValueClause() != null) || (first.getResultColumnList() == null)) return false;
847        int skip = Math.max((renamed == null) ? 0 : renamed.size(), (declared == null) ? 0 : declared.size());
848        for (int i = skip; i < first.getResultColumnList().size(); i++) {
849            TResultColumn resultColumn = first.getResultColumnList().getResultColumn(i);
850            String output = (resultColumn == null) ? null : outputName(resultColumn);
851            if ((output != null) && sameColumn(vendor, output, column)) return true;
852        }
853        return false;
854    }
855
856    /** Whether an UNNEST names {@code column}: its alias column list, alias or value, offset, or STRUCT fields. */
857    private static boolean unnestNamesColumn(TTable unnest, String column) {
858        EDbVendor vendor = unnest.dbvendor;
859        TAliasClause alias = unnest.getAliasClause();
860        if ((alias != null) && (alias.getColumns() != null) && (alias.getColumns().size() > 0)) {
861            return namesColumn(vendor, alias.getColumns(), column);
862        }
863        if ((alias != null) && (alias.getAliasName() != null)) {
864            if (sameColumn(vendor, alias.getAliasName().toString(), column)) return true;
865        } else if (sameColumn(vendor, "value", column)) {
866            return true;
867        }
868        TUnnestClause clause = unnest.getUnnestClause();
869        if (clause == null) return false;
870        if ((clause.getWithOffsetAlais() != null) && (clause.getWithOffsetAlais().getAliasName() != null)
871                && sameColumn(vendor, clause.getWithOffsetAlais().getAliasName().toString(), column)) {
872            return true;
873        }
874        if (clause.getDerivedColumnList() != null) return namesColumn(vendor, clause.getDerivedColumnList(), column);
875        return unnestHasStructField(unnest, column);
876    }
877
878    /** The catalog table of {@code table} as bound, else as the parse reads it ({@code ParseTimeCatalog}, the catalog). */
879    private static TSQLTable tableMetadata(TSelectSqlStatement block, TTable table, TSQLEnv sqlEnv) {
880        TSQLTable metadata = table.getResolvedTable();
881        if ((metadata == null) && (sqlEnv != null)) {
882            metadata = gudusoft.gsqlparser.resolver2.metadata.ParseTimeCatalog.searchTable(block,
883                    table.getPrefixDatabase(), table.getPrefixSchema(), table.getName());
884            if (metadata == null) {
885                metadata = sqlEnv.searchTable(nullToEmpty(table.getPrefixDatabase()) + "."
886                        + nullToEmpty(table.getPrefixSchema()) + "." + table.getName());
887            }
888        }
889        return metadata;
890    }
891
892    /**
893     * Whether a derived table or a CTE may have a column {@code columnName}. Its columns are those of the first SELECT
894     * of {@code query}, named in order by {@code renamed} (the reference's alias column list), else by {@code declared}
895     * (the CTE's column list), else by the SELECT: an alias, or the name of a column it reads. False only when every
896     * column's name is known and none is that name; a star, or an expression the vendor names itself, may be anything.
897     */
898    private static boolean derivedMayExposeColumn(EDbVendor vendor, TSelectSqlStatement query, String columnName,
899            TObjectNameList renamed, TObjectNameList declared) {
900        TSelectSqlStatement first = query;
901        while ((first != null) && first.isCombinedQuery()) first = first.getLeftStmt();
902        TResultColumnList projection = ((first == null) || (first.getValueClause() != null)) ? null
903                : first.getResultColumnList();
904        // The number of columns, or -1 when the SELECT does not show it.
905        int width = (projection == null) ? -1 : projection.size();
906        for (int i = 0; (width >= 0) && (i < projection.size()); i++) {
907            TResultColumn column = projection.getResultColumn(i);
908            TObjectName field = (column == null) ? null : column.getFieldAttr();
909            boolean star = (field != null) && (field.getPartToken() != null)
910                    && (field.getPartToken().tokentype == ETokenType.ttasterisk);
911            boolean several = (column != null) && (column.getAliasClause() != null)
912                    && (column.getAliasClause().getColumns() != null) && (column.getAliasClause().getColumns().size() > 0);
913            if ((column == null) || star || several) width = -1;
914        }
915        int listed = Math.max((renamed == null) ? 0 : renamed.size(), (declared == null) ? 0 : declared.size());
916        int count = Math.max(width, listed);
917        for (int i = 0; i < count; i++) {
918            String name;
919            if ((renamed != null) && (i < renamed.size())) {
920                name = renamed.getObjectName(i).toString();
921            } else if ((declared != null) && (i < declared.size())) {
922                name = declared.getObjectName(i).toString();
923            } else {
924                name = (i < width) ? outputName(projection.getResultColumn(i)) : null;
925                if (name == null) return true;
926            }
927            if (sameColumn(vendor, name, columnName)) return true;
928        }
929        return width < 0;
930    }
931
932    /** The name a SELECT gives a column of its result: its alias, or the column it reads; null when the vendor names it. */
933    private static String outputName(TResultColumn column) {
934        if (column.getAliasClause() != null) {
935            return (column.getAliasClause().getAliasName() == null) ? null
936                    : column.getAliasClause().getAliasName().toString();
937        }
938        TExpression expr = column.getExpr();
939        if ((expr != null) && (expr.getExpressionType() == EExpressionType.simple_object_name_t)
940                && (expr.getObjectOperand() != null)) {
941            return expr.getObjectOperand().getColumnNameOnly();
942        }
943        return null;
944    }
945
946    /**
947     * Whether an UNNEST relation may have a column {@code columnName}: the value column its alias names (GSP names it
948     * {@code value} when there is no alias), its offset column, the columns an alias list such as {@code t(x, n)} or the
949     * catalog's STRUCT type gives it, and, while its elements may be STRUCTs, any of their fields. In
950     * {@code ARRAY(SELECT x FROM UNNEST(some_numbers) AS x WHERE 2 IN UNNEST(some_numbers))} over an array of numbers,
951     * the second {@code some_numbers} can only be the enclosing query's column.
952     */
953    private static boolean unnestMayExposeColumn(TTable unnest, String columnName) {
954        EDbVendor vendor = unnest.dbvendor;
955        TAliasClause alias = unnest.getAliasClause();
956        if ((alias != null) && (alias.getColumns() != null) && (alias.getColumns().size() > 0)) {
957            return namesColumn(vendor, alias.getColumns(), columnName);
958        }
959        if ((alias != null) && (alias.getAliasName() != null)) {
960            if (sameColumn(vendor, alias.getAliasName().toString(), columnName)) return true;
961        } else if (sameColumn(vendor, "value", columnName)) {
962            return true;
963        }
964        TUnnestClause clause = unnest.getUnnestClause();
965        if (clause == null) return true;
966        if ((clause.getWithOffsetAlais() != null) && (clause.getWithOffsetAlais().getAliasName() != null)
967                && sameColumn(vendor, clause.getWithOffsetAlais().getAliasName().toString(), columnName)) {
968            return true;
969        }
970        if (clause.getDerivedColumnList() != null) return namesColumn(vendor, clause.getDerivedColumnList(), columnName);
971        java.util.List<String> fields = structFields(clause);
972        if (fields != null) return containsColumn(vendor, fields, columnName);
973        return !hasScalarElements(clause);
974    }
975
976    /** Whether the arrays an UNNEST reads provably hold no STRUCTs, so it has no column but its value and offset. */
977    private static boolean hasScalarElements(TUnnestClause clause) {
978        if (clause.getArrayExpr() != null) return hasScalarElements(clause.getArrayExpr(), 0);
979        if (clause.getExprList() != null) {
980            if (clause.getExprList().size() == 0) return false;
981            for (int i = 0; i < clause.getExprList().size(); i++) {
982                if (!hasScalarElements(clause.getExprList().getExpression(i), 0)) return false;
983            }
984            return true;
985        }
986        return (clause.getColumns() != null) && (clause.getColumns().size() == 1)
987                && hasScalarElements(clause.getColumns().getObjectName(0), 0);
988    }
989
990    /**
991     * Whether an array expression provably holds no STRUCTs: an array literal of literals, a BigQuery GENERATE_ARRAY,
992     * GENERATE_DATE_ARRAY, GENERATE_TIMESTAMP_ARRAY or SPLIT, or a column a CTE or derived table defines as one.
993     */
994    private static boolean hasScalarElements(TExpression array, int depth) {
995        if ((array == null) || (depth > 8)) return false;
996        switch (array.getExpressionType()) {
997            case array_t:
998                if ((array.getExprList() == null) || (array.getExprList().size() == 0)) return false;
999                for (int i = 0; i < array.getExprList().size(); i++) {
1000                    TExpression element = array.getExprList().getExpression(i);
1001                    if ((element.getExpressionType() == EExpressionType.unary_minus_t)
1002                            || (element.getExpressionType() == EExpressionType.unary_plus_t)) {
1003                        element = element.getRightOperand();
1004                    }
1005                    if ((element == null) || (element.getExpressionType() != EExpressionType.simple_constant_t)) return false;
1006                }
1007                return true;
1008            case function_t:
1009                TFunctionCall call = array.getFunctionCall();
1010                if ((call == null) || (array.dbvendor != EDbVendor.dbvbigquery) || (call.getFunctionName() == null)
1011                        || (call.getFunctionName().getSchemaToken() != null)) {
1012                    return false;
1013                }
1014                String function = call.getFunctionName().toString();
1015                return function.equalsIgnoreCase("GENERATE_ARRAY") // non-identifier-compare: built-in function names
1016                        || function.equalsIgnoreCase("GENERATE_DATE_ARRAY") // non-identifier-compare: built-in function name
1017                        || function.equalsIgnoreCase("GENERATE_TIMESTAMP_ARRAY") // non-identifier-compare: built-in function name
1018                        || function.equalsIgnoreCase("SPLIT"); // non-identifier-compare: built-in function name
1019            case simple_object_name_t:
1020                return hasScalarElements(array.getObjectOperand(), depth + 1);
1021            default:
1022                return false;
1023        }
1024    }
1025
1026    /** Whether the column of a CTE or derived table that {@code column} reads is defined as an array of no STRUCTs. */
1027    private static boolean hasScalarElements(TObjectName column, int depth) {
1028        TExpression definition = definingExpression(column, depth);
1029        return (definition != null) && hasScalarElements(definition, depth + 1);
1030    }
1031
1032    /** The expression that defines the column of a CTE or derived table {@code column} reads, or null. */
1033    private static TExpression definingExpression(TObjectName column, int depth) {
1034        if ((column == null) || (column.getSourceTable() == null) || (depth > 8)) return null;
1035        TTable source = column.getSourceTable();
1036        TSelectSqlStatement query = null;
1037        if (source.isCTEName() && (source.getCTE() != null)) {
1038            // a column list renames by position; not followed
1039            if ((source.getCTE().getColumnList() != null) && (source.getCTE().getColumnList().size() > 0)) return null;
1040            query = source.getCTE().getSubquery();
1041        } else if (source.getTableType() == ETableSource.subquery) {
1042            query = source.getSubquery();
1043        }
1044        while ((query != null) && query.isCombinedQuery()) query = query.getLeftStmt();
1045        if ((query == null) || (query.getResultColumnList() == null)) return null;
1046        for (int i = 0; i < query.getResultColumnList().size(); i++) {
1047            TResultColumn resultColumn = query.getResultColumnList().getResultColumn(i);
1048            TExpression expr = resultColumn.getExpr();
1049            String name = null;
1050            if ((resultColumn.getAliasClause() != null) && (resultColumn.getAliasClause().getAliasName() != null)) {
1051                name = resultColumn.getAliasClause().getAliasName().toString();
1052            } else if ((expr != null) && (expr.getObjectOperand() != null)
1053                    && (expr.getExpressionType() == EExpressionType.simple_object_name_t)) {
1054                name = expr.getObjectOperand().getColumnNameOnly();
1055            }
1056            if ((name != null) && sameColumn(column.dbvendor, name, column.getColumnNameOnly())) return expr;
1057        }
1058        return null;
1059    }
1060
1061    /**
1062     * Whether an UNNEST relation provably has a column {@code columnName} as a field of the STRUCTs its array holds:
1063     * {@code UNNEST([STRUCT(0 AS a, 1 AS b)])}, or a CTE or derived column defined so, has the columns a and b.
1064     */
1065    public static boolean unnestHasStructField(TTable unnest, String columnName) {
1066        java.util.List<String> fields = unnestStructFieldNames(unnest);
1067        return (fields != null) && (columnName != null) && containsColumn(unnest.dbvendor, fields, columnName);
1068    }
1069
1070    /**
1071     * The field names of the STRUCTs an UNNEST reads, which are its columns, or null when they are not known or an
1072     * alias column list names its columns instead. Parse-time linking and resolver2's {@code UnnestNamespace} both
1073     * read them here.
1074     */
1075    public static java.util.List<String> unnestStructFieldNames(TTable unnest) {
1076        if ((unnest == null) || (unnest.getUnnestClause() == null)) return null;
1077        TAliasClause alias = unnest.getAliasClause();
1078        if ((alias != null) && (alias.getColumns() != null) && (alias.getColumns().size() > 0)) return null;
1079        return structFields(unnest.getUnnestClause());
1080    }
1081
1082    /** The field names of the STRUCTs an UNNEST reads, or null when they are not known. */
1083    private static java.util.List<String> structFields(TUnnestClause clause) {
1084        if (clause.getArrayExpr() != null) return structFields(clause.getArrayExpr(), 0);
1085        if (clause.getExprList() != null) {
1086            return (clause.getExprList().size() == 1) ? structFields(clause.getExprList().getExpression(0), 0) : null;
1087        }
1088        if ((clause.getColumns() != null) && (clause.getColumns().size() == 1)) {
1089            TExpression definition = definingExpression(clause.getColumns().getObjectName(0), 0);
1090            return (definition == null) ? null : structFields(definition, 1);
1091        }
1092        return null;
1093    }
1094
1095    /** The field names of an array of STRUCT literals, or of a CTE or derived column defined as one; else null. */
1096    private static java.util.List<String> structFields(TExpression array, int depth) {
1097        if ((array == null) || (depth > 8)) return null;
1098        switch (array.getExpressionType()) {
1099            case array_t: {
1100                // a typed array literal, ARRAY<STRUCT<x INT64, y STRING>>[(1, 'a')]: its element type names the fields
1101                // (plan 09 slice 11)
1102                java.util.List<String> typed = typedStructFields(array.getTypeName());
1103                if (typed != null) return typed;
1104                if ((array.getExprList() == null) || (array.getExprList().size() == 0)) return null;
1105                java.util.List<String> fields = null;
1106                for (int i = 0; i < array.getExprList().size(); i++) {
1107                    java.util.List<String> elementFields = structLiteralFields(array.getExprList().getExpression(i));
1108                    if (elementFields == null) return null;
1109                    if (fields == null) fields = elementFields;
1110                }
1111                return fields;
1112            }
1113            case simple_object_name_t: {
1114                TExpression definition = definingExpression(array.getObjectOperand(), depth);
1115                return (definition == null) ? null : structFields(definition, depth + 1);
1116            }
1117            default:
1118                return null;
1119        }
1120    }
1121
1122    /** The field names a STRUCT element type declares ({@code STRUCT<x INT64, y STRING>}); else null. */
1123    private static java.util.List<String> typedStructFields(TTypeName type) {
1124        if ((type == null) || (type.getDataType() != EDataType.struct_t) || (type.getColumnDefList() == null)
1125                || (type.getColumnDefList().size() == 0)) {
1126            return null;
1127        }
1128        java.util.List<String> fields = new java.util.ArrayList<>();
1129        for (int i = 0; i < type.getColumnDefList().size(); i++) {
1130            TColumnDefinition definition = type.getColumnDefList().getColumn(i);
1131            if ((definition == null) || (definition.getColumnName() == null)) return null;
1132            fields.add(definition.getColumnName().toString());
1133        }
1134        return fields;
1135    }
1136
1137    /** The field names of a STRUCT literal, {@code STRUCT(0 AS a, 1 AS b)} or {@code STRUCT<a INT64>(0)}; else null. */
1138    private static java.util.List<String> structLiteralFields(TExpression element) {
1139        if ((element == null) || (element.getExpressionType() != EExpressionType.function_t)
1140                || (element.getFunctionCall() == null)
1141                || (element.getFunctionCall().getFunctionType() != EFunctionType.struct_t)) {
1142            return null;
1143        }
1144        TFunctionCall struct = element.getFunctionCall();
1145        java.util.List<String> fields = new java.util.ArrayList<>();
1146        if ((struct.getFieldDefs() != null) && (struct.getFieldDefs().size() > 0)) {
1147            for (int i = 0; i < struct.getFieldDefs().size(); i++) {
1148                TColumnDefinition definition = struct.getFieldDefs().getColumn(i);
1149                if ((definition == null) || (definition.getColumnName() == null)) return null;
1150                fields.add(definition.getColumnName().toString());
1151            }
1152            return fields;
1153        }
1154        if ((struct.getFieldValues() == null) || (struct.getFieldValues().size() == 0)) return null;
1155        for (int i = 0; i < struct.getFieldValues().size(); i++) {
1156            TResultColumn value = struct.getFieldValues().getResultColumn(i);
1157            String name = (value == null) ? null : outputName(value);
1158            if (name == null) return null;
1159            fields.add(name);
1160        }
1161        return fields;
1162    }
1163
1164    private static boolean containsColumn(EDbVendor vendor, java.util.List<String> names, String columnName) {
1165        for (String name : names) {
1166            if (sameColumn(vendor, name, columnName)) return true;
1167        }
1168        return false;
1169    }
1170
1171    private static boolean namesColumn(EDbVendor vendor, TObjectNameList names, String columnName) {
1172        for (int i = 0; i < names.size(); i++) {
1173            TObjectName name = names.getObjectName(i);
1174            if ((name != null) && sameColumn(vendor, name.toString(), columnName)) return true;
1175        }
1176        return false;
1177    }
1178
1179    private static boolean sameColumn(EDbVendor vendor, String a, String b) {
1180        return gudusoft.gsqlparser.util.SQLUtil.compareIdentifier(vendor, ESQLDataObjectType.dotColumn, a, b);
1181    }
1182
1183    /** A catalog table whose columns are all known: an empty list or a star lineage wrote back proves nothing. */
1184    private static boolean hasAllColumnsKnown(TSQLTable metadata) {
1185        return (metadata != null) && metadata.hasAllColumnsKnown();
1186    }
1187
1188    private static String nullToEmpty(String s) {
1189        return (s == null) ? "" : s;
1190    }
1191
1192    /**
1193     * Offset clause.
1194     * <br>
1195     * <pre>OFFSET 2 ROWS FETCH NEXT 4 ROWS ONLY;</pre>
1196     * @return {@link TOffsetClause offset clause}
1197     */
1198    public TOffsetClause getOffsetClause() {
1199        return offsetClause;
1200    }
1201
1202    /**
1203     * Fetch first/next clause
1204     * <br>
1205     * <pre>OFFSET 2 ROWS FETCH NEXT 4 ROWS ONLY;</pre>
1206     * @return {@link TFetchFirstClause fetch first/next clause}
1207     */
1208    public TFetchFirstClause getFetchFirstClause() {
1209
1210        return fetchFirstClause;
1211    }
1212
1213    private TFetchFirstClause fetchFirstClause;
1214    private TOffsetClause offsetClause;
1215
1216    private TOptionClause optionClause;
1217
1218    /**
1219     *
1220     * @return {@link  TOptionClause sql server option clause}
1221     *
1222     * @see gudusoft.gsqlparser.nodes.mssql.TOptionClause
1223     */
1224    public TOptionClause getOptionClause() {
1225        return optionClause;
1226    }
1227
1228
1229    /**
1230     *
1231     * @return {@link TIsolationClause isolation clause}
1232     */
1233    public TIsolationClause getIsolationClause() {
1234        return isolationClause;
1235    }
1236
1237    private TIsolationClause isolationClause;
1238
1239//     private THiveClusterBy hiveClusterBy;
1240//
1241//    /**
1242//     *
1243//     * @return {@link THiveClusterBy Hive cluster by clause}
1244//     */
1245//    public THiveClusterBy getHiveClusterBy() {
1246//        return hiveClusterBy;
1247//    }
1248
1249    private TSortBy sortBy;
1250
1251    /**
1252     *
1253     * @return {@link TSortBy Hive sort by clause}
1254     */
1255    public TSortBy getSortBy() {
1256        return sortBy;
1257    }
1258
1259    private TIntoTableClause intoTableClause;
1260
1261    /**
1262     *
1263     * @return {@link TIntoTableClause informix into table clause}
1264     */
1265    public TIntoTableClause getIntoTableClause() {
1266        return intoTableClause;
1267    }
1268
1269    public void setSelectToken(TSourceToken selectToken) {
1270        this.selectToken = selectToken;
1271    }
1272
1273    /**
1274     *
1275     * @return SELECT token of this query.
1276     */
1277    public TSourceToken getSelectToken() {
1278        return selectToken;
1279    }
1280
1281    private TSourceToken selectToken = null;
1282
1283    private boolean tableQuery = false;
1284
1285    /**
1286     * Whether this query was written as {@code TABLE t}, the SQL-standard
1287     * shorthand for {@code SELECT * FROM t}.
1288     * <p>
1289     * Such a query is analyzed as {@code SELECT * FROM t}: t is in
1290     * {@link #getTables()} and {@link #getJoins()}, and {@link #getResultColumnList()}
1291     * holds one wildcard result column. That wildcard is synthesized: its tokens
1292     * are not in the source token list, and the text has no SELECT or FROM
1293     * keyword, so code that rewrites the token stream (a formatter) must leave
1294     * the select list and the FROM list of such a query alone.
1295     *
1296     * @return true if this query was written as {@code TABLE t}
1297     */
1298    public boolean isTableQuery() {
1299        return tableQuery;
1300    }
1301
1302    public static final int SET_OPERATOR_NONE = 0;
1303
1304    public static final int setOperator_union = 1;
1305    public static final int setOperator_unionall = 2;
1306    public static final int SET_OPERATOR_UNIONDISTINCT = 9;
1307
1308    public static final int setOperator_intersect = 3;
1309    public static final int setOperator_intersectall = 4;
1310    public static final int SET_OPERATOR_INTERSECTDISTINCT = 10;
1311
1312    public static final int setOperator_minus = 5;
1313    public static final int setOperator_minusall = 6;
1314    public static final int SET_OPERATOR_MINUSDISTINCT = 11;
1315
1316    public static final int setOperator_except = 7;
1317    public static final int setOperator_exceptall = 8;
1318    public static final int SET_OPERATOR_EXCEPTDISTINCT = 11;
1319
1320    public boolean isAll() {
1321        return all;
1322    }
1323
1324    private boolean all = false;
1325
1326    public boolean isSetOpDistinct() {
1327        return setOpDistinct;
1328    }
1329
1330    private boolean setOpDistinct = false;
1331    private ESetOperatorType setOperatorType = ESetOperatorType.none;
1332
1333    public ESetOperatorType getSetOperatorType() {
1334        return setOperatorType;
1335    }
1336
1337    private  TWindowClause  windowClause;
1338
1339    /**
1340     *
1341     * @return {@link TWindowClause window clause.}
1342     */
1343    public TWindowClause getWindowClause() {
1344        return windowClause;
1345    }
1346
1347    private TPTNodeList <TLockingClause> lockingClauses;
1348
1349    /**
1350     *  PostgreSQL, for update of, for read only.
1351     *  <br>
1352     * @return list of {@link TLockingClause}
1353     */
1354    public TPTNodeList<TLockingClause> getLockingClauses() {
1355        return lockingClauses;
1356    }
1357
1358    /**
1359     *
1360     * @return {@link TSelectDistinct distinct clause}
1361     */
1362    public TSelectDistinct getSelectDistinct() {
1363        return selectDistinct;
1364    }
1365
1366    private TSelectDistinct selectDistinct = null;
1367
1368    /**
1369     *
1370     * @return {@link gudusoft.gsqlparser.nodes.TIntoClause into clause}
1371     */
1372    public TIntoClause getIntoClause() {
1373        return intoClause;
1374    }
1375
1376    private TIntoClause intoClause = null;
1377
1378
1379    private TOrderBy orderbyClause;
1380
1381    /**
1382     * @return {@link TOrderBy order by clause}
1383     */
1384    public TOrderBy getOrderbyClause() {
1385        return orderbyClause;
1386    }
1387
1388    public TSelectSqlStatement(EDbVendor dbvendor) {
1389        super(dbvendor);
1390        sqlstatementtype = ESqlStatementType.sstselect;
1391    }
1392
1393    void buildsql() {
1394    }
1395
1396    void clear() {
1397    }
1398
1399    String getasprettytext() {
1400        return "";
1401    }
1402
1403    void iterate(TVisitorAbs pvisitor) {
1404    }
1405
1406    /**
1407     *
1408     * @return {@link TQualifyClause teradata qualify clause}
1409     */
1410    public TQualifyClause getQualifyClause() {
1411        return qualifyClause;
1412    }
1413
1414    private TQualifyClause qualifyClause = null;
1415    private TSampleClause sampleClause = null;
1416    private TTeradataWithClause teradataWithClause = null;
1417
1418    /**
1419     * @return {@link TTeradataWithClause Teradata with clause }
1420     */
1421    public TTeradataWithClause getTeradataWithClause() {
1422        return teradataWithClause;
1423    }
1424
1425    /**
1426     *
1427     * @return {@link gudusoft.gsqlparser.nodes.TSampleClause SQL Server sample clause}
1428     */
1429    public TSampleClause getSampleClause() {
1430        return sampleClause;
1431    }
1432
1433    /**
1434     * @return {@link gudusoft.gsqlparser.nodes.TForUpdate for update clause}
1435     */
1436    public TForUpdate getForUpdateClause() {
1437        return forUpdateClause;
1438    }
1439
1440    private TForUpdate forUpdateClause;
1441
1442    /**
1443     *
1444     * @return {@link TComputeClause SQL Server compute clause}
1445     */
1446    public TComputeClause getComputeClause() {
1447        return computeClause;
1448    }
1449
1450    private TComputeClause computeClause = null;
1451
1452    /**
1453     * @return  {@link TGroupBy group by clause and having clause}
1454     */
1455    public TGroupBy getGroupByClause() {
1456        return groupByClause;
1457    }
1458
1459    private TGroupBy groupByClause = null;
1460
1461
1462    /**
1463     * Oracle hierarchical_query_clause
1464     * @return {@link gudusoft.gsqlparser.nodes.THierarchical Oracle hierarchical_query_clause}
1465     */
1466    public THierarchical getHierarchicalClause() {
1467        return hierarchicalClause;
1468    }
1469
1470    private THierarchical hierarchicalClause = null;
1471
1472    /**
1473     * MySQL,PostgreSQL limit clause
1474     * @return {@link TLimitClause MySQL,PostgreSQL limit clause}
1475     */
1476    public TLimitClause getLimitClause() {
1477        return limitClause;
1478    }
1479
1480    private TLimitClause limitClause = null;
1481
1482
1483    private TExpandOnClause expandOnClause;
1484
1485    /**
1486     *
1487     * @return {@link TExpandOnClause Teradata expand on clause}
1488     */
1489    public TExpandOnClause getExpandOnClause() {
1490        return expandOnClause;
1491    }
1492
1493    /**
1494     *
1495     * The set operators UNION, EXCEPT, and INTERSECT correspond to the relational operators union, difference, and intersection.
1496     * @return  set operator of this statement.If a set operator is not used, setOperator_none will be returned.
1497     * otherwise, set operand can be fetched by using getLeftStmt() and getRightStmt().
1498     *
1499     * @deprecated As of v1.6.4.2, use {@link #getSetOperatorType()}  and {@link #isAll()} instead.
1500     */
1501    public int getSetOperator() {
1502        return setOperator;
1503    }
1504
1505    private int setOperator = 0;
1506
1507    /**
1508     * Valid when {@link #getSetOperator} is not setOperator_none.
1509     * @return left side query expression
1510     */
1511    public TSelectSqlStatement getLeftStmt() {
1512        return leftStmt;
1513    }
1514
1515    public TSelectSqlStatement getFarLeftStmt() {
1516        TSelectSqlStatement current = leftStmt;
1517        while (current.isCombinedQuery()) {
1518            current = current.getLeftStmt();
1519        }
1520        return current;
1521    }
1522
1523    /**
1524     * Valid when {@link #getSetOperator} is not setOperator_none.
1525     * @return right side query expression.
1526     */
1527    public TSelectSqlStatement getRightStmt() {
1528        return rightStmt;
1529    }
1530
1531    private TSelectSqlStatement leftStmt;
1532    private TSelectSqlStatement rightStmt;
1533
1534    private TValueClause valueClause = null;
1535
1536    private String oracleHint;
1537
1538    private String hint;
1539
1540    private boolean isValueClause = false;
1541
1542    public boolean isValueClause() {
1543        return isValueClause;
1544    }
1545
1546    /**
1547     * Hint for Oracle and MySQL
1548     *
1549     * @return hint string
1550     */
1551    public String getHint() {
1552        return hint;
1553    }
1554
1555    /**
1556     * Hint for Oracle and MySQL
1557     *
1558     * @return  hint string.
1559     */
1560    /**
1561     * @deprecated As of 2.0.4.5, replaced by {@link #getHint()}
1562     */
1563    public String getOracleHint() {
1564        return oracleHint;
1565    }
1566
1567    private THiveHintClause hiveHintClause;
1568
1569    /**
1570     *
1571     * @return {@link THiveHintClause Hive hint clause}
1572     */
1573    public THiveHintClause getHiveHintClause() {
1574        return hiveHintClause;
1575    }
1576
1577    private THiveTransformClause transformClause;
1578
1579    /**
1580     *
1581     * @return {@link THiveTransformClause Hive from clause}
1582     */
1583    public THiveTransformClause getTransformClause() {
1584        return transformClause;
1585    }
1586
1587    private TDistributeBy distributeBy;
1588
1589    /**
1590     *
1591     * @return {@link TDistributeBy Hive distribute by clause}
1592     */
1593    public TDistributeBy getDistributeBy() {
1594        return distributeBy;
1595    }
1596
1597    /**
1598     * DB2 value clause
1599     * @return {@link TValueClause DB2 value clause}
1600     */
1601    public TValueClause getValueClause() {
1602        return valueClause;
1603    }
1604
1605    public void setChildOfCombinedQuery(boolean childOfCombinedQuery) {
1606        this.childOfCombinedQuery = childOfCombinedQuery;
1607    }
1608
1609    public boolean isChildOfCombinedQuery() {
1610
1611        return childOfCombinedQuery;
1612    }
1613
1614    private boolean childOfCombinedQuery = false;
1615
1616
1617    @Override
1618    public TreeMap<String,TResultColumn> getExpandedResultColumns() {
1619        if (this.setOperatorType == ESetOperatorType.none){
1620            return super.getExpandedResultColumns();
1621        }else{
1622            return this.getLeftStmt().getExpandedResultColumns();
1623        }
1624    }
1625
1626    private ArrayList<TSelectSqlStatement> flattenSelects = null;
1627
1628    public ArrayList<TSelectSqlStatement> getFlattenedSelects() {
1629        if (!isCombinedQuery()) return null;
1630        if (this.flattenSelects != null) return this.flattenSelects;
1631
1632        this.flattenSelects = new ArrayList<TSelectSqlStatement>();
1633        this.flattenSelects.add(this.rightStmt);
1634        doFlattenCombinedSelect(this.leftStmt,this.flattenSelects);
1635
1636        Collections.reverse(this.flattenSelects);
1637        return flattenSelects;
1638    }
1639
1640    void doFlattenCombinedSelect(TSelectSqlStatement select, ArrayList<TSelectSqlStatement> container){
1641        TSelectSqlStatement current = select;
1642        while (current.isCombinedQuery()){
1643            container.add(current.getRightStmt());
1644            current = current.getLeftStmt();
1645        }
1646        container.add(current);
1647    }
1648
1649    /**
1650     * This function is used internal. DON'T call it explicitly.
1651     *
1652     * @param psql input query.
1653     * @return  zero means there is no syntax error detected.
1654     */
1655    public int doParseStatement(TCustomSqlStatement psql) {
1656        if (rootNode == null) return -1;
1657
1658        if ((rootNode instanceof TSelectSqlNode)
1659                && (this.sourcetokenlist.size() == 0)){
1660            // subquery nested in other statements.
1661            TSelectSqlNode selectNode = (TSelectSqlNode)rootNode;
1662            this.setStartToken(selectNode.getStartToken());
1663            this.setEndToken(selectNode.getEndToken());
1664        }
1665
1666        super.doParseStatement(psql);
1667
1668        if (!(rootNode instanceof TSelectSqlNode)) {
1669            return reportInvalidSelectStatement();
1670        }
1671
1672        TSelectSqlNode selectNode = (TSelectSqlNode)rootNode;
1673        // TABLE t is SELECT * FROM t: give it that FROM list and select list
1674        // before anything reads them, so it is analyzed exactly like that query.
1675        selectNode.expandTableQuery();
1676        tableQuery = (selectNode.getRelationExpr() != null) && !selectNode.isCombinedQuery();
1677        if (hasInvalidResultColumnList(selectNode, psql)) {
1678            return reportInvalidSelectStatement();
1679        }
1680
1681        this.selectToken = selectNode.getSelectToken();
1682        parenthesisCount = selectNode.getParenthesisCount();
1683        parenthesisCountBeforeOrder = selectNode.getParenthissisCountBeforeOrder();
1684
1685        setOperator = selectNode.getSetOperator();
1686        initSetOperatorFields();
1687
1688        if(selectNode.isCombinedQuery()){
1689            TCustomSqlStatement lcParentSql = psql;
1690            if ((lcParentSql == null)||(!childOfCombinedQuery)){
1691                lcParentSql = this;
1692            }
1693
1694            if (selectNode.cteList != null){
1695                this.setCteList(selectNode.cteList);
1696                for(int i=0;i<getCteList().size();i++){
1697                    getCteList().getCTE(i).doParse(this,ESqlClause.cte);
1698                }
1699            }
1700
1701            // Oracle 12c WITH FUNCTION/PROCEDURE support
1702            if (selectNode.getWithPlsqlItems() != null){
1703                this.setWithPlsqlItems(selectNode.getWithPlsqlItems());
1704            }
1705
1706            // Iterative construction of the left-recursive UNION/INTERSECT/EXCEPT chain.
1707            // This avoids StackOverflow for queries with many set operations (e.g., 2000+ UNION ALL).
1708            // We collect the chain of combined query nodes, set up their properties top-down,
1709            // then process leaf children and clauses from deepest to shallowest.
1710
1711            ArrayList<TSelectSqlStatement> stmtChain = new ArrayList<>();
1712            ArrayList<TSelectSqlNode> nodeChain = new ArrayList<>();
1713
1714            stmtChain.add(this);
1715            nodeChain.add(selectNode);
1716
1717            TSelectSqlStatement currentStmt = this;
1718            TSelectSqlNode currentNode = selectNode;
1719
1720            // Walk down the left chain, creating intermediate TSelectSqlStatements
1721            while (currentNode.getLeftNode() != null && currentNode.getLeftNode().isCombinedQuery()) {
1722                TSelectSqlNode leftNode = currentNode.getLeftNode();
1723
1724                TSelectSqlStatement nextStmt = new TSelectSqlStatement(dbvendor);
1725                nextStmt.rootNode = leftNode;
1726                nextStmt.setChildOfCombinedQuery(true);
1727
1728                // Wire up parent's leftStmt
1729                currentStmt.leftStmt = nextStmt;
1730
1731                // Inline common setup (equivalent of super.doParseStatement + field init)
1732                if (nextStmt.sourcetokenlist.size() == 0) {
1733                    nextStmt.setStartToken(leftNode.getStartToken());
1734                    nextStmt.setEndToken(leftNode.getEndToken());
1735                }
1736                // Equivalent of TCustomSqlStatement.doParseStatement(lcParentSql)
1737                nextStmt.setParentStmt(lcParentSql);
1738                nextStmt.setFrameStack(lcParentSql.getFrameStack());
1739                lcParentSql.getStmtScope().incrementCurrentStmtIndex();
1740                nextStmt.setQueryId(String.format("%s#stmt_%d_%s",
1741                        lcParentSql.getQueryId(),
1742                        lcParentSql.getStmtScope().getCurrentStmtIndex(),
1743                        nextStmt.sqlstatementtype));
1744                nextStmt.setStmtScope(new TStmtScope(lcParentSql.getStmtScope(), nextStmt));
1745                if (nextStmt.getStartToken() == null && nextStmt.rootNode != null) {
1746                    nextStmt.setStartToken(nextStmt.rootNode.getStartToken());
1747                }
1748                if (nextStmt.getEndToken() == null && nextStmt.rootNode != null) {
1749                    nextStmt.setEndToken(nextStmt.rootNode.getEndToken());
1750                }
1751                if (nextStmt.getGsqlparser() == null && nextStmt.rootNode != null) {
1752                    nextStmt.setGsqlparser(nextStmt.rootNode.getGsqlparser());
1753                }
1754
1755                nextStmt.selectToken = leftNode.getSelectToken();
1756                nextStmt.parenthesisCount = leftNode.getParenthesisCount();
1757                nextStmt.parenthesisCountBeforeOrder = leftNode.getParenthissisCountBeforeOrder();
1758                nextStmt.setOperator = leftNode.getSetOperator();
1759                nextStmt.initSetOperatorFields();
1760
1761                stmtChain.add(nextStmt);
1762                nodeChain.add(leftNode);
1763
1764                currentStmt = nextStmt;
1765                currentNode = leftNode;
1766            }
1767
1768            // Create and process the leftmost leaf (non-combined SELECT)
1769            TSelectSqlStatement leftmostLeaf = new TSelectSqlStatement(dbvendor);
1770            leftmostLeaf.rootNode = currentNode.getLeftNode();
1771            leftmostLeaf.setChildOfCombinedQuery(true);
1772            currentStmt.leftStmt = leftmostLeaf;
1773            leftmostLeaf.doParseStatement(lcParentSql);
1774
1775            // Process right children and clauses from deepest to shallowest
1776            for (int i = stmtChain.size() - 1; i >= 0; i--) {
1777                TSelectSqlStatement stmt = stmtChain.get(i);
1778                TSelectSqlNode node = nodeChain.get(i);
1779
1780                // Create and process right child (always a leaf for left-recursive grammar)
1781                stmt.rightStmt = new TSelectSqlStatement(dbvendor);
1782                stmt.rightStmt.rootNode = node.getRightNode();
1783                stmt.rightStmt.setChildOfCombinedQuery(true);
1784                stmt.rightStmt.doParseStatement(lcParentSql);
1785
1786                // Handle clauses for this level
1787                if (node.getOrderbyClause() != null) {
1788                    node.getOrderbyClause().doParse(stmt, ESqlClause.orderby);
1789                    stmt.orderbyClause = node.getOrderbyClause();
1790                }
1791                stmt.limitClause = node.getLimitClause();
1792                if (node.getForupdateClause() != null) {
1793                    node.getForupdateClause().doParse(stmt, ESqlClause.forUpdate);
1794                    stmt.forUpdateClause = node.getForupdateClause();
1795                }
1796                if (node.getComputeClause() != null) {
1797                    node.getComputeClause().doParse(stmt, ESqlClause.compute);
1798                    stmt.computeClause = node.getComputeClause();
1799                }
1800                stmt.optionClause = node.getOptionClause();
1801                stmt.timeTravel = node.getTimeTravel();
1802                stmt.hintClause = node.getHintClause();
1803            }
1804
1805            return 0;
1806        }
1807
1808        if (selectNode.getValueClause() != null){
1809            // WITH c AS (...) VALUES (...): the rows may read the CTEs, so the list comes first (Mantis 5017); the
1810            // early return below used to drop it
1811            if (selectNode.cteList != null){
1812                this.setCteList(selectNode.cteList);
1813                this.getCteList().doParse(this,ESqlClause.cte);
1814            }
1815            this.valueClause = selectNode.getValueClause();
1816            this.valueClause.doParse(this,ESqlClause.selectValue);
1817            isValueClause = true;
1818            return 0;
1819        }
1820
1821        if (selectNode.getTransformClause() != null){
1822            this.transformClause = selectNode.getTransformClause();
1823            this.transformClause.doParse(this,ESqlClause.transformClause);
1824        }
1825
1826        if (selectNode.cteList != null){
1827            this.setCteList(selectNode.cteList);
1828            this.getCteList().doParse(this,ESqlClause.cte);
1829        }
1830
1831        // Oracle 12c WITH FUNCTION/PROCEDURE support
1832        if (selectNode.getWithPlsqlItems() != null){
1833            this.setWithPlsqlItems(selectNode.getWithPlsqlItems());
1834        }
1835
1836        if (selectNode.isHiveFromQuery()){
1837            if(selectNode.getSelectSqlNodes() != null){
1838                for(int i=1;i<selectNode.getSelectSqlNodes().size();i++){
1839                    //start from the second, the first select statement is represented by this class instance.
1840
1841                    TSelectSqlStatement select = new TSelectSqlStatement(EDbVendor.dbvhive);
1842                    select.rootNode = selectNode.getSelectSqlNodes().get(i);
1843                    select.doParseStatement(this);
1844                    this.getMultiSelectStatements().add(select);
1845                }
1846            }
1847
1848            // Hive: from query insert...
1849//            if (selectNode.getFromTableList() != null){
1850//                TFromTable lcFromTable;
1851//                TJoin lcJoin;
1852//
1853//                for(int i=0; i<selectNode.getFromTableList().size();i++){
1854//                    lcFromTable = selectNode.getFromTableList().getFromTable(i);
1855//                    TTable lcTable = null;
1856//
1857//                    if (lcFromTable.getFromtableType() != ETableSource.join){
1858//                        lcJoin = new TJoin();
1859//                        lcTable = analyzeFromTable(lcFromTable,true);
1860//                        lcTable.setEffectType(ETableEffectType.tetSelect);
1861//                        lcJoin.setTable(lcTable);
1862//                        lcJoin.setStartToken(lcJoin.getTable().getStartToken());
1863//                        lcJoin.setEndToken(lcJoin.getTable().getEndToken());
1864//                        lcJoin.setGsqlparser(getGsqlparser());
1865//                    }else{
1866//                        lcJoin = analyzeJoin(lcFromTable.getJoinExpr(),null,true);
1867//                        lcJoin.doParse(this, ESqlClause.join);
1868//
1869//                        if (lcFromTable.getLateralViewList() != null){
1870//                            for(TLateralView lateralView:lcFromTable.getLateralViewList()){
1871//                                addToTables(lateralView.createATable(this));
1872//                            }
1873//                        }
1874//                    }
1875//
1876//                    joins.addJoin(lcJoin);
1877//                }
1878//            }
1879
1880
1881//            if (selectNode.getHiveBodyList() != null){
1882//                for(int i = 0; i<selectNode.getHiveBodyList().size();i++){
1883//                    TParseTreeNode node1 = selectNode.getHiveBodyList().get(i);
1884//                    switch (node1.getNodeType()){
1885//                        case TStatementSqlNode.select:
1886//                            TSelectSqlStatement select = new TSelectSqlStatement(EDbVendor.dbvhive);
1887//                            select.rootNode = node1;
1888//                            select.doParseStatement(this);
1889//                            this.getHiveBodyList().add(select);
1890//
1891//                            break;
1892//                        case TStatementSqlNode.insert:
1893//
1894//                            TInsertSqlStatement insert = new TInsertSqlStatement(EDbVendor.dbvhive);
1895//                            insert.rootNode = node1;
1896//                            insert.doParseStatement(this);
1897//                            this.getHiveBodyList().add(insert);
1898//                            break;
1899//                        default:
1900//                            break;
1901//                    }
1902//                }
1903//            }
1904           // return 0;
1905        }
1906
1907        // get oracle hint
1908        if((dbvendor == EDbVendor.dbvoracle)||isMySQLDialect()){ // MySQL includes an OceanBase MySQL tenant (Mantis 4813)
1909            TSourceToken lcnextst = this.selectToken.container.nextsolidtoken(this.selectToken.posinlist,1,true);
1910            while (lcnextst != null){
1911                if (!((lcnextst.tokentype == ETokenType.ttsimplecomment) || (lcnextst.tokentype == ETokenType.ttbracketedcomment))) break;
1912
1913                if (lcnextst.tokentype == ETokenType.ttbracketedcomment){
1914                    if (lcnextst.toString().startsWith("/*+")){
1915                       //lcnextst.setDbObjType(TObjectName.ttObjOracleHint);
1916                       lcnextst.setDbObjectType(EDbObjectType.oracleHint);
1917                       if (oracleHint == null){
1918                           oracleHint = lcnextst.toString();
1919                       }else {
1920                           oracleHint = oracleHint + " " + lcnextst.toString();
1921                       }
1922                    }
1923                }
1924
1925                if (lcnextst.tokentype == ETokenType.ttsimplecomment){
1926                    if (lcnextst.toString().startsWith("--+")){
1927                       //lcnextst.setDbObjType(TObjectName.ttObjOracleHint);
1928                        lcnextst.setDbObjectType(EDbObjectType.oracleHint);
1929                       if (oracleHint == null){
1930                           oracleHint = lcnextst.toString();
1931                       }else {
1932                           oracleHint = oracleHint + " " + lcnextst.toString();
1933                       }
1934                    }
1935                }
1936
1937                lcnextst = lcnextst.container.nextsolidtoken(lcnextst,1,true);
1938            }
1939
1940            hint = oracleHint;
1941        }
1942
1943        if (selectNode.getHiveHintClause() != null){
1944            hiveHintClause = selectNode.getHiveHintClause();
1945        }
1946
1947        isConsume = selectNode.isConsume();
1948
1949        if (selectNode.getTopClause() != null){
1950           selectNode.getTopClause().doParse(this,ESqlClause.top);
1951            this.setTopClause(selectNode.getTopClause()); 
1952        }
1953
1954 //       this.skipOffset = selectNode.getSkipOffset();
1955//        this.firstMax = selectNode.getFirstMax();
1956//        this.limitMax = selectNode.getLimitMax();
1957
1958        if (selectNode.getSelectDistinct() != null){
1959            this.selectDistinct = selectNode.getSelectDistinct();
1960        }
1961
1962        this.selectModifiers = selectNode.getSelectModifiers();
1963
1964        TFromTable lcFromTable = null;
1965        TJoin lcJoin = null;
1966
1967        if (selectNode.getFromTableList() != null){
1968            fromClause = new TFromClause(this.getRelations());
1969            fromClause.setStartToken(selectNode.getFromTableList().getStartToken());
1970            fromClause.setEndToken(selectNode.getFromTableList().getEndToken());
1971
1972            for(int i=0; i<selectNode.getFromTableList().size();i++){
1973                 lcFromTable = selectNode.getFromTableList().getFromTable(i);
1974                 lcJoin = analyzeTableOrJoin(lcFromTable);
1975                 joins.addJoin(lcJoin);
1976            }
1977        }
1978
1979        // ClickHouse expression-CTEs (WITH <expr> AS ident) are evaluated in
1980        // this statement's own scope, so their column references can only
1981        // link once the FROM clause above has populated tables — the
1982        // cteList.doParse earlier in this method runs before that.
1983        if ((dbvendor == EDbVendor.dbvclickhouse) && (this.getCteList() != null)){
1984            for(int i=0;i<getCteList().size();i++){
1985                TCTE lcCte = getCteList().getCTE(i);
1986                if (lcCte.getExpression() != null){
1987                    lcCte.getExpression().doParse(this,ESqlClause.cte);
1988                }
1989            }
1990        }
1991
1992        // Publish the hierarchical clause before the select list is parsed.
1993        // doParse() for the clause itself still runs in its usual place below;
1994        // this only makes getHierarchicalClause() answer truthfully while the
1995        // result columns are being linked, which is when
1996        // TCustomSqlStatement.isOraclePseudoColumnReference() needs to know
1997        // whether CONNECT_BY_ISLEAF / CONNECT_BY_ISCYCLE are pseudocolumns
1998        // here or ordinary identifiers (Mantis #4675).
1999        if (selectNode.getHierarchicalClause() != null){
2000            this.hierarchicalClause = selectNode.getHierarchicalClause();
2001        }
2002
2003        if (selectNode.getResultColumnList() != null){
2004            this.setResultColumnList(selectNode.getResultColumnList());
2005
2006//            if ((this.getResultColumnList().size() > 10)
2007//                    &&(this.getTables().size() == 1)
2008//                    &&(this.getTables().getTable(0).getTableType() == ETableSource.subquery)
2009//                    &&( this.getTables().getTable(0).getSubquery().getResultColumnList().size() == this.getResultColumnList().size() )
2010//            ){
2011//                TTable t0 = this.getTables().getTable(0);
2012//                ArrayList<TResultColumn> targetColumnArrayList = this.getResultColumnList().getSortedColumns();
2013//                ArrayList<TResultColumn> sourceColumnArrayList = t0.getSubquery().getResultColumnList().getSortedColumns();
2014//                int i=0;
2015//                for(TResultColumn rc:targetColumnArrayList){
2016//                    TObjectName targetColumn = rc.getExpr().getObjectOperand();
2017//                    TResultColumn sourceResultColumn = sourceColumnArrayList.get(i++);
2018//                    //TObjectName sourceColumn = sourceResultColumn.getExpr().getObjectOperand();
2019//                    if (targetColumn != null){
2020//                        //System.out.println(targetColumn+"\t->\t"+sourceResultColumn.getDisplayName());
2021//                        targetColumn.setSourceTable(t0);
2022//                        t0.getLinkedColumns().addObjectName(targetColumn);
2023//                        targetColumn.setSourceColumn(sourceResultColumn);
2024//                        sourceResultColumn.getTargetColumns().addObjectName(targetColumn);
2025//                    }
2026//                }
2027//            }
2028
2029            selectNode.getResultColumnList().doParse(this,ESqlClause.selectList);
2030
2031            // Slice 73: link DISTINCT ON expression list (PostgreSQL /
2032            // Greenplum `SELECT DISTINCT ON (cols) ...`) so each
2033            // TObjectName inside gets dbObjectType=column and
2034            // sourceTable populated. Without this, the expressions
2035            // remain EDbObjectType.unknown. The legacy column-linker
2036            // (linkColumnToTable) is invoked here via TExpression.doParse;
2037            // resolver-2 picks the same expressions up via the
2038            // TSelectDistinct.acceptChildren descent that slice 73
2039            // also wires up. Tables are populated by the FROM clause
2040            // loop above so column linking has a relation list to
2041            // resolve against. Mirrors the TGroupBy.doParse →
2042            // items.doParse pattern.
2043            if (this.selectDistinct != null
2044                    && this.selectDistinct.getExpressionList() != null) {
2045                TExpressionList distinctOnExprList =
2046                        this.selectDistinct.getExpressionList();
2047                for (int i = 0; i < distinctOnExprList.size(); i++) {
2048                    TExpression e = distinctOnExprList.getExpression(i);
2049                    if (e != null) {
2050                        e.doParse(this, ESqlClause.unknown);
2051                    }
2052                }
2053            }
2054
2055            for(int i=0;i<selectNode.getResultColumnList().size();i++){
2056                TResultColumn resultColumn = selectNode.getResultColumnList().getResultColumn(i);
2057                TExpression expression = resultColumn.getExpr();
2058                String expressionText = expression.toString();
2059                if (expressionText.endsWith("*")){
2060                    TTable starSourceTable = null;
2061                    //System.out.println(resultColumn.toString()+":"+resultColumn.getStartToken().lineNo+","+resultColumn.getStartToken().columnNo);
2062                    if (expressionText.startsWith("*")){
2063                        // add all result columns in from tables
2064                        for(int j=0;j<getTables().size();j++){
2065                            if (getTables().getTable(j).getTableType() == ETableSource.subquery){
2066                                getExpandedResultColumns().putAll(getTables().getTable(j).getSubquery().getExpandedResultColumns());
2067                            }else{
2068                                // ToDo for real table
2069                            }
2070                            //System.out.println("\t"+getTables().getTable(j).toString()+",\t"+getTables().getTable(j).getTableType()+",\t"+getTables().getTable(j).isCTEName());
2071                        }
2072                        if (getTables().size() == 1){
2073                            starSourceTable = getTables().getTable(0);
2074                        }
2075
2076                    }else{
2077                        for(int j=0;j<getTables().size();j++){
2078                            if (getTables().getTable(j).getAliasName().equalsIgnoreCase( resultColumn.toString().split("[.]")[0])){
2079                                if (getTables().getTable(j).getTableType() == ETableSource.subquery){
2080                                    getExpandedResultColumns().putAll(getTables().getTable(j).getSubquery().getExpandedResultColumns());
2081                                }else{
2082                                    // ToDo for real table
2083                                }
2084                                starSourceTable = getTables().getTable(j);
2085                                break;
2086                            }
2087                        }
2088                    }
2089                    if (starSourceTable != null) {
2090                        TObjectName objectOperand = expression.getObjectOperand();
2091                        if (objectOperand == null) {
2092                            return reportInvalidSelectStatement();
2093                        }
2094                        objectOperand.setSourceTable(starSourceTable);
2095                    }
2096                }else{
2097                    String expandedKey = IdentifierService.normalizeStatic(this.dbvendor, ESQLDataObjectType.dotColumn,resultColumn.getDisplayName());
2098                    if (expandedKey == null){
2099                        return reportInvalidSelectStatement();
2100                    }
2101                    getExpandedResultColumns().put(expandedKey,resultColumn);
2102                }
2103            }
2104        }
2105
2106        if (selectNode.getIntoClause() != null){
2107            this.intoClause = selectNode.getIntoClause();
2108            this.intoClause.doParse(this,ESqlClause.selectInto);
2109        }
2110
2111
2112        if (selectNode.getWhereCondition() != null){
2113            selectNode.getWhereCondition().doParse(this,ESqlClause.where);
2114            this.setWhereClause( selectNode.getWhereCondition());
2115        }
2116
2117        if (selectNode.getHierarchicalClause() != null){
2118         selectNode.getHierarchicalClause().doParse(this,ESqlClause.hierarchical);
2119         this.hierarchicalClause = selectNode.getHierarchicalClause();
2120        }
2121
2122        if (selectNode.getGroupByClause() != null){
2123            selectNode.getGroupByClause().doParse(this,ESqlClause.groupby);
2124            this.groupByClause = selectNode.getGroupByClause(); 
2125        }
2126
2127        if (selectNode.getQualifyClause() != null){
2128            selectNode.getQualifyClause().doParse(this,ESqlClause.qualify);
2129            this.qualifyClause = selectNode.getQualifyClause(); 
2130        }
2131
2132        if (selectNode.getSampleClause() != null){
2133            selectNode.getSampleClause().doParse(this,ESqlClause.sample);
2134            this.sampleClause = selectNode.getSampleClause();
2135        }
2136
2137        if (selectNode.getExpandOnClause() != null){
2138            selectNode.getExpandOnClause().doParse(this,ESqlClause.expandOn);
2139            this.expandOnClause = selectNode.getExpandOnClause();
2140        }
2141
2142        if (selectNode.getWithClause() != null){
2143            selectNode.getWithClause().doParse(this,ESqlClause.teradataWith);
2144            this.teradataWithClause  = selectNode.getWithClause();
2145        }
2146
2147        if (selectNode.getOrderbyClause() != null){
2148            selectNode.getOrderbyClause().doParse(this,ESqlClause.orderby);
2149            this.orderbyClause = selectNode.getOrderbyClause(); 
2150        }
2151
2152        if (selectNode.getLockingClauses() != null){
2153           selectNode.getLockingClauses().doParse(this,ESqlClause.lockingClause);
2154            this.lockingClauses = selectNode.getLockingClauses();
2155        }
2156
2157        this.sortBy = selectNode.getSortBy();
2158        if (this.sortBy != null){
2159            this.sortBy.doParse(this,ESqlClause.sortby);
2160        }
2161
2162        this.clusterBy = selectNode.getClusterBy();
2163        if (this.clusterBy != null){
2164            this.clusterBy.doParse(this,ESqlClause.cluster);
2165        }
2166
2167        this.windowClause = selectNode.getWindowClause();
2168        if (this.windowClause != null){
2169            this.windowClause.doParse(this,ESqlClause.windowClause);
2170        }
2171        this.limitClause = selectNode.getLimitClause();
2172        if (this.limitClause != null){
2173            this.limitClause.doParse(this,ESqlClause.limit);
2174        }
2175
2176       if (selectNode.getForupdateClause() != null){
2177            selectNode.getForupdateClause().doParse(this,ESqlClause.forUpdate);
2178            this.forUpdateClause  = selectNode.getForupdateClause();
2179        }
2180
2181        if (selectNode.getComputeClause() != null){
2182            selectNode.getComputeClause().doParse(this,ESqlClause.compute);
2183            this.computeClause = selectNode.getComputeClause(); 
2184        }
2185
2186        this.intoTableClause = selectNode.getIntoTableClause();
2187
2188        if (selectNode.getDistributeBy() != null){
2189            this.distributeBy = selectNode.getDistributeBy();
2190            this.distributeBy.doParse(this,ESqlClause.distributeBy);
2191        }
2192
2193
2194        this.isolationClause = selectNode.getIsolationClause();
2195
2196        this.optionClause = selectNode.getOptionClause();
2197
2198        this.offsetClause = selectNode.getOffsetClause();
2199        this.fetchFirstClause = selectNode.getFetchFirstClause();
2200
2201        this.timeTravel = selectNode.getTimeTravel();
2202        this.hintClause = selectNode.getHintClause();
2203
2204        if (selectNode.getForXMLClause() != null){
2205            if (tables.size() > 0){
2206                tables.getTable(0).setForXMLClause(selectNode.getForXMLClause());
2207            }
2208        }
2209
2210        // StarRocks pipe operator support
2211        this.pipeOperators = selectNode.getPipeOperators();
2212        this.isPipeQuery = selectNode.isPipeQuery();
2213
2214        return 0;
2215    }
2216
2217    private boolean hasInvalidResultColumnList(TSelectSqlNode selectNode,
2218            TCustomSqlStatement psql) {
2219        TResultColumnList resultColumns = selectNode.getResultColumnList();
2220        if (resultColumns == null) {
2221            return false;
2222        }
2223
2224        for (int i = 0; i < resultColumns.size(); i++) {
2225            TResultColumn resultColumn = resultColumns.getResultColumn(i);
2226            if ((resultColumn == null) || (resultColumn.getExpr() == null)) {
2227                return true;
2228            }
2229
2230            TExpression expression = resultColumn.getExpr();
2231            String expressionText = expression.toString();
2232            if (expressionText == null) {
2233                return true;
2234            }
2235            if (!expressionText.endsWith("*")
2236                    && (resultColumn.getDisplayName() == null)) {
2237                return true;
2238            }
2239        }
2240
2241        if (((dbvendor == EDbVendor.dbvmssql)
2242                || (dbvendor == EDbVendor.dbvoracle))
2243                && (psql instanceof TInsertSqlStatement)
2244                && (resultColumns.getEndToken() != null)
2245                && (resultColumns.getEndToken().container != null)) {
2246            TSourceToken resultListEnd = resultColumns.getEndToken();
2247            TSourceToken nextToken = resultListEnd.container
2248                    .nextsolidtoken(resultListEnd, 1, true);
2249            if ((nextToken != null) && (nextToken.tokencode == ',')) {
2250                TSourceToken tokenAfterComma = nextToken.container
2251                        .nextsolidtoken(nextToken, 1, true);
2252                if ((tokenAfterComma != null)
2253                        && (tokenAfterComma.tokencode == TBaseType.rrw_from)) {
2254                    return true;
2255                }
2256            }
2257        }
2258
2259        return false;
2260    }
2261
2262    private int reportInvalidSelectStatement() {
2263        TSourceToken errorToken = rootNode.getStartToken();
2264        if ((errorToken == null) && (sourcetokenlist.size() > 0)) {
2265            errorToken = sourcetokenlist.get(0);
2266        }
2267        if (errorToken != null) {
2268            parseerrormessagehandle(new TSyntaxError(errorToken,
2269                    "invalid SELECT statement", EErrorType.sperror,
2270                    TBaseType.MSG_ERROR_SYNTAX_ERROR, this));
2271        } else {
2272            parseerrormessagehandle(new TSyntaxError("SELECT", 0, 0,
2273                    "invalid SELECT statement", EErrorType.sperror,
2274                    TBaseType.MSG_ERROR_SYNTAX_ERROR, this, -1));
2275        }
2276        return TBaseType.MSG_ERROR_SYNTAX_ERROR;
2277    }
2278
2279    /**
2280     * Determine whether The UNION, EXCEPT and INTERSECT operators were used.
2281     * @return true if one of those operators were used.
2282     */
2283    public boolean isCombinedQuery(){
2284    // combine multiple queries using the set operators UNION, UNION ALL, INTERSECT, and MINUS.
2285        return (setOperator > 0);
2286    }
2287
2288    /**
2289     * Initialize setOperatorType, all, and setOpDistinct fields from setOperator value.
2290     * Used by doParseStatement's iterative combined query construction.
2291     */
2292    private void initSetOperatorFields() {
2293        switch (setOperator) {
2294            case SET_OPERATOR_NONE:
2295                setOperatorType = ESetOperatorType.none; all = false; break;
2296            case setOperator_union:
2297                setOperatorType = ESetOperatorType.union; all = false; break;
2298            case setOperator_unionall:
2299                setOperatorType = ESetOperatorType.union; all = true; break;
2300            case SET_OPERATOR_UNIONDISTINCT:
2301                setOperatorType = ESetOperatorType.union; setOpDistinct = true; break;
2302            case setOperator_intersect:
2303                setOperatorType = ESetOperatorType.intersect; all = false; break;
2304            case setOperator_intersectall:
2305                setOperatorType = ESetOperatorType.intersect; all = true; break;
2306            case setOperator_except:
2307                setOperatorType = ESetOperatorType.except; all = false; break;
2308            case setOperator_exceptall:
2309                setOperatorType = ESetOperatorType.except; all = true; break;
2310            case setOperator_minus:
2311                setOperatorType = ESetOperatorType.minus; all = false; break;
2312            case setOperator_minusall:
2313                setOperatorType = ESetOperatorType.minus; all = true; break;
2314            case SET_OPERATOR_INTERSECTDISTINCT:
2315                setOperatorType = ESetOperatorType.intersect; setOpDistinct = true; break;
2316            case SET_OPERATOR_EXCEPTDISTINCT:
2317                setOperatorType = ESetOperatorType.except; setOpDistinct = true; break;
2318        }
2319    }
2320
2321    public void accept(TParseTreeVisitor v)
2322    {
2323        v.preVisit(this);
2324        v.postVisit(this);
2325    }
2326
2327    /**
2328     * Accept visitor to visit this class.
2329     * @param v user defined visitor.
2330     */
2331    public void acceptChildren(TParseTreeVisitor v)
2332    {
2333
2334        if(this.isCombinedQuery()){
2335
2336            // Iterative traversal for combined queries to avoid StackOverflow
2337            // on deeply nested left-recursive UNION/INTERSECT/EXCEPT chains.
2338            // Collect the left-recursive chain of combined query nodes.
2339            ArrayList<TSelectSqlStatement> chain = new ArrayList<>();
2340            TSelectSqlStatement current = this;
2341            while (current.isCombinedQuery()) {
2342                chain.add(current);
2343                current = current.getLeftStmt();
2344            }
2345            // 'current' is now the leftmost leaf (non-combined SELECT)
2346
2347            // Phase 1: preVisit all combined nodes and their CTEs, from root to deepest
2348            for (int i = 0; i < chain.size(); i++) {
2349                TSelectSqlStatement node = chain.get(i);
2350                v.preVisit(node);
2351                if (node.getCteList() != null) {
2352                    node.getCteList().acceptChildren(v);
2353                }
2354            }
2355
2356            // Phase 2: Visit leftmost leaf SELECT
2357            current.acceptChildren(v);
2358
2359            // Phase 3: Visit right branches and postVisit, from deepest to root
2360            for (int i = chain.size() - 1; i >= 0; i--) {
2361                TSelectSqlStatement node = chain.get(i);
2362                node.getRightStmt().acceptChildren(v);
2363
2364                if (node.getOrderbyClause() != null) {
2365                    node.getOrderbyClause().acceptChildren(v);
2366                }
2367                if (node.getLimitClause() != null) {
2368                    node.getLimitClause().acceptChildren(v);
2369                }
2370                if (node.getForUpdateClause() != null) {
2371                    node.getForUpdateClause().acceptChildren(v);
2372                }
2373                if (node.getComputeClause() != null) {
2374                    node.getComputeClause().acceptChildren(v);
2375                }
2376                v.postVisit(node);
2377            }
2378
2379            return ;
2380        }
2381
2382        // System.out.println(this.toString().hashCode()+" "+ this.toString().substring(0,50));
2383
2384        v.preVisit(this);
2385
2386        if (this.getTransformClause() != null){
2387            this.getTransformClause().acceptChildren(v);
2388        }
2389
2390        if (this.getCteList() != null){
2391            this.getCteList().acceptChildren(v);
2392        }
2393
2394        if (TBaseType.USE_JOINEXPR_INSTEAD_OF_JOIN){
2395//            fromClause.acceptChildren(v);
2396            v.preVisit(fromClause);
2397            for(TTable table:getRelations()){
2398                table.acceptChildren(v);
2399            }
2400            v.postVisit(fromClause);
2401        }else{
2402            if (this.getJoins() != null){
2403                this.getJoins().acceptChildren(v);
2404            }
2405        }
2406
2407        if (this.getHiveHintClause() != null){
2408            this.getHiveHintClause().acceptChildren(v);
2409        }
2410
2411
2412        if (this.getTopClause() != null){
2413            this.getTopClause().acceptChildren(v);
2414        }
2415
2416        if (this.getSelectDistinct() != null){
2417            this.getSelectDistinct().acceptChildren(v);
2418        }
2419        
2420        if (this.getResultColumnList() != null){
2421            this.getResultColumnList().acceptChildren(v);
2422        }
2423
2424        if (this.getIntoClause() != null){
2425            this.getIntoClause().acceptChildren(v);
2426        }
2427
2428
2429        if (this.getWhereClause() != null){
2430            this.getWhereClause().acceptChildren(v);
2431        }
2432
2433        if (this.getHierarchicalClause() != null){
2434            this.getHierarchicalClause().acceptChildren(v);
2435        }
2436
2437        if (this.getGroupByClause() != null){
2438            this.getGroupByClause().acceptChildren(v);
2439        }
2440
2441        if (this.getQualifyClause() != null){
2442            this.getQualifyClause().acceptChildren(v);
2443        }
2444
2445        if (this.getSampleClause() != null){
2446            this.getSampleClause().acceptChildren(v);
2447        }
2448
2449        if (this.getExpandOnClause() != null){
2450            this.getExpandOnClause().acceptChildren(v);
2451        }
2452
2453        if (this.getTeradataWithClause() != null){
2454            this.getTeradataWithClause().acceptChildren(v);
2455        }
2456
2457        if (this.getOrderbyClause() != null){
2458            this.getOrderbyClause().acceptChildren(v);
2459        }
2460
2461        if (this.getLockingClauses() != null){
2462            this.getLockingClauses().acceptChildren(v);
2463        }
2464
2465        if (this.getSortBy() != null){
2466            this.getSortBy().acceptChildren(v);
2467        }
2468
2469        if (this.getClusterBy() != null){
2470            this.getClusterBy().acceptChildren(v);
2471        }
2472
2473        if (this.getWindowClause() != null){
2474            this.getWindowClause().acceptChildren(v);
2475        }
2476
2477        if (this.getLimitClause() != null){
2478            this.getLimitClause().acceptChildren(v);
2479        }
2480
2481        if (this.getForUpdateClause() != null){
2482            this.getForUpdateClause().acceptChildren(v);
2483        }
2484
2485        if (this.getComputeClause() != null){
2486            this.getComputeClause().acceptChildren(v);
2487        }
2488
2489        if (this.getIntoTableClause() != null){
2490            this.getIntoTableClause().acceptChildren(v);
2491        }
2492
2493        if (this.getDistributeBy() != null){
2494            this.getDistributeBy().acceptChildren(v);
2495        }
2496
2497        if (this.getIsolationClause() != null){
2498            this.getIsolationClause().acceptChildren(v);
2499        }
2500
2501        if (this.getOptionClause() != null){
2502            this.getOptionClause().acceptChildren(v);
2503        }
2504
2505        if (this.getOffsetClause() != null){
2506            this.getOffsetClause().acceptChildren(v);
2507        }
2508
2509        if (this.getFetchFirstClause() != null){
2510            this.getFetchFirstClause().acceptChildren(v);
2511        }
2512
2513        if (this.getTimeTravel() != null){
2514            this.getTimeTravel().acceptChildren(v);
2515        }
2516
2517        if (this.getHintClause() != null){
2518            this.getHintClause().acceptChildren(v);
2519        }
2520
2521        v.postVisit(this);
2522        return ;
2523
2524    };
2525
2526    /**
2527     *  Add an order by clause to this class.
2528     * @param orderByStr new order by string
2529     * @return added order by clause.
2530     */
2531    public  TOrderBy addOrderBy(String orderByStr){
2532        if (this.getOrderbyClause() != null){
2533            this.getOrderbyClause().addOrderByItem(orderByStr);
2534        }else{
2535
2536            TOrderBy orderBy = new TOrderBy();
2537            orderBy.setGsqlparser(this.getGsqlparser());
2538            orderBy.setString(" order by "+orderByStr);
2539            this.orderbyClause = orderBy;
2540            TSourceToken last_token = null;
2541            if (this.getGroupByClause() != null){
2542                last_token = this.getGroupByClause().getEndToken();
2543            }else if (this.getWhereClause() != null){
2544                last_token = this.getWhereClause().getEndToken();
2545            }else{
2546                last_token = joins.getEndToken();
2547            }
2548
2549            orderbyClause.addAllMyTokensToTokenList(last_token.container, last_token.posinlist+1);
2550
2551           for(int i=0;i<last_token.getNodesEndWithThisToken().size();i++){
2552               TParseTreeNode node = last_token.getNodesEndWithThisToken().get(i);
2553               if (node instanceof TSelectSqlStatement)
2554               {
2555                   // change all end token of parse tree node except the last order by item
2556                   node.setEndToken(orderbyClause.getEndToken());
2557               }
2558           }
2559        }
2560
2561        return this.getOrderbyClause();
2562    }
2563
2564    /**
2565     *  this function is alias of addWhereClause
2566     * @param condition new condition
2567     * @return new where clause
2568     */
2569    public TWhereClause addCondition(String condition){
2570        return  addWhereClause(condition);
2571    }
2572
2573    public TWhereClause addConditionOR(String condition){
2574        return  addWhereClauseOR(condition);
2575    }
2576
2577    public  TWhereClause addWhereClause(String condition){
2578       return doAddWhereClause(condition,true);
2579    }
2580
2581    public  TWhereClause addWhereClauseOR(String condition){
2582       return doAddWhereClause(condition,false);
2583    }
2584
2585    protected  TWhereClause doAddWhereClause(String condition, boolean isAnd){
2586
2587        if (this.getWhereClause() != null){
2588                if (isAnd){
2589                    this.getWhereClause().getCondition().addANDCondition(condition);
2590                }else{
2591                    this.getWhereClause().getCondition().addORCondition(condition);
2592                }
2593
2594          }else{
2595             TWhereClause whereClause = new TWhereClause();
2596             whereClause.setGsqlparser(this.getGsqlparser());
2597             whereClause.setString(" where "+condition);
2598             this.setWhereClause(whereClause);
2599
2600            TSourceToken last_token = null;
2601            TJoin lastjoin = joins.getJoin(joins.size()-1);
2602            if (lastjoin.getJoinItems().size() > 0){
2603                TJoinItem lastitem = lastjoin.getJoinItems().getJoinItem(lastjoin.getJoinItems().size()-1);
2604                 last_token = lastitem.getEndToken();
2605            }else {
2606                last_token = joins.getEndToken();
2607            }
2608
2609             whereClause.addAllMyTokensToTokenList(last_token.container, last_token.posinlist+1);
2610
2611            for(int i=0;i<last_token.getNodesEndWithThisToken().size();i++){
2612                TParseTreeNode node = last_token.getNodesEndWithThisToken().get(i);
2613                if (!((node instanceof TJoinList)
2614                        ||(node instanceof TJoin)
2615                        ||(node instanceof TJoinItemList)
2616                        ||(node instanceof TJoinItem)
2617                        ))
2618                {
2619                    // change all end token of parse tree node except the last order by item
2620                    node.setEndToken(whereClause.getEndToken());
2621                }
2622            }
2623
2624          }
2625           return this.getWhereClause();
2626    }
2627
2628    private ArrayList<String> columnsInFromClause = null;
2629
2630
2631    @Override
2632    public TFromClause getFromClause() {
2633        // #todo , select union 类型的 sql 语句,需要穿透到低层的各个 子 select 语句,需要进一步完善
2634        TFromClause ret = super.getFromClause();
2635        if ((ret == null)&&(isCombinedQuery()&&(this.getRightStmt()!=null))){
2636            return   this.getRightStmt().getFromClause();
2637        }else{
2638            return  ret;
2639        }
2640    }
2641
2642    public TResultColumnList getResultColumnList(boolean useColumnsFromLeftmost) {
2643        TResultColumnList ret = super.getResultColumnList();
2644        if (ret != null) return ret;
2645
2646        if (isCombinedQuery()){
2647            if (useColumnsFromLeftmost) {
2648                return this.getFarLeftStmt().getResultColumnList();
2649            } else {
2650                return   this.getRightStmt().getResultColumnList();
2651            }
2652        }
2653        return ret;
2654
2655
2656//        if ((ret == null)&&(isCombinedQuery()&&(this.getRightStmt()!=null))){
2657//            return   this.getRightStmt().getResultColumnList();
2658//        }else{
2659//            return  ret;
2660//        }
2661    }
2662
2663    @Override
2664    public TResultColumnList getResultColumnList() {
2665        return getResultColumnList(false);
2666
2667//        TResultColumnList ret = super.getResultColumnList();
2668//        if ((ret == null)&&(isCombinedQuery()&&(this.getRightStmt()!=null))){
2669//            return   this.getRightStmt().getResultColumnList();
2670//        }else{
2671//            return  ret;
2672//        }
2673    }
2674
2675    public ArrayList<String> getColumnsInFromClause() {
2676        if (columnsInFromClause != null) return columnsInFromClause;
2677        columnsInFromClause = new ArrayList<String>();
2678        TTable lcTable;
2679        for(int j=0;j<tables.size();j++){
2680            lcTable = tables.getTable(j);
2681            switch (lcTable.getTableType()){
2682                case objectname:
2683                    break;
2684                case subquery:
2685                    TSelectSqlStatement subQuery = lcTable.getSubquery();
2686                                        if (subQuery.getResultColumnList() != null)
2687                                                for(int i=0;i<subQuery.getResultColumnList().size();i++){
2688                                                        TResultColumn resultColumn = subQuery.getResultColumnList().getResultColumn(i);
2689                                                        String aliasName = null;
2690                                                        if (resultColumn.getAliasClause() != null){
2691                                                                aliasName = resultColumn.getAliasClause().getAliasName().toString();
2692                                                        }else{
2693                                                                if (resultColumn.getExpr().getObjectOperand() != null){
2694                                                                        aliasName = resultColumn.getExpr().getObjectOperand().toString();
2695                                                                }
2696                                                        }
2697                                                        if (aliasName != null){
2698                                                                columnsInFromClause.add(TSQLEnv.getObjectName(aliasName));
2699                                                        }
2700                                                }
2701                    break;
2702            }
2703        }
2704        return columnsInFromClause;
2705    }
2706
2707    public void addColumnInSelectListToSQLEnv(TSQLTable sqlTable){
2708
2709        if (sqlTable == null) return;
2710        if (this.getValueClause() != null) return;
2711                
2712                if (this.getResultColumnList() != null)
2713                        for(int i=0;i<this.getResultColumnList().size();i++){
2714                                TResultColumn resultColumn = this.getResultColumnList().getResultColumn(i);
2715                                String aliasName = null;
2716                                if ((resultColumn.getAliasClause() != null) && (resultColumn.getAliasClause().getAliasName() == null)){
2717                                        // A tuple alias of Hive and Spark SQL, stack(...) AS (k, v): a column of each name.
2718                                        TObjectNameList tupleColumns = resultColumn.getAliasClause().getColumns();
2719                                        for (int k = 0; (tupleColumns != null) && (k < tupleColumns.size()); k++){
2720                                                sqlTable.addColumn(TSQLEnv.getObjectName(tupleColumns.getObjectName(k).toString()));
2721                                        }
2722                                        continue;
2723                                }
2724                                if (resultColumn.getAliasClause() != null){
2725                                        aliasName = resultColumn.getAliasClause().getAliasName().toString();
2726                                }else{
2727                                        if (resultColumn.getExpr().getObjectOperand() != null){
2728                                                aliasName = resultColumn.getExpr().getObjectOperand().toString();
2729                                        }
2730                                }
2731                                if (aliasName != null){
2732                                        TObjectName operand = resultColumn.getAliasClause() == null ? resultColumn.getExpr().getObjectOperand() : null;
2733                                        if (operand != null && operand.getPartToken() != null
2734                                                        && operand.getPartToken().tokentype == ETokenType.ttasterisk) {
2735                                                // The view's width is not known here: a star, not a column named *.
2736                                                sqlTable.addUnresolvedStar();
2737                                        } else {
2738                                                sqlTable.addColumn(TSQLEnv.getObjectName(aliasName));
2739                                        }
2740                                }
2741                        }
2742    }
2743
2744    /**
2745     * search column in the select list
2746     *
2747     * Internal function, DON'T call it explicitly.
2748     * @param pColumn
2749     * @param pMustIn
2750     * @return true if found.
2751     */
2752    public boolean searchColumnInResultSet(TObjectName pColumn,boolean pMustIn){
2753        boolean lcResult = false;
2754        int candidateTableCnt = 0;
2755        int numOfColumnStar = 0;
2756        TResultColumn columnStar = null;
2757
2758
2759
2760        TResultColumn c = null;
2761        String lookupKey = IdentifierService.normalizeStatic(this.dbvendor, ESQLDataObjectType.dotColumn,pColumn.getColumnNameOnly());
2762        if ( (lookupKey != null) && (c=  this.getExpandedResultColumns().get(lookupKey)) != null)
2763        {
2764            //System.out.println("Found:"+pColumn.toString());
2765            pColumn.setSourceColumn(c);
2766            c.getTargetColumns().addObjectName(pColumn);
2767
2768            return true;
2769        }else{
2770           // System.out.println("NotCatch:"+pColumn.toString());
2771        }
2772
2773        if (isCombinedQuery()){
2774            boolean lcResult1;//,lcResult2;
2775            // search column in all select
2776            lcResult1 = getFarLeftStmt().searchColumnInResultSet(pColumn,pMustIn);
2777            if (!lcResult1)
2778            lcResult1 = rightStmt.searchColumnInResultSet(pColumn,pMustIn);
2779            return  lcResult1;
2780        }
2781
2782        if (getResultColumnList() == null) return  false;
2783
2784        if (pColumn.toString().endsWith("*")){
2785            // pColumn here is o1.*
2786            // select o1.*, o2.c1
2787            // from (select c123, c345 from some_table) o1, other_table o2
2788            pColumn.setColumnsLinkedToStar(this.getResultColumnList());
2789            return true;
2790        }
2791
2792        // search columns in select list, skip t.* but count it for later use
2793        for(int i=0;i<getResultColumnList().size();i++){
2794            TResultColumn lcField = getResultColumnList().getResultColumn(i);
2795            if (lcField.toString().endsWith("*")){
2796                numOfColumnStar++;
2797                columnStar = lcField;
2798                continue;
2799            }
2800            lcResult = lcField.isMatchedWithResultColumn(this.dbvendor, pColumn);
2801            if (lcResult){
2802                pColumn.setSourceColumn(lcField);
2803                lcField.getTargetColumns().addObjectName(pColumn);
2804                break;
2805            }
2806        }
2807
2808        if (lcResult) return true;
2809
2810//        // search in * column, including t.* and *
2811        for(int i=0;i<getResultColumnList().size();i++){
2812            TResultColumn lcField = getResultColumnList().getResultColumn(i);
2813            if (lcField.toString().endsWith("*")){
2814                // search column in t.*, have to check column in table t
2815                String prefixTableName = "";
2816                if (lcField.toString().endsWith(".*")){
2817                    String[] a = lcField.toString().split("[.]");
2818                    prefixTableName = a[a.length - 2];
2819                }
2820                for(int j=0;j<tables.size();j++){
2821                    lcResult = tables.getTable(j).searchColumn(this,prefixTableName,pColumn,false);
2822                    if (lcResult) {
2823//                        tables.getTable(j).getLinkedColumns().addObjectName(pColumn);
2824//                        pColumn.setSourceTable(tables.getTable(j));
2825                        pColumn.setSourceColumn(lcField);
2826                        lcField.getTargetColumns().addObjectName(pColumn);
2827                        break;
2828                    }
2829                }
2830            }else{
2831                continue;
2832            }
2833            if (lcResult) break;
2834        }
2835
2836        if (lcResult) return true;
2837
2838        if (pMustIn){
2839            if (tables.size() == 1){ // only a single table in from clause
2840                lcResult = true;
2841                boolean inStarColumn = false;
2842//                tables.getTable(0).getLinkedColumns().addObjectName(pColumn);
2843//                pColumn.setSourceTable(tables.getTable(0));
2844                for(int i=0;i<getResultColumnList().size();i++) {
2845                    TResultColumn lcField = getResultColumnList().getResultColumn(i);
2846                    if (lcField.toString().endsWith("*")) {
2847                        String prefixTableName = "";
2848                        if (lcField.toString().endsWith(".*")){
2849                            String[] a = lcField.toString().split("[.]");
2850                            prefixTableName = a[a.length - 2];
2851                        }
2852                        tables.getTable(0).searchColumn(this,prefixTableName,pColumn,true);
2853                        pColumn.setSourceColumn(lcField);
2854                        lcField.getTargetColumns().addObjectName(pColumn);
2855                        inStarColumn = true;
2856                        break;
2857                    }
2858                }
2859                if (!inStarColumn){
2860
2861                    if (!locateVariableOrParameter(pColumn)){
2862                        // check variable before raise error
2863
2864                        // raise
2865                        TSyntaxError err = new TSyntaxError(pColumn.getStartToken().toString()
2866                                ,pColumn.getStartToken().lineNo,pColumn.getStartToken().columnNo
2867                                ,String.format("Column %s is not found in subquery",pColumn.toString())
2868                                ,EErrorType.sphint ,TBaseType.MSG_ERROR_COLUMN_NOT_FOUND,this,pColumn.getStartToken().posinlist);
2869                        this.parseerrormessagehandle( err);
2870                    }
2871
2872                }
2873            }else if (numOfColumnStar == 1){// only a single * column in select list, but more than one table in from clause
2874
2875                pColumn.setSourceColumn(columnStar);
2876                columnStar.getTargetColumns().addObjectName(pColumn);
2877                if (columnStar.toString().endsWith(".*")){
2878                    lcResult = true;
2879                    String[] a = columnStar.toString().split("[.]");
2880                    String prefixTableName = a[a.length - 2];
2881                    for(int i=0;i<tables.size();i++){
2882                        if (tables.getTable(i).checkTableByName(prefixTableName)){
2883                            tables.getTable(i).searchColumn(this,prefixTableName,pColumn,true);
2884//                            tables.getTable(i).getLinkedColumns().addObjectName(pColumn);
2885//                            pColumn.setSourceTable(tables.getTable(i));
2886                            pColumn.setSourceColumn(columnStar);
2887                            columnStar.getTargetColumns().addObjectName(pColumn);
2888                            break;
2889                        }
2890                    }
2891                }else{
2892                    lcResult = false;
2893                    // * column, no table prefix, this column can only be in one of tables
2894                    for(int i=0;i<tables.size();i++){
2895                        lcResult = tables.getTable(i).searchColumn(this,"",pColumn,false);
2896                        //tables.getTable(i).getLinkedColumns().addObjectName(pColumn);
2897                        //pColumn.setSourceTable(tables.getTable(i));
2898                        pColumn.setSourceColumn(columnStar);
2899                        columnStar.getTargetColumns().addObjectName(pColumn);
2900                    }
2901                }
2902                if (!lcResult) {
2903                    lcResult = linkToFirstTable(pColumn,0);
2904                }
2905            }else{
2906                // multi tables in from clause and multi * column in select list.
2907                lcResult = linkToFirstTable(pColumn,0);
2908            }
2909        }
2910
2911        return lcResult;
2912
2913        //if (lcResult) return true;
2914
2915        // From now on, let's search in * column, there maybe more than one * column in the select list, such as t1.*, t2.*
2916//        TResultColumn resultColumn = getResultColumnList().getResultColumn(0);
2917//        if ((getResultColumnList().size() == 1) &&(resultColumn.toString().equalsIgnoreCase("*") )){
2918//            // search in * or t1.* in the select list
2919//            int tableCntInFromClause = 0;
2920//            TTable tableInFromClause = null;
2921//            for(int i=0;i<tables.size();i++){
2922//                TTable tn = tables.getTable(i);
2923//                if (tn.getEffectType() == ETableEffectType.tetTeradataReference) continue;
2924//                tableInFromClause = tn;
2925//                tableCntInFromClause++;
2926//            }
2927//            //if (tables.size() == 1){
2928//            if (tableCntInFromClause == 1){
2929//                    //TTable t0 = tables.getTable(0);
2930//                TTable t0 = tableInFromClause;
2931//
2932//                if (t0.isBaseTable()){
2933//                        lcResult = fireOnMetaDatabaseTableColumn(t0.getPrefixServer(),t0.getPrefixDatabase(),t0.getPrefixSchema(),t0.getName(),pColumn.getColumnNameOnly());
2934//                        if (! lcResult) candidateTableCnt++;
2935//                    }else{
2936//                        if (t0.getTableType() == ETableSource.subquery){
2937//                            if (t0.isIncludeColumnAlias()){
2938//                                lcResult = t0.searchColumnInAlias(pColumn);
2939//                            }else{
2940//                                lcResult = t0.getSubquery().searchColumnInResultSet(pColumn,pMustIn);
2941//                                if (! lcResult) candidateTableCnt++;
2942//                            }
2943//                        }else if (t0.isCTEName()){
2944//                            lcResult = t0.getCTE().searchColumnInResultSet(this,t0,pColumn,pMustIn);
2945//                            if (! lcResult) candidateTableCnt++;
2946//                        }
2947//                    }
2948//                    if (pMustIn) lcResult = true;
2949//                    if (lcResult){
2950//                        t0.getLinkedColumns().addObjectName(pColumn);
2951//                        pColumn.setSourceTable(t0);
2952//                        pColumn.setSourceColumn(resultColumn);
2953//                        resultColumn.getTargetColumns().addObjectName(pColumn);
2954//                    }
2955//            }else{ // more than one table
2956//                    candidateTableCnt = 0;
2957//                    for(int i=0;i<tables.size();i++){
2958//                        TTable tn = tables.getTable(i);
2959//                        if (tn.isBaseTable()){
2960//                            lcResult = fireOnMetaDatabaseTableColumn(tn.getPrefixServer(),tn.getPrefixDatabase(),tn.getPrefixSchema(),tn.getName(),pColumn.getColumnNameOnly());
2961//                            if (!lcResult) candidateTableCnt++;
2962//                        }else{
2963//                            if (tn.getTableType() == ETableSource.subquery){
2964//                                if (tn.isIncludeColumnAlias()){
2965//                                    lcResult = tn.searchColumnInAlias(pColumn);
2966//                                }else{
2967//                                    lcResult = tn.getSubquery().searchColumnInResultSet(pColumn,false);
2968//                                    if (!lcResult) candidateTableCnt++;
2969//                                }
2970//                            }else if (tn.isCTEName()){
2971//                                lcResult = tn.getCTE().searchColumnInResultSet(this,tn,pColumn,false);
2972//                                if (!lcResult) candidateTableCnt++;
2973//                            }
2974//                        }
2975//
2976//                        if (lcResult){
2977//                            tn.getLinkedColumns().addObjectName(pColumn);
2978//                            pColumn.setSourceTable(tn);
2979//                            pColumn.setSourceColumn(resultColumn);
2980//                            resultColumn.getTargetColumns().addObjectName(pColumn);
2981//                            break;
2982//                        }
2983//                    }
2984//                    if ((!lcResult) && (pMustIn)) linkToFirstTable(pColumn,candidateTableCnt);
2985//            }
2986//           // }
2987//        }
2988//
2989//        if (lcResult) return true;
2990//
2991//        // search t.*
2992//        //int starColumnPos = 0,
2993//        int numOfTableDotStar=0;
2994//        for (int i=0;i<getResultColumnList().size();i++){
2995//            if (getResultColumnList().getResultColumn(i).toString().endsWith(".*")){
2996//                numOfTableDotStar++;
2997//            }
2998//        }
2999//
3000//        TResultColumn rc = null;
3001//        TTable tn = null;
3002//        for (int i=0;i<getResultColumnList().size();i++){
3003//            if (!getResultColumnList().getResultColumn(i).toString().endsWith(".*")) continue;
3004//            rc = getResultColumnList().getResultColumn(i);
3005//          //  starColumnPos = i;
3006//
3007//            for (int j=0;j<tables.size();j++){
3008//                if (! tables.getTable(j).equalByName(rc.getPrefixTable())) continue;
3009//                tn = tables.getTable(j);
3010//
3011//                if (tn.isBaseTable()){
3012//                    lcResult = fireOnMetaDatabaseTableColumn(tn.getPrefixServer(),tn.getPrefixDatabase(),tn.getPrefixSchema(),tn.getName(),pColumn.getColumnNameOnly());
3013//                    if (!lcResult) candidateTableCnt++;
3014//                }else{
3015//                    if (tn.getTableType() == ETableSource.subquery){
3016//                        if (tn.isIncludeColumnAlias()){
3017//                            lcResult = tn.searchColumnInAlias(pColumn);
3018//                        }else{
3019//                            lcResult = tn.getSubquery().searchColumnInResultSet(pColumn,((pMustIn)&&(numOfTableDotStar == 1)));
3020//                            if (!lcResult) candidateTableCnt++;
3021//                        }
3022//                    }else if (tn.isCTEName()){
3023//                        lcResult = tn.getCTE().searchColumnInResultSet(this,tn,pColumn,((pMustIn)&&(numOfTableDotStar == 1)));
3024//                        if (!lcResult) candidateTableCnt++;
3025//                    }
3026//                }
3027//
3028//                if (lcResult){
3029//                    tn.getLinkedColumns().addObjectName(pColumn);
3030//                    pColumn.setSourceTable(tn);
3031//                    pColumn.setSourceColumn(rc);
3032//                    rc.getTargetColumns().addObjectName(pColumn);
3033//                    break;
3034//                }
3035//            }
3036//            if (lcResult) break;
3037//        }
3038//
3039//        if (lcResult) return true;
3040//
3041//        if ((pMustIn) && (numOfTableDotStar == 1) && (tn != null)){
3042//            tn.getLinkedColumns().addObjectName(pColumn);
3043//            pColumn.setSourceTable(tn);
3044//            pColumn.setSourceColumn(rc);
3045//            rc.getTargetColumns().addObjectName(pColumn);
3046//            lcResult = true;
3047//        }
3048//
3049//        if (lcResult) return true;
3050//
3051//        if ((pMustIn) && (numOfTableDotStar > 0)) lcResult = linkToFirstTable(pColumn,candidateTableCnt);
3052//
3053//        if ((pMustIn)&&(!lcResult)) try {
3054//            throw new Exception(" column must be found in the select list:"+pColumn.toString());
3055//        } catch (Exception e) {
3056//            e.printStackTrace();
3057//        }
3058//
3059//        return lcResult;
3060    }
3061
3062
3063}