001package gudusoft.gsqlparser.nodes;
002
003import gudusoft.gsqlparser.*;
004import gudusoft.gsqlparser.nodes.mysql.TGroupConcatParam;
005import gudusoft.gsqlparser.nodes.oracle.TListaggOverflow;
006import gudusoft.gsqlparser.nodes.teradata.TDataConversionItem;
007import gudusoft.gsqlparser.sqlenv.ESQLDataObjectType;
008import gudusoft.gsqlparser.sqlenv.TSQLEnv;
009import gudusoft.gsqlparser.sqlenv.TSQLFunction;
010import gudusoft.gsqlparser.util.CollectionUtil;
011import gudusoft.gsqlparser.util.SQLUtil;
012import gudusoft.gsqlparser.util.TBuiltFunctionUtil;
013import gudusoft.gsqlparser.util.functionChecker;
014
015import java.io.BufferedReader;
016import java.io.IOException;
017import java.io.InputStreamReader;
018import java.util.*;
019
020
021/**
022 * Represents the database function, all functions are represented by this class no matter what's type of the function.
023 * Maybe it's a better idea to create the specific sub-class for function with different syntax such as the built-in
024 * function extract, convert, substring and others. We may implement this feature in the later version.
025 *
026 * For the most of functions, the arguments are a lists of {@link gudusoft.gsqlparser.nodes.TExpression}. For other
027 * functions with the different syntax in arguments, please check the detailed information below.
028 *
029 * this class includes function name and arguments.
030 * <br>There are 3 types of functions:
031 * <ul>
032 *     <li>Arguments is a list of expressions</li>
033 *     <li>Analytic function</li>
034 *     <li>Function with specific arguments, such as cast function</li>
035 * </ul>
036 *
037 *
038 *     <b>1.Arguments is a list of expressions</b>
039 *     <br>
040* function([expr,expr, ...])
041 * <ul>
042 *     <li>function name: {@link #getFunctionName()}</li>
043 *     <li>args: {@link #getArgs()}</li>
044 *     </ul>
045 * <p>
046 *
047 *     <b>2.Analytic function</b>
048 *     <br>
049 *  Use {@link #getWindowDef()} to get over clause information.
050* <p>
051 *     <b>3.Function with specific arguments</b>
052* <p><b>trim</b>
053 * <ul>
054 *     <li>type: {@link EFunctionType#trim_t}</li>
055 *     <li>arg: {@link #getTrimArgument()}</li>
056 * </ul>
057* <p><b>cast(expr as typename)</b>,
058 * <b>cast(expr as typename [,datatypeAttribute])</b>,
059 * <b>cast(expr as datatypeAttribute)</b>
060 * <ul>
061 *     <li>type:{@link EFunctionType#cast_t}</li>
062 *     <li>expr: {@link #getExpr1()}</li>
063 *     <li>typename: {@link #getTypename()}, datatypeAttribute is included in {@link TTypeName} as well</li>
064 * </ul>
065*
066*
067* <p><b>convert(typename,[null|not null] expr1 [,expr2])</b>,
068 * <ul>
069 *     <li>type: {@link EFunctionType#convert_t}</li>
070 *     <li>expr1: {@link #getExpr1()}</li>
071 *     <li>expr2: {@link #getExpr2()}</li>
072 *     <li>typename: {@link #getTypename()}</li>
073 * </ul>
074*
075* <p><b>Teradata ( udf(args) RETURNS data_type )</b>, the RETURNS clause of a function with a
076 * TD_ANYTYPE result; <b>TRYCAST(expr) RETURNS data_type</b> is a {@link EFunctionType#cast_t}
077 * like TRYCAST(expr AS data_type)
078 * <ul>
079 *     <li>typename: {@link #getTypename()}, the requested result type</li>
080 * </ul>
081*
082* <p><b>extract([time_token from expr])</b>,
083 * <ul>
084 *     <li>type: {@link EFunctionType#extract_t}</li>
085 *     <li>expr: {@link #getExpr1()}</li>
086 *     <li>time_token: {@link #getExtract_time_token()}</li>
087 * </ul>
088*
089* <p>sql server contains function,<b>contains(in_expr, expr [,langTerm])</b>,
090 * <ul>
091 *     <li>type: {@link EFunctionType#contains_t}</li>
092 *     <li>expr: {@link #getExpr1()}</li>
093 *     <li>in_expr: {@link #getInExpr()}</li>
094 * </ul>
095*
096*
097* <p>sql server freetext,freetext(contain in expr, expr [,langTerm])</p>,
098 * <ul>
099 *     <li>type: {@link EFunctionType#freetext_t}</li>
100 *     <li>expr: {@link #getExpr1()}</li>
101 *     <li>in_expr: {@link #getInExpr()}</li>
102 * </ul>
103*
104 *
105* <p>Oracle Extract(XML): <b>extract(XMLType_instance, XPath_string[,namespace_string])</b>,
106 * <ul>
107 *     <li>type: {@link EFunctionType#extractxml_t}</li>
108 *     <li>XMLType_instance: {@link #getXMLType_Instance()}</li>
109 *     <li>XPath_string: {@link #getXPath_String()}</li>
110 *     <li>namespace_string: {@link #getNamespace_String()}</li>
111 * </ul>
112
113* <p> <b>Rank(value,...)</b>,
114 * <ul>
115 *     <li>type: {@link EFunctionType#rank_t}</li>
116 *     <li>value list {@link #getOrderByList()}</li>
117 * </ul>
118 *
119 * <p>XMLPassingClause of XMLExists function</p>
120 * <ul>
121 *     <li>type: {@link EFunctionType#xmlexists_t}</li>
122 *     <li>value list {@link #getPassingClause()}</li>
123 * </ul>
124 *
125*/
126public class TFunctionCall extends TParseTreeNode{
127
128    protected TExpression filterClause;
129
130    /**
131     * spark sql filter clause, postgresql filter clause
132     * 
133     * <pre>
134     * SELECT COUNT(*) FILTER (WHERE bid_amount_value < 10000) AS low_range_bid_count_value FROM A
135     * </pre>
136     * 
137     * @return
138     */
139    public TExpression getFilterClause() {
140        return filterClause;
141    }
142
143    public void setFilterClause(TWhereClause filterClause) {
144        if (filterClause == null) return;
145        this.filterClause = filterClause.getCondition();
146    }
147
148    public void setFilterClause(TDummy filterClause) {
149        if (filterClause == null) return;
150        this.setFilterClause((TWhereClause)filterClause.node1);
151    }      
152 
153
154    private  TIndirection indirection;
155
156    public void setIndirection(TIndirection indirection) {
157        this.indirection = indirection;
158    }
159
160    /**
161     * The expressions inside the subscripts after this call, {@code split(s, ':')[n]}: column references that
162     * TSQLResolver2 and the lineage analyzer read like any other (Mantis 4817). Empty without a subscript.
163     *
164     * @return the subscript expressions, never null
165     */
166    public java.util.List<TExpression> getSubscriptExpressions() {
167        if (indirection == null) return java.util.Collections.<TExpression>emptyList();
168        return indirection.getSubscriptExpressions();
169    }
170
171    /**
172     * databricks,  current_version().dbsql_version;
173     * @return
174     */
175    public TIndirection getIndirection() {
176        return indirection;
177    }
178
179
180        final static Map<EDbVendor, Map<String, Set<String>>> tableFunctionMap = new HashMap<EDbVendor, Map<String, Set<String>>>();
181        static {
182                tableFunctionMap.put(EDbVendor.dbvmssql, loadTableFunctions(EDbVendor.dbvmssql));
183        tableFunctionMap.put(EDbVendor.dbvoracle, loadTableFunctions(EDbVendor.dbvoracle));
184        }
185        
186        private static Map<String, Set<String>> loadTableFunctions(EDbVendor vendor) {
187                Map<String, Set<String>> tableFunctionInfoMap = new HashMap<String, Set<String>>();
188                try {
189                        BufferedReader reader = new BufferedReader(new InputStreamReader(TFunctionCall.class.getResourceAsStream(
190                                        "/gudusoft/gsqlparser/tablefunction/" + vendor.name().replace("dbv", "") + ".txt")));
191                        String line;
192                        while ((line = reader.readLine()) != null) {
193                                String[] tableFunctionInfo = line.toUpperCase(Locale.ROOT).split("\\s*,\\s*");
194                                if (tableFunctionInfo.length > 1) {
195                                        tableFunctionInfoMap.put(tableFunctionInfo[0],
196                                                        CollectionUtil.convertArraysToSet(tableFunctionInfo));
197                                        tableFunctionInfoMap.get(tableFunctionInfo[0]).remove(tableFunctionInfo[0]);
198                                }
199                        }
200                } catch (IOException e) {
201                        e.printStackTrace();
202                }
203                return tableFunctionInfoMap;
204        }
205
206        private ArrayList<TDataConversionItem> dataConversionItems;
207
208    public void setDataConversionItems(ArrayList<TDataConversionItem> dataConversionItems) {
209        this.dataConversionItems = dataConversionItems;
210    }
211
212    public ArrayList<TDataConversionItem> getDataConversionItems() {
213        return dataConversionItems;
214    }
215
216    private TListaggOverflow listaggOverflow;
217
218    public void setListaggOverflow(TListaggOverflow listaggOverflow) {
219        this.listaggOverflow = listaggOverflow;
220    }
221
222    public TListaggOverflow getListaggOverflow() {
223        return listaggOverflow;
224    }
225
226    public void setFirstArgAsDateTimePart(int pos){
227            if (this.getArgs() == null) return;
228            if (this.getArgs().size()<=pos) return;
229            TExpression expression = this.getArgs().getExpression(pos);
230            if (expression.getObjectOperand() != null){
231                expression.getObjectOperand().setDbObjectType(EDbObjectType.date_time_part);
232        }
233    }
234        public int isColumnInThisTableFunction(TSQLEnv sqlEnv, EDbVendor dbVendor, TObjectName pColumn) {
235                int ret = TBaseType.COLUMN_IN_TABEL_FUNCTION_NOTSURE;
236                String functionNameKey = this.getFunctionName().toString().toUpperCase(Locale.ROOT);
237                functionNameKey = functionNameKey.substring(functionNameKey.lastIndexOf('.') + 1);
238                Map<String, Set<String>> tableFunctionInfoMap = tableFunctionMap.get(dbVendor);
239                if (tableFunctionInfoMap != null && tableFunctionInfoMap.containsKey(functionNameKey)) {
240                        if (tableFunctionInfoMap.get(functionNameKey).contains(SQLUtil.getIdentifierNormalName(dbVendor, pColumn.getColumnNameOnly(), ESQLDataObjectType.dotColumn).toUpperCase(Locale.ROOT))) {
241                                ret = TBaseType.COLUMN_IN_TABEL_FUNCTION_YES;
242                        } else {
243                                ret = TBaseType.COLUMN_IN_TABEL_FUNCTION_NO;
244                        }
245                }
246                else if(this.functionType == EFunctionType.udf_t && sqlEnv!=null){
247                        TSQLFunction functionDef = sqlEnv.searchFunction(this.getFunctionName().toString());
248                        if (functionDef == null) {
249                                // A function the catalog does not know has columns nobody knows either (Mantis 4797).
250                                ret = TBaseType.COLUMN_IN_TABEL_FUNCTION_NOTSURE;
251                        }
252                        else if(functionDef.searchColumnInReturnTable(pColumn.getColumnNameOnly())){
253                                ret = TBaseType.COLUMN_IN_TABEL_FUNCTION_YES;
254                        }
255                        else {
256                                ret = TBaseType.COLUMN_IN_TABEL_FUNCTION_NO;
257                        }
258                }
259                else {
260                        ret = TBaseType.COLUMN_IN_TABEL_FUNCTION_NOTSURE;
261                }
262
263                return ret;
264        }
265
266         protected TWithinGroup withinGroup;
267
268    public void setWithinGroup(TWithinGroup withinGroup) {
269        this.withinGroup = withinGroup;
270    }
271
272    public TWithinGroup getWithinGroup() {
273        return withinGroup;
274    }
275
276    public void setIntervalUnit(String intervalUnit) {
277        this.intervalUnit = intervalUnit;
278    }
279
280    public String getIntervalUnit() {
281        return intervalUnit;
282
283    }
284
285    private String intervalUnit;
286
287    private  TExpression stringExpr;
288
289    public void setStringExpr(TExpression stringExpr) {
290        this.stringExpr = stringExpr;
291    }
292
293    /**
294     * DB2 listagg function
295     * @return the first argument of the listagg function
296     */
297    public TExpression getStringExpr() {
298        return stringExpr;
299    }
300
301    private TExpression separatorExpr;
302
303    public void setSeparatorExpr(TExpression separatorExpr) {
304        this.separatorExpr = separatorExpr;
305    }
306
307    /**
308     * DB2 listagg function.
309     * @return the second argument of the listagg function.
310     */
311    public TExpression getSeparatorExpr() {
312
313        return separatorExpr;
314    }
315
316    private TExpression searchCondition;
317
318    /**
319     * contains search condition. or freetext_string in freetext predicate
320     *
321     * @param searchCondition contains search condition.
322     */
323    public void setSearchCondition(TExpression searchCondition) {
324        this.searchCondition = searchCondition;
325    }
326
327    /**
328     * contains search condition. It usually a string constant or a variable.
329     * SQL Server contains predicate.
330     * <pre>
331     * CONTAINS (
332     * {
333     * column_name | ( column_list )
334     * | *
335     * | PROPERTY ( { column_name }, 'property_name' )
336     * }
337     * , '&lt;contains_search_condition&gt;'
338     * [ , LANGUAGE language_term ]
339     * )
340     *</pre>
341     *
342     * Or freetext_string in freetext predicate
343     * <pre>
344     *     FREETEXT ( { column_name | (column_list) | * }
345     *     , 'freetext_string' [ , LANGUAGE language_term ] )
346     * </pre>
347     *
348     * @return contains search condition
349     */
350    public TExpression getSearchCondition() {
351
352        return searchCondition;
353    }
354
355    private TExpression columnNameOrListExpression;
356
357    /**
358     * set column name or column list of SQL Server contains predicate or freetext predicate
359     *
360     * @param columnNameOrListExpression column name or column list
361     * @see #getColumnNameOrListExpression
362     */
363    public void setColumnNameOrListExpression(TExpression columnNameOrListExpression) {
364        this.columnNameOrListExpression = columnNameOrListExpression;
365    }
366
367    /**
368     * get column name or column list in SQL Server contains predicate.
369     * <pre>
370     * CONTAINS (
371     * {
372     * column_name | ( column_list )
373     * | *
374     * | PROPERTY ( { column_name }, 'property_name' )
375     * }
376     * , '&lt;contains_search_condition&gt;'
377     * [ , LANGUAGE language_term ]
378     * )
379     *</pre>
380     * Or column name or column list in freetext predicate
381     * <pre>
382     *     FREETEXT ( { column_name | (column_list) | * }
383     *     , 'freetext_string' [ , LANGUAGE language_term ] )
384     * </pre>
385     *
386     * If this expression represents a column name, then the type of this expression is {@link gudusoft.gsqlparser.EExpressionType#simple_object_name_t},
387     * if this expression represents a column list, then the type of this expression is {@link gudusoft.gsqlparser.EExpressionType#list_t},
388     *
389     *
390     * @return column name or column list
391     */
392    public TExpression getColumnNameOrListExpression() {
393
394        return columnNameOrListExpression;
395    }
396
397    private TExpression startExpression;
398    private TExpression lengthExpression;
399
400    /**
401     * set a start expression that specifies the position within {@link #getSourceExpression} that is to be the first
402     * string unit of the result in substring function.
403     * <pre>
404     *     substring(sourceExpression from startExpression [for lengthExpression])
405     *     substring(sourceExpression , startExpression [, lengthExpression])
406     * </pre>
407     *
408     * @param startExpression an expression that specifies the position within {@link #getSourceExpression}
409     * that is to be the first string unit of the result in substring function
410     * @see #getStartExpression
411     */
412    public void setStartExpression(TExpression startExpression) {
413        this.startExpression = startExpression;
414    }
415
416    /**
417     * set the length expression in substring function.
418     * <pre>
419     *     substring(sourceExpression from startExpression [for lengthExpression])
420     *     substring(sourceExpression , startExpression [, lengthExpression])
421     * </pre>
422     *
423     * @param lengthExpression the length expression in substring function.
424     * @see #getLengthExpression
425     */
426    public void setLengthExpression(TExpression lengthExpression) {
427        this.lengthExpression = lengthExpression;
428    }
429
430    /**
431     * A start expression that specifies the position within {@link #getSourceExpression} that is to be the first
432     * string unit of the result in substring function.
433     * <p>substring</p>
434     * <pre>
435     *     substring(sourceExpression from startExpression [for lengthExpression])
436     *     substring(sourceExpression , startExpression [, lengthExpression])
437     * </pre>
438     *
439     * @return an expression that specifies the position within {@link #getSourceExpression} that is to be the first
440     * string unit of the result.
441     */
442    public TExpression getStartExpression() {
443
444        return startExpression;
445    }
446
447    /**
448     * The length of the {@link #getSourceExpression} string unit to be returned in substring function.
449     * <p>substring</p>
450     * <pre>
451     *     substring(sourceExpression from startExpression [for lengthExpression])
452     *     substring(sourceExpression , startExpression [, lengthExpression])
453     * </pre>
454     *
455     * @return the length of the string unit to be returned.
456     */
457    public TExpression getLengthExpression() {
458        return lengthExpression;
459    }
460
461    private TExpression sourceExpression;
462
463    /**
464     * set an expression that specifies the string from which the result is derived in substring function.
465     * <pre>
466     *     substring(sourceExpression from startExpression [for lengthExpression])
467     *     substring(sourceExpression , startExpression [, lengthExpression])
468     * </pre>
469     *
470     * @param sourceExpression An expression that specifies the string from which the result is derived
471     * @see #getSourceExpression
472     */
473    public void setSourceExpression(TExpression sourceExpression) {
474        this.sourceExpression = sourceExpression;
475    }
476
477    /**
478     * An expression that specifies the string from which the result is derived in substring function.
479     * <p>substring: DB2</p>
480     * <pre>
481     *     substring(sourceExpression from startExpression [for lengthExpression])
482     *     substring(sourceExpression , startExpression [, lengthExpression])
483     * </pre>
484     *
485     * @return An expression that specifies the string from which the result is derived
486     */
487    public TExpression getSourceExpression() {
488
489        return sourceExpression;
490    }
491
492    private TExpression typeExpression;
493
494    /**
495     * a structured type expression in DB2 subtype-treatment: treat.
496     *
497     * @param typeExpression a structured type expression
498     * @see #getTypeExpression
499     */
500    public void setTypeExpression(TExpression typeExpression) {
501        this.typeExpression = typeExpression;
502    }
503
504    /**
505     * A structured type expression in subtype-treatment.
506     * <p>treat function: DB2, Greenplum</p>
507     *
508     * @return a structured type expression
509     */
510    public TExpression getTypeExpression() {
511
512        return typeExpression;
513    }
514
515    private TExpression castOperand;
516
517    /**
518     * cast specification, set the first operand in the cast specification
519     *
520     * @param castOperand set the first operand in the cast specification
521     * @see #getCastOperand
522     */
523    public void setCastOperand(TExpression castOperand) {
524        this.castOperand = castOperand;
525    }
526
527    /**
528     * The first operand in the cast specification.
529     * <p>cast specification: DB2, Greenplum,SQL Server; try_cast function of SQL Server</p>
530     *
531     * @return The first operand in the cast specification
532     */
533    public TExpression getCastOperand() {
534
535        return castOperand;
536    }
537
538    private TExpression dateExpression;
539
540    /**
541     * set date, time or timestamp expression in extract/extend function.
542     *
543     * @param dateExpression date, time or timestamp expression
544     * @see #getDateExpression
545     */
546    public void setDateExpression(TExpression dateExpression) {
547        this.dateExpression = dateExpression;
548    }
549
550    /**
551     * date, time or timestamp expression in extract function of DB2, Greenplum.
552     * <p>
553     *     datetime or date value expression in extend function of informix.
554     * </p>
555     *
556     * @return date, time or timestamp expression
557     */
558    public TExpression getDateExpression() {
559        return dateExpression;
560    }
561
562    private TExpressionCallTarget callTarget;
563
564    /**
565     * set expressionCallTarget
566     *
567     * @param callTarget expression call target
568     * @see #getCallTarget
569     */
570    public void setCallTarget(TExpressionCallTarget callTarget) {
571        this.callTarget = callTarget;
572    }
573
574    /**
575     * get an xml type column, parameter, variable or subquery.
576     * <pre>
577     *     DECLARE @myDoc xml
578     *      DECLARE @ProdID int
579     *      SET @myDoc = '&lt;Root&gt;
580     *     &lt;ProductDescription ProductID="1" ProductName="Road Bike"&gt;
581     *     &lt;Features&gt;
582     *     &lt;Warranty&gt;1 year parts and labor &lt;/Warranty&gt;
583     *     &lt;Maintenance&gt;3 year parts and labor extended maintenance is available &lt;/Maintenance&gt;
584     *     &lt;/Features&gt;
585     *     &lt;/ProductDescription&gt;
586     *     &lt;/Root&gt;'
587     *     SET @ProdID =  @myDoc.value('(/Root/ProductDescription/@ProductID)[1]', 'int' )
588     *     SELECT @ProdID
589     * </pre>
590     *
591     * the value returned by this method represents <code>@myDoc</code> before the value() function.
592     *
593     * SQL Server value() Method performs an XQuery against the XML and returns a value of SQL type.
594     * <p></p>
595     * You typically use this method to extract a value from an XML instance stored in an xml type
596     * column, parameter, or variable.
597     *
598     * @return expressionCallTarget that represents an xml type column, parameter, variable or subquery
599     */
600    public TExpressionCallTarget getCallTarget() {
601
602        return callTarget;
603    }
604
605    private TGroupConcatParam groupConcatParam;
606
607    public void setGroupConcatParam(TGroupConcatParam groupConcatParam) {
608        this.groupConcatParam = groupConcatParam;
609    }
610
611    public TGroupConcatParam getGroupConcatParam() {
612
613        return groupConcatParam;
614    }
615
616    public static boolean isBuiltIn(String pName, EDbVendor pDBVendor){
617        List<String> avVersions = functionChecker.getAvailableDbVersions(pDBVendor);
618        if (avVersions == null || avVersions.isEmpty()) {
619            return false;
620        }
621        return functionChecker.isBuiltInFunction(pName,pDBVendor,avVersions.get(avVersions.size()-1));
622    }
623
624    private boolean isCheckedBuiltIn = false;
625    private boolean isBuiltIn = false;
626
627    public boolean isBuiltIn(EDbVendor pDBVendor) {
628        if (isCheckedBuiltIn) return isBuiltIn;
629        List<String> avVersions = functionChecker.getAvailableDbVersions(pDBVendor);
630        if (avVersions == null || avVersions.isEmpty()) {
631            isBuiltIn = false;
632        } else {
633            isBuiltIn = functionChecker.isBuiltInFunction(this.functionName.toString(),pDBVendor,avVersions.get(avVersions.size()-1));
634        }
635        isCheckedBuiltIn = true;
636        return isBuiltIn;
637    }
638
639    private TXMLPassingClause passingClause;
640
641    public void setPassingClause(TXMLPassingClause passingClause) {
642        this.passingClause = passingClause;
643    }
644
645    /**
646     *  XMLPassingClause of XMLExists function
647     * @return XMLPassingClause of XMLExists function
648     */
649    public TXMLPassingClause getPassingClause() {
650        return passingClause;
651    }
652
653    private EAggregateType aggregateType = EAggregateType.none;
654
655    /**
656     * set <code>ALL | DISTINCT | UNIQUE</code> keywords used in an aggregate function.
657     *
658     * @param aggregateType
659     * @see #getAggregateType
660     */
661    public void setAggregateType(EAggregateType aggregateType) {
662        this.aggregateType = aggregateType;
663    }
664
665    /**
666     * get <code>ALL | DISTINCT | UNIQUE</code> keywords used in an aggregate function.
667     * An aggregate function performs a calculation on a set of values, and returns a single value.
668     *
669     * @return aggregate type, one of those values:  none,all,distinct,unique
670     */
671    public EAggregateType getAggregateType() {
672
673        return aggregateType;
674    }
675
676    private  TOrderByItemList orderByList;
677
678    public void setOrderByList(TOrderByItemList orderByList) {
679        this.orderByList = orderByList;
680    }
681
682    /**
683     * arguments in rank(value,...) function.
684     * @return a list of {@link TOrderByItem} in function: rank, csum
685     */
686    public TOrderByItemList getOrderByList() {
687
688        return orderByList;
689    }
690
691    private  TOrderBy sortClause;
692
693    public void setSortClause(TOrderBy sortClause) {
694        this.sortClause = sortClause;
695    }
696
697    /**
698     * ORDER BY inside the argument list of an aggregate, such as
699     * {@code LIST(x, ',' ORDER BY y)} (SQL Anywhere, Sybase IQ) or {@code string_agg(x, ',' ORDER BY y)}.
700     * Its keys are resolved like the arguments of the call, not with the select-list alias rule of a
701     * statement ORDER BY.
702     * @return the ORDER BY clause, or null when the function has none
703     */
704    public TOrderBy getSortClause() {
705
706        return sortClause;
707    }
708
709    /**
710     * Over clause of analytic function
711     * @return over clause
712     */
713    public TWindowDef getWindowDef() {
714        return windowDef;
715    }
716
717    public void setWindowDef(TWindowDef windowDef) {
718
719        this.windowDef = windowDef;
720    }
721
722    public void setFunctionOptionsWithDummy(TDummy dummy){
723        if (dummy == null) return;
724        if (dummy.node2 != null){
725            setWindowDef((TWindowDef)dummy.node2 );
726        }
727        if (dummy.node3 != null){
728            setWithinGroup((TWithinGroup) dummy.node3);
729        }
730    }
731
732     protected TWindowDef windowDef;
733
734//    public void setWindowSpecification(TWindowDef windowSpecification) {
735//        this.windowSpecification = windowSpecification;
736//    }
737
738    /**
739     * @deprecated As of v1.8.6.0, replaced by {@link #windowDef}
740     */
741    private TWindowSpecification windowSpecification;
742
743    private TDatatypeAttribute datatypeAttribute = null;
744
745    public TWindowSpecification getWindowSpecification() {
746        return windowSpecification;
747    }
748
749    /**
750     * Not used.
751     *
752     * @param datatypeAttribute datatype attribute
753     */
754    public void setDatatypeAttribute(TDatatypeAttribute datatypeAttribute) {
755        this.datatypeAttribute = datatypeAttribute;
756    }
757
758    /**
759     * Not used.
760     *
761     * @return datatype attribute
762     */
763    public TDatatypeAttribute getDatatypeAttribute() {
764
765        return datatypeAttribute;
766    }
767
768    /**
769     * one of the YEAR/MONTH/DAY, HOUR/MINUTE/SECOND keywords in extract function
770     *
771     * @return one of the YEAR/MONTH/DAY, HOUR/MINUTE/SECOND keywords in extract function
772     */
773    public TSourceToken getExtract_time_token() {
774        return extract_time_token;
775    }
776
777
778    /**
779     * set one of the YEAR/MONTH/DAY, HOUR/MINUTE/SECOND keyword in extract function
780     *
781     * @param extract_time_token one of the YEAR/MONTH/DAY, HOUR/MINUTE/SECOND token
782     */
783    public void setExtract_time_token(TSourceToken extract_time_token) {
784        this.extract_time_token = extract_time_token;
785    }
786
787
788    private TDummy dummy = null;
789
790    /**
791     * Set value for temporary use only
792     *
793     * @param dummy a temporary value from parse tree
794     */
795    public void setDummy(TDummy dummy) {
796        this.dummy = dummy;
797    }
798
799    /**
800     * Not used
801     *
802     * @return a temporary value
803     */
804    public TDummy getDummy() {
805
806        return dummy;
807    }
808
809    public void setInExpr(TInExpr inExpr) {
810        this.inExpr = inExpr;
811    }
812
813    public TInExpr getInExpr() {
814
815        return inExpr;
816    }
817
818    private TInExpr inExpr = null; //sql server, used in fntContains, fntFreetext
819
820    public void setExtractXMLArg(TExpressionList exprList){
821       if (exprList.size() > 1){
822           this.XMLType_Instance = exprList.getExpression(0);
823           this.XPath_String = exprList.getExpression(1);
824       }
825
826       if (exprList.size() > 2){
827           this.Namespace_String = exprList.getExpression(2);
828       }
829
830       functionType = EFunctionType.extractxml_t;
831
832    }
833    private TExpression XMLType_Instance = null;
834
835    public TExpression getNamespace_String() {
836        return Namespace_String;
837    }
838
839    public TExpression getXPath_String() {
840
841        return XPath_String;
842    }
843
844    public TExpression getXMLType_Instance() {
845
846        return XMLType_Instance;
847    }
848
849    private TExpression XPath_String = null;
850    private TExpression Namespace_String = null;
851
852
853    private TObjectNameList matchColumns = null;
854
855    /**
856     * column list in match function of MySQL
857     * @return
858     */
859    public TObjectNameList getMatchColumns() {
860        return matchColumns;
861    }
862
863    /**
864     * against expr in match function of MySQL
865     * <pre>
866     *     MATCH (col1,col2,...) AGAINST (expr [search_modifier])
867     * </pre>
868     *
869     * @return expression after against keyword
870     */
871    public TExpression getAgainstExpr() {
872
873        return againstExpr;
874    }
875
876    private TExpression againstExpr = null;
877
878    public void setMatchColumns(TObjectNameList matchColumns) {
879        this.matchColumns = matchColumns;
880    }
881
882    /**
883     * against expr in match function of MySQL
884     * <pre>
885     *     MATCH (col1,col2,...) AGAINST (expr [search_modifier])
886     * </pre>
887     * @param againstExpr expression after against keyword
888     */
889    public void setAgainstExpr(TExpression againstExpr) {
890
891        this.againstExpr = againstExpr;
892    }
893
894    private TTypeName typename = null;
895
896    public void setTypename(TTypeName typename) {
897        this.typename = typename;
898    }
899
900    public TTypeName getTypename() {
901
902        return typename;
903    }
904
905    private TExpression parameter;
906
907    public void setParameter(TExpression parameter) {
908        this.parameter = parameter;
909    }
910
911    public void setStyle(TExpression style) {
912        this.style = style;
913    }
914
915    /**
916     * parameter in function:convert(datetype,parameter,style)
917     * @return
918     */
919    public TExpression getParameter() {
920
921        return parameter;
922    }
923
924    /**
925     * style in function:convert(datetype,parameter,style)
926     * @return
927     */
928    public TExpression getStyle() {
929        return style;
930    }
931
932    private TExpression style;
933
934    private TSourceToken extract_time_token = null;
935
936     TExpression expr1 = null; //teradata position function, sql server: convert; oracle convert,translate  , mysql substring
937     TExpression expr2 = null; //teradata: position,substring function,, sql server: convert; oracle convert, mysql substring
938     TExpression expr3 = null; //teradata: substring function, for n, mysql substring
939
940     protected EFunctionType functionType = EFunctionType.udf_t;
941
942    /**
943     * The function type as the parser set it. Unlike {@link #getFunctionType()}, it never looks an unknown type up:
944     * that lookup renders the function name and caches a built-in check on this node, so a caller that must not
945     * change the tree (the SQL-LR producer's classifier) reads this instead. The lookup only ever answers
946     * {@code contains_t}, {@code builtin_t} or {@code unknown_t}, so for every other type the two agree.
947     *
948     * @return the parsed function type
949     * @since 4.2.17
950     */
951    public EFunctionType getParsedFunctionType() {
952        return functionType;
953    }
954
955    public EFunctionType getFunctionType() {
956        if (functionType != EFunctionType.unknown_t){
957            return functionType;
958        }else if (getFunctionName().toString().toLowerCase(Locale.ROOT).equalsIgnoreCase("contains")){
959            return EFunctionType.contains_t;
960        }else if (isBuiltIn(this.dbvendor)){
961            return  EFunctionType.builtin_t;
962        }else{
963            return functionType;
964        }
965    }
966
967    public boolean hasParenthesis() {
968        // 只有一个token,肯定不包含括号
969        if ((this.getStartToken() != null) && (this.getEndToken() != null) && (this.getStartToken() == this.getEndToken())) return false;
970        if (isOracleBuiltinFunction(this.dbvendor, this.getFunctionName().toString())) {
971            return false;
972        }
973
974        //
975        // 补充其他没有括号的情况
976        //
977
978        return true;
979    }
980
981    private boolean isOracleBuiltinFunction(EDbVendor dbvendor, String functionName) {
982        if (dbvendor != EDbVendor.dbvoracle) return false;
983
984        return functionName.equalsIgnoreCase("sysdate")
985                || functionName.equalsIgnoreCase("user")
986                || functionName.equalsIgnoreCase("SYSTIMESTAMP")
987                || functionName.equalsIgnoreCase("CURRENT_DATE")
988                || functionName.equalsIgnoreCase("CURRENT_TIMESTAMP")
989                ;
990
991    }
992
993    TColumnDefinitionList fieldDefs;
994    TResultColumnList fieldValues;
995
996    public TColumnDefinitionList getFieldDefs() {
997        return fieldDefs;
998    }
999
1000    public TResultColumnList getFieldValues() {
1001        return fieldValues;
1002    }
1003
1004
1005    public TObjectName getFunctionName() {
1006        return functionName;
1007    }
1008
1009    protected TObjectName functionName;
1010
1011    public TExpression Trim_Expr = null;
1012    public TExpression Trim_From_Expr = null;
1013
1014
1015    public void setTrimArgument(TTrimArgument trimArgument) {
1016        this.trimArgument = trimArgument;
1017    }
1018
1019    public TTrimArgument getTrimArgument() {
1020
1021        return trimArgument;
1022    }
1023
1024    TTrimArgument trimArgument = null;
1025
1026
1027    public void setExpr1(TExpression expr1) {
1028        this.expr1 = expr1;
1029    }
1030
1031    public void setExpr2(TExpression expr2) {
1032        this.expr2 = expr2;
1033    }
1034
1035    private TTypeName asDatatype;
1036
1037    /**
1038     *  datatype defined in Oracle function: <code> XMLSERIALIZE(value_expr as datatype)</code>
1039     *
1040     * @param asDatatype datatype defined in Oracle function: <code> XMLSERIALIZE(value_expr as datatype)</code>
1041     */
1042    public void setAsDatatype(TTypeName asDatatype) {
1043        this.asDatatype = asDatatype;
1044    }
1045
1046    private TResultColumnList XMLForestValueList;
1047
1048    public void setXMLForestValueList(TResultColumnList XMLForestValueList) {
1049        this.XMLForestValueList = XMLForestValueList;
1050    }
1051
1052    private TExpression XMLElementNameExpr;
1053
1054    public void setXMLElementNameExpr(TExpression XMLElementNameExpr) {
1055        this.XMLElementNameExpr = XMLElementNameExpr;
1056    }
1057
1058    /**
1059     * XMLElement name/evalname expr
1060     * @return XMLElement name/evalname expr
1061     */
1062    public TExpression getXMLElementNameExpr() {
1063        return XMLElementNameExpr;
1064
1065    }
1066
1067    private TXMLAttributesClause XMLAttributesClause;
1068
1069    public void setXMLAttributesClause(TXMLAttributesClause XMLAttributesClause) {
1070        this.XMLAttributesClause = XMLAttributesClause;
1071    }
1072
1073    public void setXmlPassingClause(TXMLPassingClause xmlPassingClause) {
1074        this.xmlPassingClause = xmlPassingClause;
1075    }
1076
1077    public TXMLPassingClause getXmlPassingClause() {
1078        return xmlPassingClause;
1079    }
1080
1081    TXMLPassingClause xmlPassingClause;
1082
1083
1084    private TResultColumnList XMLElementValueExprList;
1085
1086    public void setXMLElementValueExprList(TResultColumnList XMLElementValueExprList) {
1087        this.XMLElementValueExprList = XMLElementValueExprList;
1088    }
1089
1090    /**
1091     * XMLElement ( value_expr [,XML_attribute_clause] [,value expr list] )
1092     * @return XMLElement value expr list
1093     */
1094    public TResultColumnList getXMLElementValueExprList() {
1095
1096        return XMLElementValueExprList;
1097    }
1098
1099    /**
1100     * XMLAttribute clause in xmlelement function
1101     * @return XMLAttribute clause in xmlelement function
1102     */
1103    public TXMLAttributesClause getXMLAttributesClause() {
1104
1105        return XMLAttributesClause;
1106    }
1107
1108    /**
1109     * XMLFOREST (value_expr [as aliasName], ... )
1110     * @return XMLFOREST value list
1111     */
1112    public TResultColumnList getXMLForestValueList() {
1113
1114        return XMLForestValueList;
1115    }
1116
1117    /**
1118     * get datatype defined in Oracle XMLSERIALIZE(value_expr as datatype)
1119     *
1120     * @return datatype defined in oracle XMLSERIALIZE function
1121     */
1122    public TTypeName getAsDatatype() {
1123
1124        return asDatatype;
1125    }
1126
1127    /**
1128     * paramter of following functions
1129     * <br>teradata: position function,
1130     * <br>sql server: convert;
1131     * <br>oracle: convert,translate,cast,oracle XMLSERIALIZE(value_expr), oracle XMLROOT (value_expr)
1132     * <br>mysql: substring
1133     * @return
1134     */
1135    public TExpression getExpr1() {
1136
1137        return expr1;
1138    }
1139
1140    public TExpression getExpr2() {
1141        return expr2;
1142    }
1143
1144    public void setExpr3(TExpression expr3) {
1145        this.expr3 = expr3;
1146    }
1147
1148    public TExpression getExpr3() {
1149
1150        return expr3;
1151    }
1152
1153
1154    public void setExprList(TExpressionList exprList) {
1155        this.exprList = exprList;
1156    }
1157
1158    /**
1159     * return TExpressionList instead of TGroupingExpressionItemList after v1.4.3.3
1160     * @return    expr list
1161     */
1162    public TExpressionList getExprList() {
1163
1164        return exprList;
1165    }
1166
1167    private TExpressionList exprList = null;//teradata case_n, range_n
1168
1169
1170    protected TExpressionList Args = null;
1171
1172    public void setArgs(TExpressionList args) {
1173        Args = args;
1174    }
1175
1176
1177    public void init(Object arg1){
1178        functionName = (TObjectName)arg1;
1179        if (functionName.getDbObjectType() == EDbObjectType.unknown){
1180            functionName.setObjectType(TObjectName.ttobjFunctionName);
1181        }
1182    }
1183
1184    public void init(Object arg1,Object arg2){
1185        if (arg1 instanceof EFunctionType){
1186            functionType  = (EFunctionType)arg1;
1187            init(arg2);
1188        }
1189        else if (arg1 instanceof TObjectName){
1190            functionType  = (EFunctionType)arg2;
1191            init(arg1);
1192        }
1193        switch (functionType){
1194            case xmlmethod_t:
1195                if (functionName.getMethodToken().tokencode == TBaseType.rrw_xml_nodes){
1196                    functionType = EFunctionType.xmlnodes_t;
1197                }else if (functionName.getMethodToken().tokencode == TBaseType.rrw_xml_query){
1198                    functionType = EFunctionType.xml_sqlserver_query_t;
1199                }else if (functionName.getMethodToken().tokencode == TBaseType.rrw_xml_value){
1200                    functionType = EFunctionType.xmlvalue_t;
1201                }else if (functionName.getMethodToken().tokencode == TBaseType.rrw_xml_exist){
1202                    functionType = EFunctionType.xmlexists_t;
1203                }else if (functionName.getMethodToken().tokencode == TBaseType.rrw_xml_modify){
1204                    functionType = EFunctionType.xmlmodify_t;
1205                }
1206                break;
1207            case oracle_dbms_package_t:
1208                this.isBuiltIn = true;
1209                break;
1210            default:
1211                break;
1212        }
1213    }
1214
1215    public void init(Object arg1,Object arg2,Object arg3){
1216        init(arg1,arg2);
1217        // For struct_t functions, store the field values (TResultColumnList)
1218        if (arg3 instanceof TResultColumnList) {
1219            if (this.functionType == EFunctionType.struct_t) {
1220                this.fieldValues = (TResultColumnList) arg3;
1221            }
1222        }
1223    }
1224
1225    public void init(Object arg1,Object arg2,Object arg3,Object arg4){
1226        init(arg1,arg2,arg3);
1227        // For struct_t functions, store the field values (TResultColumnList)
1228        if (arg4 instanceof TResultColumnList) {
1229            if (this.functionType == EFunctionType.struct_t) {
1230                this.fieldValues = (TResultColumnList) arg4;
1231            }
1232        }
1233    }
1234
1235
1236    public void setAnalyticFunction(TAnalyticFunction analyticFunction) {
1237        this.analyticFunction = analyticFunction;
1238    }
1239
1240    /**
1241     * get the list of parameters defined in this function
1242     *
1243     * @return the list of parameter
1244     */
1245    public TExpressionList getArgs() {
1246        return Args;
1247    }
1248
1249     protected TAnalyticFunction analyticFunction;
1250
1251
1252    public void doParse(TCustomSqlStatement psql, ESqlClause plocation){
1253        this.dbvendor = psql.dbvendor;
1254        switch(functionType){
1255            case unknown_t:
1256            case chr_t:
1257            case udf_t:
1258            case builtin_t:
1259            case listagg_t:
1260            case year_t:
1261            case generate_date_array_t:
1262                if (callTarget != null){
1263                    callTarget.doParse(psql,plocation);
1264                }
1265                if (Args != null){
1266                    // Use TBuiltFunctionUtil to determine which argument positions contain keywords
1267                    // This supports datepart keywords like QUARTER, MONTH, YEAR, etc. across all databases.
1268                    // It is the only list: the old resolver and resolver2 read the same properties files, so a
1269                    // function a vendor does not have (Redshift has no DATENAME) is not marked here either.
1270                    Set<Integer> keywordPositions = null;
1271                    if (functionName != null) {
1272                        keywordPositions = TBuiltFunctionUtil.argumentsIncludeKeyword(
1273                            psql.dbvendor, functionName.toString());
1274                    }
1275                    markColumnOnlyArgument(psql);
1276
1277                    for(int i=0;i<Args.size();i++){
1278                        // Check if this argument position is marked as a keyword position (1-based in properties)
1279                        boolean isKeywordPosition = (keywordPositions != null && keywordPositions.contains(i + 1));
1280
1281                        if (isKeywordPosition) {
1282                            // Mark this argument as date_time_part so it's not treated as column reference
1283                            setFirstArgAsDateTimePart(i);
1284                            continue;
1285                        }
1286                        Args.getExpression(i).doParse(psql,plocation);
1287                    }
1288                }
1289
1290                if (psql.dbvendor == EDbVendor.dbvmssql){
1291                    // check OGC function of sql server
1292//                    System.out.println("func:"+functionName.toString());
1293                    if (isSQLServerOGCMethod(functionName.getObjectToken())){
1294                        if (functionName.getSchemaToken() != null){
1295                            functionName.getSchemaToken().setDbObjType(TObjectName.ttobjColumn);
1296                            if (functionName.getDatabaseToken() != null){
1297                                functionName.getDatabaseToken().setDbObjType(TObjectName.ttobjTable);
1298                            }
1299
1300                            TObjectName objectName = new TObjectName();
1301                            objectName.init(functionName.getDatabaseToken(),functionName.getSchemaToken());
1302                            objectName.setGsqlparser(psql.getGsqlparser()); // this will make toString work correctly
1303                            psql.linkColumnReferenceToTable(objectName,plocation);
1304                            psql.linkColumnToTable(objectName,plocation);
1305                        }
1306                    }else if (isSQLServerFunctionsONXMLColumn()) {
1307
1308
1309//                        if (functionName.getSchemaToken() != null) {
1310//                            functionName.getSchemaToken().setDbObjType(TObjectName.ttobjColumn);
1311//                            functionName.setPartToken(functionName.getSchemaToken());
1312//                            functionName.setSchemaToken(null);
1313//                            if (functionName.getDatabaseToken() != null) {
1314//                                functionName.getDatabaseToken().setDbObjType(TObjectName.ttobjTable);
1315//                                functionName.setObjectToken(functionName.getDatabaseToken());
1316//                            }
1317//
1318//                            TObjectName objectName = new TObjectName();
1319//                            objectName.init(functionName.getObjectToken(), functionName.getPartToken());
1320//                            objectName.setGsqlparser(psql.getGsqlparser()); // this will make toString work correctly
1321//                            psql.linkColumnReferenceToTable(objectName, plocation);
1322//                            psql.linkColumnToTable(objectName, plocation);
1323//                        }
1324
1325                    }
1326                } else if (psql.dbvendor == EDbVendor.dbvoracle){
1327                    if (plocation == ESqlClause.spAssignValue){
1328                        if (functionName.getNumberOfPart() == 3){
1329                            // schema1.pkg1.GETCUSTOMERNAME(2)
1330                            functionName.setPackageToken(functionName.getSchemaToken());
1331                            functionName.setSchemaToken(functionName.getDatabaseToken());
1332                            functionName.setServerToken(null);
1333                        }
1334                    }
1335                } else if (psql.dbvendor == EDbVendor.dbvteradata){
1336                    // Handle NPath function - extract ON clause tables for data lineage
1337                    if (functionName != null && functionName.toString().equalsIgnoreCase("NPath")){
1338                        analyzeTeradataNPathOnClause(psql, plocation);
1339                    }
1340                }
1341                break;
1342            case trim_t:
1343                if (this.trimArgument != null){
1344                this.trimArgument.doParse(psql,plocation);
1345                }
1346
1347                break;
1348            case cast_t:
1349                if (this.expr1 != null){
1350                    this.expr1.doParse(psql,plocation);
1351                }else{
1352                    this.getArgs().getExpression(0).doParse(psql,plocation);
1353                }
1354
1355                break;
1356            case convert_t:
1357                if (this.typename != null) this.typename.doParse(psql,plocation);
1358                this.parameter.doParse(psql,plocation);
1359                if (this.style != null)
1360                { //sql server
1361                    this.style.doParse(psql,plocation);
1362                }
1363                break;
1364            case extract_t:
1365                if (this.expr1 != null){
1366                    this.expr1.doParse(psql,plocation);
1367                }
1368                break;
1369            case treat_t:
1370                this.expr1.doParse(psql,plocation);
1371                break;
1372            case contains_t:
1373                //this.inExpr.doParse(psql,plocation);
1374
1375                this.expr1.doParse(psql,plocation);
1376                this.expr2.doParse(psql,plocation);
1377                break;
1378            case freetext_t:
1379                //inExpr.doParse(psql,plocation);
1380                this.expr1.doParse(psql,plocation);
1381                this.expr2.doParse(psql,plocation);
1382                break;
1383            case case_n_t:
1384                this.getArgs().doParse(psql, plocation);
1385                break;
1386//            case range_n_t:
1387//                this.expr1.doParse(psql,plocation);
1388//                for(TRangeNFunctionItem rangeItem : rangeNFunctionItems){
1389//                    rangeItem.doParse(psql,plocation);
1390//                }
1391//                break;
1392            case position_t:
1393                expr1.doParse(psql,plocation);
1394                expr2.doParse(psql,plocation);
1395                break;
1396            case substring_t:
1397                if (Args != null)
1398                    for(int i=0;i<Args.size();i++){
1399                        Args.getExpression(i).doParse(psql,plocation);
1400                    }
1401                if (expr1 != null) expr1.doParse(psql,plocation);
1402                if (expr2 != null) expr2.doParse(psql,plocation);
1403                if (expr3 != null){
1404                    expr3.doParse(psql,plocation);
1405                }
1406                break;
1407            case xmlquery_t:
1408                if (xmlPassingClause != null){
1409                    xmlPassingClause.doParse(psql,plocation);
1410                }
1411                break;
1412            case xmlcast_t:
1413                if (typeExpression != null){
1414                    typeExpression.doParse(psql,plocation);
1415                }
1416                break;
1417            case match_against_t:
1418                for(int i=0;i<this.getMatchColumns().size();i++){
1419                    psql.linkColumnReferenceToTable(this.getMatchColumns().getObjectName(i),plocation);
1420                    psql.linkColumnToTable(this.getMatchColumns().getObjectName(i),plocation);
1421                }
1422                this.getAgainstExpr().doParse(psql,plocation);
1423                break;
1424            case adddate_t:
1425            case date_add_t:
1426            case subdate_t:
1427            case date_sub_t:
1428            case timestampadd_t:
1429            case timestampdiff_t:
1430                expr1.doParse(psql,plocation);
1431                expr2.doParse(psql,plocation);
1432                break;
1433            case xmlserialize_t:
1434                if (expr1 != null){
1435                    expr1.doParse(psql,plocation);
1436                }else if (typeExpression != null){
1437                    typeExpression.doParse(psql,plocation);
1438                }
1439                break;
1440            case xmlroot_t:
1441                expr1.doParse(psql,plocation);
1442                break;
1443            case xmlforest_t:
1444                XMLForestValueList.doParse(psql, plocation);
1445                break;
1446            case xmlelement_t:
1447                if (XMLElementNameExpr.getExpressionType() == EExpressionType.simple_object_name_t){
1448                    XMLElementNameExpr.getObjectOperand().setDbObjectType(EDbObjectType.xmlElement);
1449                }
1450                XMLElementNameExpr.doParse(psql,ESqlClause.xmlElementName);
1451                if (XMLAttributesClause != null)
1452                  XMLAttributesClause.doParse(psql,plocation);
1453                if (XMLElementValueExprList != null)
1454                    XMLElementValueExprList.doParse(psql,plocation);
1455                break;
1456            case translate_t:
1457                expr1.doParse(psql,plocation);
1458                if (expr2 != null)  expr2.doParse(psql,plocation);
1459                if (expr3 != null)  expr3.doParse(psql,plocation);
1460                break;
1461            case group_concat_t:
1462                groupConcatParam.doParse(psql,plocation);
1463                break;
1464            case quantile_t:
1465                this.orderByList.doParse(psql,plocation);
1466                break;
1467            case csum_t:
1468                this.getOrderByList().doParse(psql,plocation);
1469                break;
1470            case rank_t:
1471                if (this.getOrderByList() != null){
1472                    this.getOrderByList().doParse(psql,plocation);
1473                }
1474
1475                break;
1476            case xmlexists_t:
1477            case xmlmodify_t:
1478            case xmlnodes_t:
1479            case xmlvalue_t:
1480            case xml_sqlserver_query_t:
1481                if (callTarget != null){
1482                    callTarget.doParse(psql,plocation);
1483                }
1484                if ((functionName.getObjectToken() != null)&&(functionName.getPartToken() != null)){
1485                    TObjectName objectName = new TObjectName();
1486                    objectName.init(functionName.getObjectToken(), functionName.getPartToken());
1487                    objectName.setGsqlparser(psql.getGsqlparser()); // this will make toString work correctly
1488                    psql.linkColumnReferenceToTable(objectName, plocation);
1489                    psql.linkColumnToTable(objectName, plocation);
1490                }
1491                break;
1492            case xmlagg_t:
1493                for(int i=0;i<Args.size();i++){
1494                    Args.getExpression(i).doParse(psql,plocation);
1495                }
1496                break;
1497            case if_t:
1498                expr1.doParse(psql,plocation);
1499                expr2.doParse(psql,plocation);
1500                expr3.doParse(psql,plocation);
1501                break;
1502            case array_t: // bigquery array (subquery)
1503                this.getArgs().getExpression(0).doParse(psql,plocation);
1504                break;
1505            case array_agg_t:
1506                this.getArgs().getExpression(0).doParse(psql,plocation);
1507                break;
1508            case string_agg_t:
1509                this.getArgs().getExpression(0).doParse(psql,plocation);
1510                if (getOrderByList() != null){
1511                    getOrderByList().doParse(psql,plocation);
1512                }
1513                break;
1514            default:
1515                if (getArgs() != null){
1516                    for(int i=0;i<getArgs().size();i++){
1517                        Args.getExpression(i).doParse(psql,plocation);
1518                    }
1519                }
1520                break;
1521        }
1522
1523        // ORDER BY inside the argument list, as in LIST(x, ',' ORDER BY y), string_agg(x, ',' ORDER BY y) or
1524        // XMLAGG(x ORDER BY y). Its keys are linked like the arguments, not through TOrderBy.doParse: that
1525        // one applies the rules of a statement ORDER BY, where an unqualified key may name a select-list alias.
1526        if (sortClause != null){
1527            TOrderByItemList sortItems = sortClause.getItems();
1528            for(int i=0;i<sortItems.size();i++){
1529                TExpression sortKey = sortItems.getOrderByItem(i).getSortKey();
1530                if (sortKey != null){
1531                    sortKey.doParse(psql,plocation);
1532                }
1533            }
1534        }
1535
1536        if (withinGroup != null){
1537            withinGroup.doParse(psql,plocation);
1538        }
1539        if (analyticFunction != null){
1540            analyticFunction.doParse(psql,plocation);
1541        }
1542
1543        if (filterClause != null){
1544            filterClause.doParse(psql,plocation);
1545        }
1546
1547        if (windowDef != null){
1548            windowDef.doParse(psql,plocation);
1549        }
1550    }
1551
1552
1553    /**
1554     * @deprecated As of v1.8.6.3, use {@link #getWindowDef()} instead
1555     * window clause in window function.
1556     * @return a value of {@link TAnalyticFunction}
1557     * @see TAnalyticFunction
1558     */
1559    public TAnalyticFunction getAnalyticFunction() {
1560        return analyticFunction;
1561    }
1562
1563    public void accept(TParseTreeVisitor v){
1564        v.preVisit(this);
1565        v.postVisit(this);
1566    }
1567
1568    public void acceptChildren(TParseTreeVisitor v){
1569        v.preVisit(this);
1570        // SQL Server method chain T.c.query('.').value('.', 'int') (MantisBT 4897): the call target is the
1571        // inner method call, and it is the only node that names the column, so walk it. The other call
1572        // targets (a column, a variable, a subquery) are not walked, as before.
1573        if ((callTarget != null) && (callTarget.getExpr() != null)
1574                && (callTarget.getExpr().getExpressionType() == EExpressionType.function_t)){
1575            callTarget.acceptChildren(v);
1576        }
1577        functionName.acceptChildren(v);
1578        switch(functionType){
1579            case unknown_t:
1580                if (Args != null){
1581                    Args.acceptChildren(v);
1582                }
1583                break;
1584            case udf_t:
1585                if (Args != null){
1586                    Args.acceptChildren(v);
1587                }
1588                if (analyticFunction != null){
1589                    analyticFunction.acceptChildren(v);
1590                }
1591                break;
1592            case trim_t:
1593                if (this.trimArgument != null){
1594                    this.trimArgument.acceptChildren(v);
1595                }
1596
1597                break;
1598            case cast_t:
1599                if (this.expr1 != null) {
1600                    this.expr1.acceptChildren(v);
1601                }else{
1602                    this.getArgs().getExpression(0).acceptChildren(v);
1603                }
1604
1605                break;
1606            case convert_t:
1607                if (this.typename != null) this.typename.acceptChildren(v);
1608                if (this.parameter != null) this.parameter.acceptChildren(v);
1609                if (this.style != null) this.style.acceptChildren(v);
1610//                this.expr1.acceptChildren(v);
1611//                if (this.expr2 != null)
1612//                { //sql server
1613//                    this.expr2.acceptChildren(v);
1614//                }
1615                break;
1616            case extract_t:
1617                if (this.expr1 != null){
1618                    this.expr1.acceptChildren(v);
1619                }
1620                break;
1621            case treat_t:
1622                this.expr1.acceptChildren(v);
1623                break;
1624            case contains_t:
1625                //this.inExpr.acceptChildren(v);
1626
1627                this.expr1.acceptChildren(v);
1628                this.expr2.acceptChildren(v);
1629                break;
1630            case freetext_t:
1631                //inExpr.acceptChildren(v);
1632                this.expr1.acceptChildren(v);
1633                this.expr2.acceptChildren(v);
1634                break;
1635            case case_n_t:
1636                this.getArgs().acceptChildren(v);
1637                break;
1638//            case range_n_t:
1639//                this.expr1.acceptChildren(v);
1640//                for(TRangeNFunctionItem item:rangeNFunctionItems){
1641//                    item.acceptChildren(v);
1642//                }
1643//
1644//                break;
1645            case position_t:
1646                expr1.acceptChildren(v);
1647                expr2.acceptChildren(v);
1648                break;
1649            case substring_t:
1650                if (Args != null){
1651                    Args.acceptChildren(v);
1652                }
1653                if (expr1 != null) expr1.acceptChildren(v);
1654                if (expr2 != null) expr2.acceptChildren(v);
1655                if (expr3 != null){
1656                    expr3.acceptChildren(v);
1657                }
1658                break;
1659            case xmlquery_t:
1660                break;
1661            case xmlcast_t:
1662                break;
1663            case match_against_t:
1664                //for(int i=0;i<this.getMatchColumns().size();i++){
1665                //    psql.linkColumnReferenceToTable(this.getMatchColumns().getObjectName(i),plocation);
1666                //}
1667                this.getAgainstExpr().acceptChildren(v);
1668                break;
1669            case adddate_t:
1670            case date_add_t:
1671            case subdate_t:
1672            case date_sub_t:
1673            case timestampadd_t:
1674            case timestampdiff_t:
1675                expr1.acceptChildren(v);
1676                expr2.acceptChildren(v);
1677                break;
1678            default:
1679                if (Args != null){
1680                    Args.acceptChildren(v);
1681                }
1682                break;
1683        }
1684
1685        if (analyticFunction != null){
1686            analyticFunction.acceptChildren(v);
1687        }
1688
1689        if (filterClause != null){
1690            filterClause.acceptChildren(v);
1691        }
1692
1693        if (windowDef != null){
1694            windowDef.acceptChildren(v);
1695        }
1696
1697        v.postVisit(this);
1698    }
1699
1700     boolean isSQLServerFunctionsONXMLColumn(){
1701//        return ((st.tokencode == TBaseType.rrw_xml_exist)
1702//                ||(st.tokencode == TBaseType.rrw_xml_query)
1703//                ||(st.tokencode == TBaseType.rrw_xml_modify)
1704//                ||(st.tokencode == TBaseType.rrw_xml_nodes)
1705//                ||(st.tokencode == TBaseType.rrw_xml_value)
1706//                );
1707        return ((this.functionType == EFunctionType.xmlmodify_t)
1708                ||(this.functionType == EFunctionType.xmlvalue_t)
1709                ||(this.functionType == EFunctionType.xmlnodes_t)
1710                ||(this.functionType == EFunctionType.xmlexists_t)
1711                ||(this.functionType == EFunctionType.xmlquery_t)
1712        );
1713    }
1714
1715    /**
1716     * MySQL {@code VALUES(col)} and {@code DEFAULT(col)} take a column's name, so a variable of that name is never
1717     * read there, though MySQL reads a variable before a column elsewhere (Mantis 4797).
1718     */
1719    private void markColumnOnlyArgument(TCustomSqlStatement psql){
1720        if ((!psql.isMySQLDialect()) || (functionName == null) || (functionName.getNumberOfPart() != 1)) return;
1721        if ((Args == null) || (Args.size() != 1)) return;
1722        String name = functionName.toString();
1723        if (!("VALUES".equalsIgnoreCase(name) // non-identifier-compare: built-in function keyword
1724                || "DEFAULT".equalsIgnoreCase(name))) return; // non-identifier-compare: built-in function keyword
1725        TExpression arg = Args.getExpression(0);
1726        if ((arg != null) && (arg.getExpressionType() == EExpressionType.simple_object_name_t)
1727                && (arg.getObjectOperand() != null)){
1728            arg.getObjectOperand().setColumnOnly(true);
1729        }
1730    }
1731
1732     boolean isSQLServerOGCMethod(TSourceToken st){
1733        return ((st.tokencode == TBaseType.rrw_starea)
1734                ||(st.tokencode == TBaseType.rrw_stasbinary)
1735                ||(st.tokencode == TBaseType.rrw_stastext)
1736                ||(st.tokencode == TBaseType.rrw_stbuffer)
1737                ||(st.tokencode == TBaseType.rrw_stdimension)
1738                ||(st.tokencode == TBaseType.rrw_stdisjoint)
1739                ||(st.tokencode == TBaseType.rrw_stdistance)
1740                ||(st.tokencode == TBaseType.rrw_stendpoint)
1741                ||(st.tokencode == TBaseType.rrw_stgeometryn)
1742                ||(st.tokencode == TBaseType.rrw_stgeometrytype)
1743                ||(st.tokencode == TBaseType.rrw_stintersection)
1744                ||(st.tokencode == TBaseType.rrw_stintersects)
1745                ||(st.tokencode == TBaseType.rrw_stisclosed)
1746                ||(st.tokencode == TBaseType.rrw_stisempty)
1747                ||(st.tokencode == TBaseType.rrw_stlength)
1748                ||(st.tokencode == TBaseType.rrw_stnumgeometries)
1749                ||(st.tokencode == TBaseType.rrw_stnumpoints)
1750                ||(st.tokencode == TBaseType.rrw_stpointn)
1751                ||(st.tokencode == TBaseType.rrw_stsrid)
1752                ||(st.tokencode == TBaseType.rrw_ststartpoint)
1753                ||(st.tokencode == TBaseType.rrw_stunion)
1754                );
1755    }
1756
1757    /**
1758     * @deprecated As of v1.4.3.0, replaced by {@link #functionType}
1759     */
1760    private int funcType = fntUdf;
1761
1762    /**
1763     * @deprecated As of v1.4.3.0
1764     */
1765    public void setFuncType(int funcType) {
1766        this.funcType = funcType;
1767
1768    }
1769
1770    /**
1771     * @deprecated As of v1.4.3.0, replaced by {@link #getFunctionType()}.
1772     */
1773    public int getFuncType() {
1774
1775        return funcType;
1776    }
1777
1778    /**
1779     * @deprecated As of v1.4.3.0, replaced by {@link EFunctionType#udf_t}
1780     */
1781    public final static int fntUdf = 0;
1782
1783    /**
1784     * @deprecated As of v1.4.3.0, replaced by {@link EFunctionType#trim_t}
1785     */
1786    public final static int fntTrim = 1;
1787
1788    /**
1789     * @deprecated As of v1.4.3.0, replaced by {@link EFunctionType#cast_t}
1790     */
1791    public final static int fntCast = 2;
1792
1793    /**
1794     * @deprecated As of v1.4.3.0, replaced by {@link EFunctionType#convert_t}
1795     */
1796    public final static int fntConvert = 3;
1797
1798    /**
1799     * @deprecated As of v1.4.3.0, replaced by {@link EFunctionType#extract_t}
1800     */
1801    public final static int fntExtract = 4;
1802
1803    /**
1804     * @deprecated As of v1.4.3.0, replaced by {@link EFunctionType#treat_t}
1805     */
1806    public final static int fntTreat = 5;
1807
1808    /**
1809     * @deprecated As of v1.4.3.0, replaced by {@link EFunctionType#contains_t}
1810     */
1811    public final static int fntContains = 6;
1812
1813    /**
1814     * @deprecated As of v1.4.3.0, replaced by {@link EFunctionType#freetext_t}
1815     */
1816    public final static int fntFreetext = 7; //
1817
1818    /**
1819     * @deprecated As of v1.4.3.0, replaced by {@link EFunctionType#case_n_t}
1820     */
1821    public final static int fntCaseN = 10; //teradata
1822
1823    /**
1824     * @deprecated As of v1.4.3.0, replaced by {@link EFunctionType#range_n_t}
1825     */
1826    public final static int fntRangeN = 11; //teradata
1827
1828    /**
1829     * @deprecated As of v1.4.3.0, replaced by {@link EFunctionType#position_t}
1830     */
1831    public final static int fntPosition = 12; //teradata
1832    /**
1833     * @deprecated As of v1.4.3.0, replaced by {@link EFunctionType#substring_t}
1834     */
1835    public final static int fntSubstring = 13; //teradata
1836    /**
1837     * @deprecated As of v1.4.3.0, replaced by {@link EFunctionType#translate_t}
1838     */
1839    public final static int fntTranslate = 14; //teradata ,oracle
1840    /**
1841     * @deprecated As of v1.4.3.0, replaced by {@link EFunctionType#translate_chk_t}
1842     */
1843    public final static int fntTranslateCHK = 15; //teradata
1844    /**
1845     * @deprecated As of v1.4.3.0, replaced by {@link EFunctionType#csum_t}
1846     */
1847    public final static int fntCSUM = 16; //teradata
1848    /**
1849     * @deprecated As of v1.4.3.0, replaced by {@link EFunctionType#rank_t}
1850     */
1851    public final static int fntRank = 17; //teradata
1852    /**
1853     * @deprecated As of v1.4.3.0, replaced by {@link EFunctionType#xmlquery_t}
1854     */
1855    public final static int fntXmlQuery= 18; //oracle
1856
1857    /**
1858     * @deprecated As of v1.4.3.0, replaced by {@link EFunctionType#substring_t}
1859     */
1860    public final static int fntSubString  = 31;//mysql
1861    /**
1862     * @deprecated As of v1.4.3.0, replaced by {@link EFunctionType#adddate_t}
1863     */
1864    public final static int fntAddDate  = 32;//mysql
1865    /**
1866     * @deprecated As of v1.4.3.0, replaced by {@link EFunctionType#date_add_t}
1867     */
1868    public final static int fntDateAdd  = 33;//mysql
1869    /**
1870     * @deprecated As of v1.4.3.0, replaced by {@link EFunctionType#subdate_t}
1871     */
1872    public final static int fntSubDate  = 34;//mysql
1873    /**
1874     * @deprecated As of v1.4.3.0, replaced by {@link EFunctionType#date_sub_t}
1875     */
1876    public final static int fntDateSub  = 35;//mysql
1877    /**
1878     * @deprecated As of v1.4.3.0, replaced by {@link EFunctionType#timestampadd_t}
1879     */
1880    public final static int fntTimestampAdd  = 36;//mysql
1881    /**
1882     * @deprecated As of v1.4.3.0, replaced by {@link EFunctionType#timestampdiff_t}
1883     */
1884    public final static int fntTimestampDiff  = 37;//mysql
1885    /**
1886     * @deprecated As of v1.4.3.0, replaced by {@link EFunctionType#group_concat_t}
1887     */
1888    public final static int fntGroupConcat  = 38;//mysql
1889    /**
1890     * @deprecated As of v1.4.3.0, replaced by {@link EFunctionType#match_against_t}
1891     */
1892    public final static int fntMatchAgainst  = 39;//mysql
1893
1894    /**
1895     * @deprecated As of v1.4.3.0, replaced by {@link EFunctionType#extract_t}
1896     */
1897    public final static int fntExtractXML  = 50;//oracle
1898
1899
1900    /**
1901     * @deprecated As of v1.4.3.0, replaced by {@link EFunctionType#ogc_t}
1902     */
1903    public final static int fntOGC  = 60;
1904
1905    public void setCheckedBuiltIn(boolean isCheckedBuiltIn) {
1906        this.isCheckedBuiltIn = isCheckedBuiltIn;
1907    }
1908
1909    public void setBuiltIn(boolean isBuiltIn) {
1910        this.isBuiltIn = isBuiltIn;
1911    }
1912
1913    public void setXMLType_Instance(TExpression XMLType_Instance) {
1914        this.XMLType_Instance = XMLType_Instance;
1915    }
1916
1917    public void setXPath_String(TExpression XPath_String) {
1918        this.XPath_String = XPath_String;
1919    }
1920
1921    public void setNamespace_String(TExpression namespace_String) {
1922        Namespace_String = namespace_String;
1923    }
1924
1925    public void setFunctionType(EFunctionType functionType) {
1926        this.functionType = functionType;
1927    }
1928
1929    public void setFunctionName(TObjectName functionName) {
1930        this.functionName = functionName;
1931    }
1932
1933    public void setTrim_Expr(TExpression trim_Expr) {
1934        Trim_Expr = trim_Expr;
1935    }
1936
1937    public void setTrim_From_Expr(TExpression trim_From_Expr) {
1938        Trim_From_Expr = trim_From_Expr;
1939    }
1940
1941    /**
1942     * Extracts and analyzes tables from Teradata NPath function's ON clause.
1943     * The NPath function syntax is: NPath(ON table_or_subquery PARTITION BY ... USING ...)
1944     * This method parses the ON clause to extract table references for data lineage.
1945     */
1946    private void analyzeTeradataNPathOnClause(TCustomSqlStatement psql, ESqlClause plocation) {
1947        String funcText = this.toString();
1948        if (funcText == null || funcText.isEmpty()) {
1949            return;
1950        }
1951
1952        // Find the ON clause
1953        String upperFuncText = funcText.toUpperCase(Locale.ROOT);
1954        int onIndex = upperFuncText.indexOf(" ON ");
1955        if (onIndex < 0) {
1956            onIndex = upperFuncText.indexOf("(ON ");
1957            if (onIndex >= 0) {
1958                onIndex++; // Skip the opening parenthesis
1959            }
1960        }
1961        if (onIndex < 0) {
1962            return;
1963        }
1964
1965        // Extract text after ON
1966        String afterOn = funcText.substring(onIndex + 4).trim();
1967        if (afterOn.isEmpty()) {
1968            return;
1969        }
1970
1971        try {
1972            if (afterOn.startsWith("(")) {
1973                // It's a subquery - find matching closing parenthesis
1974                int depth = 0;
1975                int endPos = -1;
1976                for (int i = 0; i < afterOn.length(); i++) {
1977                    char c = afterOn.charAt(i);
1978                    if (c == '(') depth++;
1979                    else if (c == ')') {
1980                        depth--;
1981                        if (depth == 0) {
1982                            endPos = i + 1;
1983                            break;
1984                        }
1985                    }
1986                }
1987                if (endPos > 0) {
1988                    String subqueryText = afterOn.substring(0, endPos);
1989                    // Parse the subquery to extract tables
1990                    TGSqlParser subParser = new TGSqlParser(EDbVendor.dbvteradata);
1991                    subParser.sqltext = "SELECT * FROM " + subqueryText + " __npath_on";
1992                    if (subParser.parse() == 0 && subParser.sqlstatements.size() > 0) {
1993                        gudusoft.gsqlparser.stmt.TSelectSqlStatement subSelect =
1994                            (gudusoft.gsqlparser.stmt.TSelectSqlStatement) subParser.sqlstatements.get(0);
1995                        if (subSelect.tables != null && subSelect.tables.size() > 0) {
1996                            TTable onTable = subSelect.tables.getTable(0);
1997                            // If it has a subquery, analyze its tables
1998                            if (onTable.getSubquery() != null) {
1999                                for (int i = 0; i < onTable.getSubquery().tables.size(); i++) {
2000                                    TTable sourceTable = onTable.getSubquery().tables.getTable(i);
2001                                    addNPathSourceTable(sourceTable, psql);
2002                                }
2003                            }
2004                        }
2005                    }
2006                }
2007            } else {
2008                // It's a direct table reference - extract until whitespace or special char
2009                StringBuilder tableName = new StringBuilder();
2010                for (int i = 0; i < afterOn.length(); i++) {
2011                    char c = afterOn.charAt(i);
2012                    if (Character.isWhitespace(c) || c == ')' || c == ',') {
2013                        break;
2014                    }
2015                    tableName.append(c);
2016                }
2017                if (tableName.length() > 0) {
2018                    // Parse the table reference
2019                    TGSqlParser subParser = new TGSqlParser(EDbVendor.dbvteradata);
2020                    subParser.sqltext = "SELECT * FROM " + tableName.toString() + " __npath_on";
2021                    if (subParser.parse() == 0 && subParser.sqlstatements.size() > 0) {
2022                        gudusoft.gsqlparser.stmt.TSelectSqlStatement subSelect =
2023                            (gudusoft.gsqlparser.stmt.TSelectSqlStatement) subParser.sqlstatements.get(0);
2024                        if (subSelect.tables != null && subSelect.tables.size() > 0) {
2025                            TTable sourceTable = subSelect.tables.getTable(0);
2026                            addNPathSourceTable(sourceTable, psql);
2027                        }
2028                    }
2029                }
2030            }
2031        } catch (Exception e) {
2032            // Silently ignore parsing errors - the main query still works
2033        }
2034    }
2035
2036    /**
2037     * Adds a source table from NPath ON clause to the current statement's table list.
2038     */
2039    private void addNPathSourceTable(TTable sourceTable, TCustomSqlStatement psql) {
2040        if (sourceTable == null || psql == null) {
2041            return;
2042        }
2043        // Mark the table as coming from NPath ON clause
2044        sourceTable.setNPathOnClauseTable(true);
2045        // Add to the statement's table list
2046        if (psql.tables == null) {
2047            psql.tables = new TTableList();
2048        }
2049        psql.tables.addTable(sourceTable);
2050    }
2051
2052}