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}