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(java.util.Locale.ROOT);
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(java.util.Locale.ROOT) : bodyStrTrim.substring(0, 7).toLowerCase(java.util.Locale.ROOT);
263                String suffixStr = bodyStringLength <= 7 ? prefixStr : bodyStrTrim.substring(bodyStringLength - 7).toLowerCase(java.util.Locale.ROOT);
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(java.util.Locale.ROOT);
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                    TCustomSqlStatement lcBodyStmt = newParser.getSqlstatements().get(0);
403                    if (!isExpression){
404                        // A single-quoted body holds the same thing a $$...$$ body does, so
405                        // expose it the same way: getBlockBody() must not depend on which
406                        // delimiter the author used (MantisBT 4723). The $$ branch in
407                        // doParseStatement() assigns createFunctionNode.getBlcok(); there is no
408                        // grammar block node here, so wrap the re-parsed statement in one.
409                        //
410                        // ORDER MATTERS. TBlockSqlStatement.getBodyStatements() returns the
411                        // fallback bodyStatements list only WHILE blockBody is null, and
412                        // delegates to blockBody.getBodyStatements() once it is set. Filling the
413                        // block before publishing it keeps getBodyStatements() returning the body
414                        // throughout - assigning blockBody first would make it answer from an
415                        // empty list and silently drop the lineage that already worked.
416                        TBlockSqlNode lcBlock = new TBlockSqlNode();
417                        // Constructing the node directly skips the node factory, which would
418                        // otherwise stamp these. Without them the block reports dbvgeneric and
419                        // node type 0, so a consumer that switches on either mis-identifies the
420                        // one node this fix exists to expose.
421                        lcBlock.setGsqlparser(this.getGsqlparser());
422                        lcBlock.setNodeType(ENodeType.T_BlockSqlNode.getId());
423                        lcBodyStmt.setParent(lcBlock);
424                        lcBlock.getBodyStatements().add(lcBodyStmt);
425                        lcBlock.setParsed(true);
426                        lcBlock.setParent(this);
427                        // A synthetic node starts with no token boundaries, which leaves
428                        // toString() null and getLineNo() -1 while the $$ block reports the body
429                        // text and its real position. Take the boundaries from the re-parsed body
430                        // statement - TReparseCoordinateShifter.shift() has already moved it onto
431                        // the outer statement's coordinates, so these positions line up with the
432                        // original SQL rather than with the throwaway parser's text.
433                        lcBlock.setStartToken(lcBodyStmt);
434                        lcBlock.setEndToken(lcBodyStmt);
435                        this.blockBody = lcBlock;
436                    } else {
437                        // An expression body (AS 'a + b') is re-parsed behind a synthetic
438                        // pseudo_expr_sign statement. That wrapper is not a routine block, so it
439                        // stays out of blockBody and is reachable through getBodyStatements().
440                        this.getBodyStatements().add(lcBodyStmt);
441                    }
442                }else {
443                    for(int j=0;j<newParser.getErrorCount();j++){
444                        this.parseerrormessagehandle(newParser.getSyntaxErrors().get(j));
445                    }
446                }
447        }
448
449    }
450
451    private TSelectSqlStatement sqlQuery;
452
453    public TSelectSqlStatement getSqlQuery() {
454        return sqlQuery;
455    }
456
457    public int doParseStatement(TCustomSqlStatement psql) {
458        if (rootNode == null) return -1;
459        // A failed parse can leave a TParseErrorSqlNode (or a node from a
460        // previous statement) in rootNode; never cast blindly.
461        if (!(rootNode instanceof TCreateFunctionSqlNode)) return -1;
462        TCreateFunctionSqlNode createFunctionNode = (TCreateFunctionSqlNode)rootNode;
463        if (dbvendor == EDbVendor.dbvpostgresql){
464            if (super.doParseStatement(psql) != 0) return -1;
465        }else
466            super.doParseStatement(psql);
467
468        TFrame currentFrame = new TFrame(this.stmtScope);
469        currentFrame.pushMeToStack(getFrameStack());
470
471        functionName = createFunctionNode.getFunctionName();
472
473        if (getSqlEnv() != null) {
474            getSqlEnv().addFunction(functionName,true);
475
476            // move to TDatabaseObjectResolver
477
478//            if (getSqlEnv().getDefaultCatalogName() != null){
479//                if (functionName.getDatabaseToken() == null){
480//                    functionName.setDatabaseToken(new TSourceToken(getSqlEnv().getDefaultCatalogName()));
481//                }
482//            }
483        }
484
485        // sql server
486        procedureOptions = createFunctionNode.getProcedureOptions();
487        //end sql server
488
489        if (createFunctionNode.getProcedureLanguage() != null){
490            // language name is retrieved through parser
491            procedureLanguage = createFunctionNode.getProcedureLanguage();
492            setRoutineBodyInConstant(procedureLanguage);
493            setRoutineLanguage(procedureLanguage.toString());
494        }else if (getRoutineLanguage() != null){
495            // language name is retrieved during TGsqlParser.dopostgresqlgetrawsqlstatements()
496        }
497
498//        procedureLanguage = createFunctionNode.getProcedureLanguage();
499//        if (procedureLanguage != null){
500//            setRoutineBodyInConstant(procedureLanguage);
501//            setRoutineLanguage(procedureLanguage.toString());
502//        }
503//
504//        // postgresql
505//        procedureLanguage = createFunctionNode.getProcedureLanguage();
506//        if (getRoutineLanguage() == null){
507//            // not already set during TGsqlParser.dopostgresqlgetrawsqlstatements()
508//            // then we set it here
509//            if (procedureLanguage != null){
510//                setRoutineLanguage(procedureLanguage.toString());
511//            }
512//        }
513
514        //outerLabelName = createFunctionNode.getLabelName();
515        objfile = createFunctionNode.getObjfile();
516        linkSymbol = createFunctionNode.getLinkSymbol();
517        this.className = createFunctionNode.getClassName();
518        this.resourceType = createFunctionNode.getResourceType();
519        this.resourceURI = createFunctionNode.getResourceURI();
520
521
522        if (createFunctionNode.getReturnDataType() != null){
523            this.returnMode = TBaseType.function_return_datatype;
524            this.returnDataType = createFunctionNode.getReturnDataType();
525        }else if (createFunctionNode.getReturnTable() != null){
526            TDummy dmy = createFunctionNode.getReturnTable();
527            this.returnMode = TBaseType.function_return_table;
528            if (dmy.list1 instanceof TTableElementList){ // hana includes TParameterDeclarationList type which is not this type
529                this.returnTableDefinitions = (TTableElementList)dmy.list1;
530                this.returnTableDefinitions.doParse(this,ESqlClause.unknown);
531            }
532        }
533        // end of postgresql
534
535        this.setParameterDeclarations(createFunctionNode.getParameters());
536
537        // sql server
538        if (createFunctionNode.getReturnDataType() != null){
539            this.returnMode = TBaseType.function_return_datatype;
540            this.returnDataType = createFunctionNode.getReturnDataType();
541            this.returnsType = EFunctionReturnsType.frtScalar;
542        }else if (createFunctionNode.getReturnTable() != null){
543            TDummy dmy = createFunctionNode.getReturnTable();
544            if (dmy.node1 != null){
545                this.returnsType = EFunctionReturnsType.frtMultiStatementTableValue;
546                this.returnMode = TBaseType.function_return_table_variable;
547                this.returnTableVaraible = (TObjectName)dmy.node1;
548                this.returnTableVaraible.setObjectType(TObjectName.ttobjVariable);
549                this.returnTableDefinitions = (TTableElementList)dmy.list1;
550                this.returnTableDefinitions.doParse(this,ESqlClause.unknown);
551
552                if (getSqlEnv() != null)  {
553                    TSQLTable returnTable = getSqlEnv().addTable(this.returnTableVaraible.toString(),true);
554                    //TSQLFunction functionTable = getSqlEnv().searchFunction(functionName.toString());
555                    TSQLFunction functionTable = getSqlEnv().searchFunction(functionName);
556
557                    if (functionTable == null){
558                        TBaseType.log(String.format("Table function: <%s> is not found in SQL Evn", functionName.toString()),TLog.WARNING,functionName);
559                        //System.out.println("Function not found:"+functionName.toString());
560                        //System.out.println(getSqlEnv().toString());
561                    }
562
563                    for(TTableElement column: this.returnTableDefinitions){
564                        if(column.getColumnDefinition()!=null && column.getColumnDefinition().getColumnName()!=null){
565                            if (returnTable != null) {
566                                // addTable returns null when enableGetMetadataFromDDL is off
567                                returnTable.addColumn(column.getColumnDefinition().getColumnName().toString());
568                            }
569                            if (functionTable != null) {
570                                functionTable.addReturnColumn(column.getColumnDefinition().getColumnName().toString());
571                            }
572                        }
573                    }
574                }
575            }else if ((createFunctionNode.getExternalName() != null)
576                    && (dmy.list1 instanceof TTableElementList)){
577                // CLR table-valued function: RETURNS TABLE (column_list) with no
578                // table variable and no SQL body, the rows come from EXTERNAL NAME.
579                // The EXTERNAL NAME is what distinguishes this from the other
580                // dialects that also hang a list off RETURNS TABLE.
581                this.returnsType = EFunctionReturnsType.frtCLRTableValue;
582                this.returnMode = TBaseType.function_return_table;
583                this.returnTableDefinitions = (TTableElementList)dmy.list1;
584                this.returnTableDefinitions.doParse(this,ESqlClause.unknown);
585
586                if (getSqlEnv() != null)  {
587                    TSQLFunction functionTable = getSqlEnv().searchFunction(functionName);
588
589                    if (functionTable == null){
590                        TBaseType.log(String.format("Table function: <%s> is not found in SQL Evn", functionName.toString()),TLog.WARNING,functionName);
591                    }else{
592                        for(TTableElement column: this.returnTableDefinitions){
593                            if(column.getColumnDefinition()!=null && column.getColumnDefinition().getColumnName()!=null){
594                                functionTable.addReturnColumn(column.getColumnDefinition().getColumnName().toString());
595                            }
596                        }
597                    }
598                }
599            }else{
600                this.returnMode = TBaseType.function_return_table;
601                this.returnsType = EFunctionReturnsType.frtInlineTableValue;
602            }
603        }
604
605        // The CLR method name is not a database object, so it is exposed through
606        // getExternalName() but deliberately kept out of acceptChildren() — the
607        // resolver must not try to bind assembly.class.method to a table column.
608        if (createFunctionNode.getExternalName() != null){
609            this.externalName = createFunctionNode.getExternalName();
610        }
611        // end sql server
612
613        // push parameterDeclarations into symbolTable
614        if (this.getParameterDeclarations() != null){
615            for(int i=0;i< this.getParameterDeclarations().size();i++){
616                this.getTopStatement().getSymbolTable().push( new TSymbolTableItem(TObjectName.ttobjParameter,this, this.getParameterDeclarations().getParameterDeclarationItem(i)));
617                TParameterDeclaration parameterDeclaration = this.getParameterDeclarations().getParameterDeclarationItem(i);
618                if (parameterDeclaration.getParameterName() != null){
619                    this.stmtScope.addSymbol(new TVariable(parameterDeclaration.getParameterName(),parameterDeclaration,functionName));
620                }
621            }
622        }
623
624
625        switch (this.dbvendor){
626            case dbvsnowflake:
627                if (createFunctionNode.getBlcok() != null){
628                    // $$ body $$ 在这里处理
629                    createFunctionNode.getBlcok().doParse(this,ESqlClause.unknown);
630                    this.blockBody = createFunctionNode.getBlcok();
631                }else  {
632                    // 'body' 在这里处理
633                    snowflakeFunctionDefinition(psql,createFunctionNode);
634                }
635
636                break;
637            case dbvpostgresql:
638            case dbvgreenplum:
639            case dbvredshift:
640                if (createFunctionNode.getFunctionBody() != null){
641                    // function body only inside '' will be processed here
642                    postgresqlFunctionDefinition(psql,createFunctionNode);
643                }else{
644                    // function body only inside $$ will be processed here
645
646                    if (createFunctionNode.getBlcok() != null){
647                        createFunctionNode.getBlcok().doParse(this,ESqlClause.unknown);
648                        this.blockBody = createFunctionNode.getBlcok();
649                    }else  if (createFunctionNode.getStmt() != null){
650                        createFunctionNode.getStmt().doParse(this, ESqlClause.unknown);
651                        this.getBodyStatements().add(createFunctionNode.getStmt().getStmt());
652                    }else {
653                        // there is no function body is language is not in sql or plsql,
654                        // such as LANGUAGE plpython3u
655                    }
656                }
657                break;
658//            case dbvgreenplum:
659//                postgresqlFunctionDefinition(psql,createFunctionNode);
660//                break;
661            case dbvmssql:
662                if (createFunctionNode.getBlcok() != null){
663                    block = new TMssqlBlock(this.dbvendor);
664                    block.rootNode = createFunctionNode.getBlcok();
665                    block.doParseStatement(this);
666                    this.getBodyStatements().add(block);
667                }
668
669                if (createFunctionNode.getReturnSqlNode() != null){
670                    returnStmt = new TMssqlReturn(this.dbvendor);
671                    returnStmt.rootNode = createFunctionNode.getReturnSqlNode();
672                    returnStmt.doParseStatement(this);
673                    this.getBodyStatements().add(returnStmt);
674                }
675
676                break;
677            case dbvmysql:
678                if (createFunctionNode.getStmt() != null){
679                    createFunctionNode.getStmt().doParse(this,ESqlClause.unknown);
680                    this.getBodyStatements().add(createFunctionNode.getStmt().getStmt());
681                }
682                else if (createFunctionNode.getBlcok() != null){
683                    createFunctionNode.getBlcok().getStmts().doParse(this,ESqlClause.unknown);
684
685                    for(int i=0;i<createFunctionNode.getBlcok().getStmts().size();i++){
686                        this.getBodyStatements().add(createFunctionNode.getBlcok().getStmts().getStatementSqlNode(i).getStmt());
687                    }
688                }
689                break;
690            case dbvbigquery:
691                if (createFunctionNode.getSqlQuery() != null){
692                    sqlQuery = new TSelectSqlStatement(this.dbvendor);
693                    sqlQuery.rootNode = createFunctionNode.getSqlQuery();
694                    sqlQuery.doParseStatement(this);
695                    this.returnMode = TBaseType.function_return_datatype;
696                    this.returnDataType = createFunctionNode.getReturnDataType();
697                    this.returnsType = EFunctionReturnsType.frtInlineTableValue;
698                    this.getBodyStatements().add(sqlQuery);
699                }
700                break;
701            case dbvdb2:
702                TCompoundSqlNode compoundSqlNode = createFunctionNode.getCompoundSql();
703               // TReturnSqlNode returnSqlNode = createFunctionNode.getReturnSqlNode();
704
705                if (compoundSqlNode != null){
706                    if (compoundSqlNode.getDeclareStmts() != null){
707                        compoundSqlNode.getDeclareStmts().doParse(this,ESqlClause.unknown);
708
709                        // push variable declare into symbolTable, and add to declareStatements
710                        for(int i=0;i<compoundSqlNode.getDeclareStmts().size();i++){
711                            this.getTopStatement().getSymbolTable().push( new TSymbolTableItem(TObjectName.ttobjVariable,this,compoundSqlNode.getDeclareStmts().getStatementSqlNode(i).getStmt() ));
712                            this.getDeclareStatements().add(compoundSqlNode.getDeclareStmts().getStatementSqlNode(i).getStmt());
713                        }
714                    }
715
716                    if (compoundSqlNode.getStmts() != null){
717                        compoundSqlNode.getStmts().doParse(this,ESqlClause.unknown);
718
719                        for(int i= 0; i<compoundSqlNode.getStmts().size();i++){
720                            this.getBodyStatements().add(compoundSqlNode.getStmts().getStatementSqlNode(i).getStmt());
721                        }
722                    }
723
724                    if (compoundSqlNode.getDeclareStmts() != null){
725                        // pop variable declare from symbolTable
726                        for(int i=0;i<compoundSqlNode.getDeclareStmts().size();i++){
727                            this.getTopStatement().getSymbolTable().pop();
728                        }
729                    }
730                }else if (createFunctionNode.getReturnSqlNode() != null){
731                    returnStmt = new TMssqlReturn(this.dbvendor);
732                    returnStmt.rootNode = createFunctionNode.getReturnSqlNode();
733                    returnStmt.doParseStatement(this);
734                    this.getBodyStatements().add(returnStmt);
735                }
736
737                break;
738            default:
739                if (createFunctionNode.getStmt() != null){
740                    createFunctionNode.getStmt().doParse(this, ESqlClause.unknown);
741                    this.getBodyStatements().add(createFunctionNode.getStmt().getStmt());
742//                    TStatementListSqlNode lcStmts = new TStatementListSqlNode();
743//                    lcStmts.addStatementSqlNode(createFunctionNode.getStmt());
744//                    this.blockBody = new TBlockSqlNode();
745//                    this.blockBody.init(lcStmts);
746                }
747                else if (createFunctionNode.getBlcok() != null){
748                    createFunctionNode.getBlcok().doParse(this,ESqlClause.unknown);
749                    //createFunctionNode.getBlcok().getStmts().doParse(this,ESqlClause.unknown);
750//
751//                    for(int i=0;i<createFunctionNode.getBlcok().getStmts().size();i++){
752//                        this.getBodyStatements().add(createFunctionNode.getBlcok().getStmts().getStatementSqlNode(i).getStmt());
753//                    }
754                    this.blockBody = createFunctionNode.getBlcok();
755                }
756                break;
757        }
758
759
760
761
762        // pop parameterDeclarations from symbolTable
763        if (this.getParameterDeclarations() != null){
764            for(int i=0;i< this.getParameterDeclarations().size();i++){
765                this.getTopStatement().getSymbolTable().pop();
766            }
767        }
768
769        if (createFunctionNode.getSharedLibraryName() != null){
770            sharedLibraryName = createFunctionNode.getSharedLibraryName().toString();
771        }
772
773        functionDefinition  = createFunctionNode.getFunctionDefinition();
774        if (functionDefinition != null){
775            setRoutineBodyInConstant(functionDefinition);
776            setRoutineBody(functionDefinition.toString());
777        }
778
779        this.sqlExpression = createFunctionNode.getSqlExpression();
780        if (this.sqlExpression != null){
781            this.sqlExpression.doParse(this,ESqlClause.unknown);
782        }
783
784        // StarRocks-specific fields
785        this.starrocksGlobal = createFunctionNode.isStarrocksGlobal();
786        this.starrocksAggregate = createFunctionNode.isStarrocksAggregate();
787        this.starrocksTableFunction = createFunctionNode.isStarrocksTableFunction();
788        this.starrocksOrReplace = createFunctionNode.isStarrocksOrReplace();
789        this.starrocksIntermediateType = createFunctionNode.getStarrocksIntermediateType();
790        this.starrocksProperties = createFunctionNode.getStarrocksProperties();
791
792        //endlabelName = createFunctionNode.getEndlabelName();
793        currentFrame.popMeFromStack(getFrameStack());
794
795        return 0;
796    }
797
798    public void accept(TParseTreeVisitor v){
799        v.preVisit(this);
800        v.postVisit(this);
801    }
802
803    public void acceptChildren(TParseTreeVisitor v){
804        v.preVisit(this);
805        if (this.getFunctionName() != null) this.getFunctionName().acceptChildren(v);
806        if (getParameterDeclarations() != null) getParameterDeclarations().acceptChildren(v);
807        if (blockBody != null){
808            blockBody.acceptChildren(v);
809        }else if (getBodyStatements().size() > 0){
810            getBodyStatements().acceptChildren(v);
811        }
812        // Visit sqlExpression for BigQuery functions where body is an expression (e.g., AS ((SELECT ...)))
813        // This ensures UNNEST tables and column references inside the expression are collected
814        if (sqlExpression != null) {
815            sqlExpression.acceptChildren(v);
816        }
817        if (returnStmt != null) returnStmt.acceptChildren(v);
818        v.postVisit(this);
819    }
820
821
822    public void setFunctionName(TObjectName functionName) {
823        this.functionName = functionName;
824    }
825
826    public void setBlock(TMssqlBlock block) {
827        this.block = block;
828    }
829    public void setReturnStmt(TMssqlReturn returnStmt) {
830        this.returnStmt = returnStmt;
831    }
832    public void setReturnMode(int returnMode) {
833        this.returnMode = returnMode;
834    }
835    public void setReturnTableVaraible(TObjectName returnTableVaraible) {
836        this.returnTableVaraible = returnTableVaraible;
837    }
838    public void setReturnTableDefinitions(TTableElementList returnTableDefinitions) {
839        this.returnTableDefinitions = returnTableDefinitions;
840    }
841    public void setReturnDataType(TTypeName returnDataType) {
842        this.returnDataType = returnDataType;
843    }
844    public void setFunctionType(int functionType) {
845        this.functionType = functionType;
846    }
847
848    // StarRocks-specific fields
849    private boolean starrocksGlobal = false;
850    private boolean starrocksAggregate = false;
851    private boolean starrocksTableFunction = false;
852    private boolean starrocksOrReplace = false;
853    private TTypeName starrocksIntermediateType = null;
854    private TPTNodeList starrocksProperties = null;
855
856    public boolean isStarrocksGlobal() { return starrocksGlobal; }
857    public boolean isStarrocksAggregate() { return starrocksAggregate; }
858    public boolean isStarrocksTableFunction() { return starrocksTableFunction; }
859    public boolean isStarrocksOrReplace() { return starrocksOrReplace; }
860    public TTypeName getStarrocksIntermediateType() { return starrocksIntermediateType; }
861    public TPTNodeList getStarrocksProperties() { return starrocksProperties; }
862
863}