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