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