001package gudusoft.gsqlparser.stmt;
002
003
004import gudusoft.gsqlparser.*;
005import gudusoft.gsqlparser.compiler.TFrame;
006import gudusoft.gsqlparser.compiler.TVariable;
007import gudusoft.gsqlparser.nodes.*;
008import gudusoft.gsqlparser.nodes.mssql.TProcedureOption;
009import gudusoft.gsqlparser.sqlenv.TSQLFunction;
010import gudusoft.gsqlparser.sqlenv.TSQLTable;
011import gudusoft.gsqlparser.stmt.db2.TDb2ReturnStmt;
012import gudusoft.gsqlparser.stmt.mssql.TMssqlBlock;
013import gudusoft.gsqlparser.stmt.mssql.TMssqlReturn;
014
015/**
016 * Create function.
017 *
018 * Supported database:
019 *
020 * <ul>
021 *     <li>BigQuery</li>
022 * </ul>
023 */
024public class TCreateFunctionStmt extends TRoutine{
025
026    private TMssqlBlock block = null;
027    private TMssqlReturn returnStmt = null;
028    private TPTNodeList <TProcedureOption> procedureOptions;
029    private TObjectName returnTableVaraible = null;
030    private TTableElementList returnTableDefinitions = null;
031    private TTypeName returnDataType = null;
032    private int functionType = TBaseType.uftScalar;
033
034    //  private TObjectName outerLabelName = null;
035    private TConstant objfile;
036    private TConstant linkSymbol;
037
038    private String className;
039    private String resourceType;//jar, file
040    private String resourceURI;//
041    private EFunctionReturnsType returnsType = EFunctionReturnsType.frtScalar;
042
043    //private TTypeName returnDataType = null;
044    private int returnMode = TBaseType.function_return_datatype;
045
046    private TExpression sqlExpression;//bigquery
047    private String sharedLibraryName;
048    private TConstant functionDefinition;
049    private TConstant procedureLanguage;
050
051
052    public void setProcedureOptions(TPTNodeList<TProcedureOption> procedureOptions) {
053        this.procedureOptions = procedureOptions;
054    }
055
056    public TPTNodeList<TProcedureOption> getProcedureOptions() {
057        return procedureOptions;
058    }
059
060
061     // TGSqlParser newParser ;
062//    static int gCount;
063//    static {
064//        //newParser = new TGSqlParser(EDbVendor.dbvpostgresql);
065//        gCount = 0;
066//    }
067    public TCreateFunctionStmt (EDbVendor dbvendor){
068        super(dbvendor);
069        sqlstatementtype = ESqlStatementType.sstcreatefunction ;
070       // newParser = new TGSqlParser(EDbVendor.dbvpostgresql);
071    }
072
073    private TObjectName functionName = null;
074    @Override
075    public TObjectName getStoredProcedureName(){
076        return functionName;
077    }
078    /**
079     * The name that you give to the function that you are declaring or defining.
080     * @return
081     */
082    public TObjectName getFunctionName() {
083        return functionName;
084    }
085
086    public EFunctionReturnsType getReturnsType() {
087        return returnsType;
088    }
089
090    public int getReturnMode() {
091        return returnMode;
092    }
093
094    /**
095     *
096     * @return statements in create function
097     */
098    public TMssqlBlock getBlock() {
099        return block;
100    }
101
102    /**
103     *
104     * @return this is the only return statement in create function.
105     */
106    public TMssqlReturn getReturnStmt() {
107        return returnStmt;
108    }
109
110    public TObjectName getReturnTableVaraible() {
111        return returnTableVaraible;
112    }
113
114    /**
115     * when {@link #getReturnsType()} == {@link EFunctionReturnsType#frtMultiStatementTableValue}
116     * returns this table_type_definition
117     *
118     * @return table_type_definition
119     */
120    public TTableElementList getReturnTableDefinitions() {
121        return returnTableDefinitions;
122    }
123    public TTypeName getReturnDataType() {
124        return returnDataType;
125    }
126
127    /**
128     * this is used for backward compatibility  of .NET version TMssqlCreateFunction.functiontype
129     * please use {@link #returnMode} in java version
130     * @return
131     */
132    public int getFunctionType() {
133        int ret = this.functionType;
134        if (this.returnMode == TBaseType.function_return_table_variable){
135            ret = TBaseType.uftMultiStatementTableValued;
136        }else if (this.returnMode == TBaseType.function_return_table){
137            ret = TBaseType.uftInlineTableValued;
138        }
139        return ret;
140    }
141
142    public TConstant getObjfile() {
143        return objfile;
144    }
145    public TConstant getLinkSymbol() {
146        return linkSymbol;
147    }
148    public String getClassName() {
149        return className;
150    }
151    public String getResourceType() {
152        return resourceType;
153    }
154    public String getResourceURI() {
155        return resourceURI;
156    }
157
158    public void setSqlExpression(TExpression sqlExpression) {
159        this.sqlExpression = sqlExpression;
160    }
161    public TExpression getSqlExpression() {
162        return sqlExpression;
163    }
164    public String getSharedLibraryName() {
165        return sharedLibraryName;
166    }
167    public TConstant getFunctionDefinition() {
168        return functionDefinition;
169    }
170    public TConstant getProcedureLanguage() {
171        return procedureLanguage;
172    }
173
174
175    private void redshiftFunctionDefinition(TCustomSqlStatement psql,TCreateFunctionSqlNode createFunctionNode){
176        language = TRoutine.LANGUAGE_UNKNOWN;
177        if ((createFunctionNode.getFunctionBody() != null)&&(getProcedureLanguage().toString().equalsIgnoreCase("sql"))){
178            language = TRoutine.LANGUAGE_SQL;
179            String bodyStr  = createFunctionNode.getFunctionBody().getStartToken().getQuotedString();//.trim();
180            if (createFunctionNode.getFunctionBody().getStartToken().toString().startsWith("'")){
181                bodyStr = bodyStr.replaceAll("''","'");
182            }
183
184
185            //System.out.println(bodyStr);
186            int testLen = 9;
187            if (bodyStr.trim().length() < testLen) testLen = bodyStr.trim().length();
188
189            String prefixStr = bodyStr.trim().substring(0,testLen).toLowerCase();
190            boolean isExpression = true;
191            TGSqlParser newParser = new TGSqlParser(this.dbvendor);
192
193            // Keep only the column padding (stringBlock first arg 0); the line
194            // offset is applied after parsing so it is never capped at 1000.
195            int bodyDeltaLine = (int)createFunctionNode.getFunctionBody().getStartToken().lineNo - 1;
196            newParser.sqltext =
197                    TBaseType.stringBlock(
198                            0,
199                            (int)createFunctionNode.getFunctionBody().getStartToken().columnNo + createFunctionNode.getFunctionBody().getStartToken().getQuoteSymbolLength()-1
200                    )
201                    + bodyStr;
202
203            newParser.setFrameStack(getFrameStack());
204            int iRet = newParser.parse();
205            TReparseCoordinateShifter.shift(newParser, bodyDeltaLine, 0);
206            if ((iRet == 0)&&(newParser.getSqlstatements().size() >0)){
207//                this.blockBody = new TBlockSqlNode();
208//                this.blockBody.setParsed(true);
209//                this.blockBody.getBodyStatements().add(newParser.getSqlstatements().get(0));
210
211               this.getBodyStatements().add(newParser.getSqlstatements().get(0));
212            }else {
213                for(int j=0;j<newParser.getErrorCount();j++){
214                    this.parseerrormessagehandle(newParser.getSyntaxErrors().get(j));
215                }
216            }
217        }
218
219    }
220
221    private void postgresqlFunctionDefinition(TCustomSqlStatement psql,TCreateFunctionSqlNode createFunctionNode){
222        language =  TRoutine.LANGUAGE_UNKNOWN;
223        if ((createFunctionNode.getFunctionBody() != null)&&(getProcedureLanguage()!=null)&&(getProcedureLanguage().toString() !=null)) {
224            if (getProcedureLanguage().toString().equalsIgnoreCase("sql")) language = TRoutine.LANGUAGE_SQL;
225            else if (getProcedureLanguage().toString().equalsIgnoreCase("plpgsql")) language = TRoutine.LANGUAGE_PLPGSQL;
226            else if (getProcedureLanguage().toString().equalsIgnoreCase("'plpgsql'")) language = TRoutine.LANGUAGE_PLPGSQL;
227
228            if ((language == TRoutine.LANGUAGE_SQL)||(language == TRoutine.LANGUAGE_PLPGSQL))
229            {
230
231                String bodyStr = createFunctionNode.getFunctionBody().getStartToken().getQuotedString();//.trim();
232                // System.out.println(bodyStr);
233                //long lineNo = createFunctionNode.getFunctionBody().getStartToken().lineNo ;
234                // CREATE OR REPLACE FUNCTION testspg__getString (varchar) RETURNS varchar as ' DECLARE inString alias for $1; begin return ''bob''; end; ' LANGUAGE plpgsql
235                // escaped quotes in string literals
236                // mantisbt/view.php?id=1331
237                if (createFunctionNode.getFunctionBody().getStartToken().toString().startsWith("'")) {
238                    bodyStr = bodyStr.replaceAll("''", "'");
239                }
240
241
242                // System.out.println(bodyStr);
243                //String prefixStr = bodyStr.trim().substring(0, (bodyStr.trim().length() < 9 ? bodyStr.trim().length() : 9)).toLowerCase();
244                //String suffixStr = bodyStr.trim().length() <= 7 ? bodyStr.trim() : bodyStr.trim().substring(bodyStr.trim().length() - 7);
245                                
246                String bodyStrTrim = bodyStr.trim();
247                int bodyStringLength = bodyStrTrim.length();
248                String prefixStr = bodyStringLength <= 7 ? bodyStrTrim.toLowerCase() : bodyStrTrim.substring(0, 7).toLowerCase();
249                String suffixStr = bodyStringLength <= 7 ? prefixStr : bodyStrTrim.substring(bodyStringLength - 7).toLowerCase();
250                                
251                boolean isSQLBlock = true;
252                TGSqlParser newParser = new TGSqlParser(EDbVendor.dbvpostgresql);
253                // Keep only the column padding (stringBlock first arg 0); the
254                // line offset is applied after parsing so it is never capped at
255                // 1000. The marker line "plpgsql_function_delimiter\n" occupies
256                // parser line 1, so the body starts on parser line 2 and the
257                // delta there is (lineNo - 2); without the marker it is
258                // (lineNo - 1).
259                int bodyDeltaLine;
260                if ((prefixStr.startsWith("declare")) || (prefixStr.startsWith("begin")) || (prefixStr.startsWith("<<"))
261                        // || (((suffixStr.toLowerCase().endsWith("end")) || (suffixStr.toLowerCase().endsWith("end;")))&&((!prefixStr.startsWith("select"))))
262                                                   || (((suffixStr.endsWith("end")) || (suffixStr.endsWith("end;")))&&((!prefixStr.startsWith("select"))))
263                ) {
264                    //bodyStr.replaceAll("''","'");
265                    //System.out.println(bodyStr);
266
267                    bodyDeltaLine = (int) createFunctionNode.getFunctionBody().getStartToken().lineNo - 2;
268                    newParser.sqltext = "plpgsql_function_delimiter\n"
269                            + TBaseType.stringBlock(0, (int) createFunctionNode.getFunctionBody().getStartToken().columnNo + createFunctionNode.getFunctionBody().getStartToken().getQuoteSymbolLength() - 1)
270                            + bodyStr;
271                } else {
272                    bodyDeltaLine = (int) createFunctionNode.getFunctionBody().getStartToken().lineNo - 1;
273                    newParser.sqltext =
274                            TBaseType.stringBlock(
275                                    0,
276                                    (int) createFunctionNode.getFunctionBody().getStartToken().columnNo + createFunctionNode.getFunctionBody().getStartToken().getQuoteSymbolLength() - 1)
277                                    + bodyStr;
278                    isSQLBlock = false;
279                }
280
281                newParser.setFrameStack(getFrameStack());
282                // we only need a raw parse tree
283                // newParser.setOnlyNeedRawParseTree(true);
284                int iRet = newParser.parse();
285                TReparseCoordinateShifter.shift(newParser, bodyDeltaLine, 0);
286                if ((iRet == 0) && (newParser.getSqlstatements().size() > 0)) {
287                    if (isSQLBlock) {
288                        TCommonBlock lcBlock = (TCommonBlock) newParser.getSqlstatements().get(0);
289                        this.blockBody = lcBlock.getBlockBody();
290                        this.blockBody.setParent(this);
291
292
293//                        this.setOuterLabelName(lcBlock.getLabelName());
294//                        for(int i=0;i<lcBlock.getDeclareStatements().size();i++){
295//                            this.getTopStatement().getSymbolTable().push( new TSymbolTableItem(TObjectName.ttobjVariable,this,lcBlock.getDeclareStatements().get(i)));
296//                            this.getDeclareStatements().add(lcBlock.getDeclareStatements().get(i));
297//                        }
298//                        for(int i=0;i<lcBlock.getBodyStatements().size();i++){
299//                            lcBlock.getBodyStatements().get(i).setAlreadyAddToParent(false);
300//                            lcBlock.getBodyStatements().get(i).setParentStmt(this);
301//                            //commonBlock.getBodyStatements().get(i).doParseStatement(this);
302//                            this.getBodyStatements().add(lcBlock.getBodyStatements().get(i));
303//                        }
304//                        if (lcBlock.getExceptionClause() != null){
305//                            this.setExceptionClause(lcBlock.getExceptionClause());
306//                        }
307//                        for(int i=0;i<lcBlock.getDeclareStatements().size();i++){
308//                            this.getTopStatement().getSymbolTable().pop();
309//                        }
310                    }else{
311                        this.getBodyStatements().add(newParser.getSqlstatements().get(0));
312//                        TStatementListSqlNode lcStmts = new TStatementListSqlNode();
313//                        TStatementSqlNode lcSqlNode = new TStatementSqlNode();
314//                        lcSqlNode.setSqlNode(newParser.getSqlstatements().get(0).rootNode);
315//                        lcStmts.addStatementSqlNode(lcSqlNode);
316//                        this.blockBody = new TBlockSqlNode();
317//                        this.blockBody.init(lcStmts);
318                    }
319                } else {
320                        for (int j = 0; j < newParser.getErrorCount(); j++) {
321                            this.parseerrormessagehandle(newParser.getSyntaxErrors().get(j));
322                        }
323                }
324
325                }
326            }
327
328    }
329
330    private void snowflakeFunctionDefinition(TCustomSqlStatement psql,TCreateFunctionSqlNode createFunctionNode){
331        language = TRoutine.LANGUAGE_UNKNOWN;
332        if ((createFunctionNode.getFunctionBody() != null)
333                &&((getProcedureLanguage()==null)||((getProcedureLanguage()!=null)&&(getProcedureLanguage().toString().equalsIgnoreCase("SQL"))))){
334                language = TRoutine.LANGUAGE_SQL;
335                String bodyStr  = createFunctionNode.getFunctionBody().getStartToken().getQuotedString();//.trim();
336                // CREATE OR REPLACE FUNCTION testspg__getString (varchar) RETURNS varchar as ' DECLARE inString alias for $1; begin return ''bob''; end; ' LANGUAGE plpgsql
337                // escaped quotes in string literals
338                // mantisbt/view.php?id=1331
339                if (createFunctionNode.getFunctionBody().getStartToken().toString().startsWith("'")){
340                    // Unescape C-style backslash escapes (\' -> ') before SQL-standard escapes
341                    // Snowflake supports both \' and '' for escaping single quotes in string literals
342                    bodyStr = bodyStr.replace("\\'", "'");
343                    bodyStr = bodyStr.replaceAll("''","'");
344                }
345
346                //System.out.println(bodyStr);
347                String trimmedBody = bodyStr.trim();
348
349                // Skip re-parsing for non-SQL bodies (e.g., SPCS URL paths like '/echo')
350                if (trimmedBody.length() > 0 && !Character.isLetterOrDigit(trimmedBody.charAt(0))
351                        && trimmedBody.charAt(0) != '(' && trimmedBody.charAt(0) != '-'
352                        && trimmedBody.charAt(0) != '+' && trimmedBody.charAt(0) != '\'') {
353                    return;
354                }
355
356                int testLen = 9;
357                if (trimmedBody.length() < testLen) testLen = trimmedBody.length();
358                String prefixStr = trimmedBody.substring(0,testLen).toLowerCase();
359                boolean isExpression = true;
360                TGSqlParser newParser = new TGSqlParser(this.dbvendor);
361                // Keep only the column padding (stringBlock first arg 0); the
362                // line offset is applied after parsing so it is never capped at
363                // 1000. The marker line "pseudo_expr_sign\n" occupies parser
364                // line 1, so the body starts on parser line 2 and the delta
365                // there is (lineNo - 2); without the marker it is (lineNo - 1).
366                int bodyDeltaLine;
367                if ((prefixStr.startsWith("select"))||(prefixStr.startsWith("insert"))||(prefixStr.startsWith("delete"))||(prefixStr.startsWith("update"))){
368                    bodyDeltaLine = (int)createFunctionNode.getFunctionBody().getStartToken().lineNo - 1;
369                    newParser.sqltext =
370                            TBaseType.stringBlock(
371                                    0,
372                                    (int)createFunctionNode.getFunctionBody().getStartToken().columnNo + createFunctionNode.getFunctionBody().getStartToken().getQuoteSymbolLength()-1)
373                                + bodyStr;
374
375                    isExpression = false;
376                }else{
377                    bodyDeltaLine = (int)createFunctionNode.getFunctionBody().getStartToken().lineNo - 2;
378                    newParser.sqltext = "pseudo_expr_sign\n"+
379                             TBaseType.stringBlock(0
380                            ,(int)createFunctionNode.getFunctionBody().getStartToken().columnNo+ createFunctionNode.getFunctionBody().getStartToken().getQuoteSymbolLength()-1)
381                            +bodyStr;
382                }
383
384                newParser.setFrameStack(getFrameStack());
385                int iRet = newParser.parse();
386                TReparseCoordinateShifter.shift(newParser, bodyDeltaLine, 0);
387                if ((iRet == 0)&&(newParser.getSqlstatements().size() >0)){
388                    this.getBodyStatements().add(newParser.getSqlstatements().get(0));
389                }else {
390                    for(int j=0;j<newParser.getErrorCount();j++){
391                        this.parseerrormessagehandle(newParser.getSyntaxErrors().get(j));
392                    }
393                }
394        }
395
396    }
397
398    private TSelectSqlStatement sqlQuery;
399
400    public TSelectSqlStatement getSqlQuery() {
401        return sqlQuery;
402    }
403
404    public int doParseStatement(TCustomSqlStatement psql) {
405        if (rootNode == null) return -1;
406        TCreateFunctionSqlNode createFunctionNode = (TCreateFunctionSqlNode)rootNode;
407        if (dbvendor == EDbVendor.dbvpostgresql){
408            if (super.doParseStatement(psql) != 0) return -1;
409        }else
410            super.doParseStatement(psql);
411
412        TFrame currentFrame = new TFrame(this.stmtScope);
413        currentFrame.pushMeToStack(getFrameStack());
414
415        functionName = createFunctionNode.getFunctionName();
416
417        if (getSqlEnv() != null) {
418            getSqlEnv().addFunction(functionName,true);
419
420            // move to TDatabaseObjectResolver
421
422//            if (getSqlEnv().getDefaultCatalogName() != null){
423//                if (functionName.getDatabaseToken() == null){
424//                    functionName.setDatabaseToken(new TSourceToken(getSqlEnv().getDefaultCatalogName()));
425//                }
426//            }
427        }
428
429        // sql server
430        procedureOptions = createFunctionNode.getProcedureOptions();
431        //end sql server
432
433        if (createFunctionNode.getProcedureLanguage() != null){
434            // language name is retrieved through parser
435            procedureLanguage = createFunctionNode.getProcedureLanguage();
436            setRoutineBodyInConstant(procedureLanguage);
437            setRoutineLanguage(procedureLanguage.toString());
438        }else if (getRoutineLanguage() != null){
439            // language name is retrieved during TGsqlParser.dopostgresqlgetrawsqlstatements()
440        }
441
442//        procedureLanguage = createFunctionNode.getProcedureLanguage();
443//        if (procedureLanguage != null){
444//            setRoutineBodyInConstant(procedureLanguage);
445//            setRoutineLanguage(procedureLanguage.toString());
446//        }
447//
448//        // postgresql
449//        procedureLanguage = createFunctionNode.getProcedureLanguage();
450//        if (getRoutineLanguage() == null){
451//            // not already set during TGsqlParser.dopostgresqlgetrawsqlstatements()
452//            // then we set it here
453//            if (procedureLanguage != null){
454//                setRoutineLanguage(procedureLanguage.toString());
455//            }
456//        }
457
458        //outerLabelName = createFunctionNode.getLabelName();
459        objfile = createFunctionNode.getObjfile();
460        linkSymbol = createFunctionNode.getLinkSymbol();
461        this.className = createFunctionNode.getClassName();
462        this.resourceType = createFunctionNode.getResourceType();
463        this.resourceURI = createFunctionNode.getResourceURI();
464
465
466        if (createFunctionNode.getReturnDataType() != null){
467            this.returnMode = TBaseType.function_return_datatype;
468            this.returnDataType = createFunctionNode.getReturnDataType();
469        }else if (createFunctionNode.getReturnTable() != null){
470            TDummy dmy = createFunctionNode.getReturnTable();
471            this.returnMode = TBaseType.function_return_table;
472            if (dmy.list1 instanceof TTableElementList){ // hana includes TParameterDeclarationList type which is not this type
473                this.returnTableDefinitions = (TTableElementList)dmy.list1;
474                this.returnTableDefinitions.doParse(this,ESqlClause.unknown);
475            }
476        }
477        // end of postgresql
478
479        this.setParameterDeclarations(createFunctionNode.getParameters());
480
481        // sql server
482        if (createFunctionNode.getReturnDataType() != null){
483            this.returnMode = TBaseType.function_return_datatype;
484            this.returnDataType = createFunctionNode.getReturnDataType();
485            this.returnsType = EFunctionReturnsType.frtScalar;
486        }else if (createFunctionNode.getReturnTable() != null){
487            TDummy dmy = createFunctionNode.getReturnTable();
488            if (dmy.node1 != null){
489                this.returnsType = EFunctionReturnsType.frtMultiStatementTableValue;
490                this.returnMode = TBaseType.function_return_table_variable;
491                this.returnTableVaraible = (TObjectName)dmy.node1;
492                this.returnTableVaraible.setObjectType(TObjectName.ttobjVariable);
493                this.returnTableDefinitions = (TTableElementList)dmy.list1;
494                this.returnTableDefinitions.doParse(this,ESqlClause.unknown);
495
496                if (getSqlEnv() != null)  {
497                    TSQLTable returnTable = getSqlEnv().addTable(this.returnTableVaraible.toString(),true);
498                    //TSQLFunction functionTable = getSqlEnv().searchFunction(functionName.toString());
499                    TSQLFunction functionTable = getSqlEnv().searchFunction(functionName);
500
501                    if (functionTable == null){
502                        TBaseType.log(String.format("Table function: <%s> is not found in SQL Evn", functionName.toString()),TLog.WARNING,functionName);
503                        //System.out.println("Function not found:"+functionName.toString());
504                        //System.out.println(getSqlEnv().toString());
505                    }
506
507                    for(TTableElement column: this.returnTableDefinitions){
508                        if(column.getColumnDefinition()!=null && column.getColumnDefinition().getColumnName()!=null){
509                            returnTable.addColumn(column.getColumnDefinition().getColumnName().toString());
510                            if (functionTable != null) {
511                                functionTable.addReturnColumn(column.getColumnDefinition().getColumnName().toString());
512                            }
513                        }
514                    }
515                }
516            }else{
517                this.returnMode = TBaseType.function_return_table;
518                this.returnsType = EFunctionReturnsType.frtInlineTableValue;
519            }
520        }
521        // end sql server
522
523        // push parameterDeclarations into symbolTable
524        if (this.getParameterDeclarations() != null){
525            for(int i=0;i< this.getParameterDeclarations().size();i++){
526                this.getTopStatement().getSymbolTable().push( new TSymbolTableItem(TObjectName.ttobjParameter,this, this.getParameterDeclarations().getParameterDeclarationItem(i)));
527                TParameterDeclaration parameterDeclaration = this.getParameterDeclarations().getParameterDeclarationItem(i);
528                if (parameterDeclaration.getParameterName() != null){
529                    this.stmtScope.addSymbol(new TVariable(parameterDeclaration.getParameterName(),parameterDeclaration,functionName));
530                }
531            }
532        }
533
534
535        switch (this.dbvendor){
536            case dbvsnowflake:
537                if (createFunctionNode.getBlcok() != null){
538                    // $$ body $$ 在这里处理
539                    createFunctionNode.getBlcok().doParse(this,ESqlClause.unknown);
540                    this.blockBody = createFunctionNode.getBlcok();
541                }else  {
542                    // 'body' 在这里处理
543                    snowflakeFunctionDefinition(psql,createFunctionNode);
544                }
545
546                break;
547            case dbvpostgresql:
548            case dbvgreenplum:
549            case dbvredshift:
550                if (createFunctionNode.getFunctionBody() != null){
551                    // function body only inside '' will be processed here
552                    postgresqlFunctionDefinition(psql,createFunctionNode);
553                }else{
554                    // function body only inside $$ will be processed here
555
556                    if (createFunctionNode.getBlcok() != null){
557                        createFunctionNode.getBlcok().doParse(this,ESqlClause.unknown);
558                        this.blockBody = createFunctionNode.getBlcok();
559                    }else  if (createFunctionNode.getStmt() != null){
560                        createFunctionNode.getStmt().doParse(this, ESqlClause.unknown);
561                        this.getBodyStatements().add(createFunctionNode.getStmt().getStmt());
562                    }else {
563                        // there is no function body is language is not in sql or plsql,
564                        // such as LANGUAGE plpython3u
565                    }
566                }
567                break;
568//            case dbvgreenplum:
569//                postgresqlFunctionDefinition(psql,createFunctionNode);
570//                break;
571            case dbvmssql:
572                if (createFunctionNode.getBlcok() != null){
573                    block = new TMssqlBlock(this.dbvendor);
574                    block.rootNode = createFunctionNode.getBlcok();
575                    block.doParseStatement(this);
576                    this.getBodyStatements().add(block);
577                }
578
579                if (createFunctionNode.getReturnSqlNode() != null){
580                    returnStmt = new TMssqlReturn(this.dbvendor);
581                    returnStmt.rootNode = createFunctionNode.getReturnSqlNode();
582                    returnStmt.doParseStatement(this);
583                    this.getBodyStatements().add(returnStmt);
584                }
585
586                break;
587            case dbvmysql:
588                if (createFunctionNode.getStmt() != null){
589                    createFunctionNode.getStmt().doParse(this,ESqlClause.unknown);
590                    this.getBodyStatements().add(createFunctionNode.getStmt().getStmt());
591                }
592                else if (createFunctionNode.getBlcok() != null){
593                    createFunctionNode.getBlcok().getStmts().doParse(this,ESqlClause.unknown);
594
595                    for(int i=0;i<createFunctionNode.getBlcok().getStmts().size();i++){
596                        this.getBodyStatements().add(createFunctionNode.getBlcok().getStmts().getStatementSqlNode(i).getStmt());
597                    }
598                }
599                break;
600            case dbvbigquery:
601                if (createFunctionNode.getSqlQuery() != null){
602                    sqlQuery = new TSelectSqlStatement(this.dbvendor);
603                    sqlQuery.rootNode = createFunctionNode.getSqlQuery();
604                    sqlQuery.doParseStatement(this);
605                    this.returnMode = TBaseType.function_return_datatype;
606                    this.returnDataType = createFunctionNode.getReturnDataType();
607                    this.returnsType = EFunctionReturnsType.frtInlineTableValue;
608                    this.getBodyStatements().add(sqlQuery);
609                }
610                break;
611            case dbvdb2:
612                TCompoundSqlNode compoundSqlNode = createFunctionNode.getCompoundSql();
613               // TReturnSqlNode returnSqlNode = createFunctionNode.getReturnSqlNode();
614
615                if (compoundSqlNode != null){
616                    if (compoundSqlNode.getDeclareStmts() != null){
617                        compoundSqlNode.getDeclareStmts().doParse(this,ESqlClause.unknown);
618
619                        // push variable declare into symbolTable, and add to declareStatements
620                        for(int i=0;i<compoundSqlNode.getDeclareStmts().size();i++){
621                            this.getTopStatement().getSymbolTable().push( new TSymbolTableItem(TObjectName.ttobjVariable,this,compoundSqlNode.getDeclareStmts().getStatementSqlNode(i).getStmt() ));
622                            this.getDeclareStatements().add(compoundSqlNode.getDeclareStmts().getStatementSqlNode(i).getStmt());
623                        }
624                    }
625
626                    if (compoundSqlNode.getStmts() != null){
627                        compoundSqlNode.getStmts().doParse(this,ESqlClause.unknown);
628
629                        for(int i= 0; i<compoundSqlNode.getStmts().size();i++){
630                            this.getBodyStatements().add(compoundSqlNode.getStmts().getStatementSqlNode(i).getStmt());
631                        }
632                    }
633
634                    if (compoundSqlNode.getDeclareStmts() != null){
635                        // pop variable declare from symbolTable
636                        for(int i=0;i<compoundSqlNode.getDeclareStmts().size();i++){
637                            this.getTopStatement().getSymbolTable().pop();
638                        }
639                    }
640                }else if (createFunctionNode.getReturnSqlNode() != null){
641                    returnStmt = new TMssqlReturn(this.dbvendor);
642                    returnStmt.rootNode = createFunctionNode.getReturnSqlNode();
643                    returnStmt.doParseStatement(this);
644                    this.getBodyStatements().add(returnStmt);
645                }
646
647                break;
648            default:
649                if (createFunctionNode.getStmt() != null){
650                    createFunctionNode.getStmt().doParse(this, ESqlClause.unknown);
651                    this.getBodyStatements().add(createFunctionNode.getStmt().getStmt());
652//                    TStatementListSqlNode lcStmts = new TStatementListSqlNode();
653//                    lcStmts.addStatementSqlNode(createFunctionNode.getStmt());
654//                    this.blockBody = new TBlockSqlNode();
655//                    this.blockBody.init(lcStmts);
656                }
657                else if (createFunctionNode.getBlcok() != null){
658                    createFunctionNode.getBlcok().doParse(this,ESqlClause.unknown);
659                    //createFunctionNode.getBlcok().getStmts().doParse(this,ESqlClause.unknown);
660//
661//                    for(int i=0;i<createFunctionNode.getBlcok().getStmts().size();i++){
662//                        this.getBodyStatements().add(createFunctionNode.getBlcok().getStmts().getStatementSqlNode(i).getStmt());
663//                    }
664                    this.blockBody = createFunctionNode.getBlcok();
665                }
666                break;
667        }
668
669
670
671
672        // pop parameterDeclarations from symbolTable
673        if (this.getParameterDeclarations() != null){
674            for(int i=0;i< this.getParameterDeclarations().size();i++){
675                this.getTopStatement().getSymbolTable().pop();
676            }
677        }
678
679        if (createFunctionNode.getSharedLibraryName() != null){
680            sharedLibraryName = createFunctionNode.getSharedLibraryName().toString();
681        }
682
683        functionDefinition  = createFunctionNode.getFunctionDefinition();
684        if (functionDefinition != null){
685            setRoutineBodyInConstant(functionDefinition);
686            setRoutineBody(functionDefinition.toString());
687        }
688
689        this.sqlExpression = createFunctionNode.getSqlExpression();
690        if (this.sqlExpression != null){
691            this.sqlExpression.doParse(this,ESqlClause.unknown);
692        }
693
694        // StarRocks-specific fields
695        this.starrocksGlobal = createFunctionNode.isStarrocksGlobal();
696        this.starrocksAggregate = createFunctionNode.isStarrocksAggregate();
697        this.starrocksTableFunction = createFunctionNode.isStarrocksTableFunction();
698        this.starrocksOrReplace = createFunctionNode.isStarrocksOrReplace();
699        this.starrocksIntermediateType = createFunctionNode.getStarrocksIntermediateType();
700        this.starrocksProperties = createFunctionNode.getStarrocksProperties();
701
702        //endlabelName = createFunctionNode.getEndlabelName();
703        currentFrame.popMeFromStack(getFrameStack());
704
705        return 0;
706    }
707
708    public void accept(TParseTreeVisitor v){
709        v.preVisit(this);
710        v.postVisit(this);
711    }
712
713    public void acceptChildren(TParseTreeVisitor v){
714        v.preVisit(this);
715        this.getFunctionName().acceptChildren(v);
716        if (getParameterDeclarations() != null) getParameterDeclarations().acceptChildren(v);
717        if (blockBody != null){
718            blockBody.acceptChildren(v);
719        }else if (getBodyStatements().size() > 0){
720            getBodyStatements().acceptChildren(v);
721        }
722        // Visit sqlExpression for BigQuery functions where body is an expression (e.g., AS ((SELECT ...)))
723        // This ensures UNNEST tables and column references inside the expression are collected
724        if (sqlExpression != null) {
725            sqlExpression.acceptChildren(v);
726        }
727        if (returnStmt != null) returnStmt.acceptChildren(v);
728        v.postVisit(this);
729    }
730
731
732    public void setFunctionName(TObjectName functionName) {
733        this.functionName = functionName;
734    }
735
736    public void setBlock(TMssqlBlock block) {
737        this.block = block;
738    }
739    public void setReturnStmt(TMssqlReturn returnStmt) {
740        this.returnStmt = returnStmt;
741    }
742    public void setReturnMode(int returnMode) {
743        this.returnMode = returnMode;
744    }
745    public void setReturnTableVaraible(TObjectName returnTableVaraible) {
746        this.returnTableVaraible = returnTableVaraible;
747    }
748    public void setReturnTableDefinitions(TTableElementList returnTableDefinitions) {
749        this.returnTableDefinitions = returnTableDefinitions;
750    }
751    public void setReturnDataType(TTypeName returnDataType) {
752        this.returnDataType = returnDataType;
753    }
754    public void setFunctionType(int functionType) {
755        this.functionType = functionType;
756    }
757
758    // StarRocks-specific fields
759    private boolean starrocksGlobal = false;
760    private boolean starrocksAggregate = false;
761    private boolean starrocksTableFunction = false;
762    private boolean starrocksOrReplace = false;
763    private TTypeName starrocksIntermediateType = null;
764    private TPTNodeList starrocksProperties = null;
765
766    public boolean isStarrocksGlobal() { return starrocksGlobal; }
767    public boolean isStarrocksAggregate() { return starrocksAggregate; }
768    public boolean isStarrocksTableFunction() { return starrocksTableFunction; }
769    public boolean isStarrocksOrReplace() { return starrocksOrReplace; }
770    public TTypeName getStarrocksIntermediateType() { return starrocksIntermediateType; }
771    public TPTNodeList getStarrocksProperties() { return starrocksProperties; }
772
773}