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}