001package gudusoft.gsqlparser.stmt; 002 003import gudusoft.gsqlparser.*; 004import gudusoft.gsqlparser.compiler.TSymbolTableManager; 005import gudusoft.gsqlparser.compiler.TVariable; 006import gudusoft.gsqlparser.nodes.*; 007 008 009/** 010 * execute statement 011 * 012 * db: couchbase, netezza,greenplum,mysql, postgresql,redshift 013 * 014 * @TODO: 2024/2/7 {@link gudusoft.gsqlparser.stmt.mssql.TMssqlExecute}, {@link TExecImmeStmt} should merge into this class 015 */ 016public class TExecuteSqlStatement extends TCustomSqlStatement { 017 018 private String preparedSqlText; 019 020 public void setPreparedSqlText(String preparedSqlText) { 021 this.preparedSqlText = preparedSqlText; 022 } 023 024 public String getPreparedSqlText() { 025 return preparedSqlText; 026 } 027 028 private EExecType executeType = EExecType.unknown; 029 030 public EExecType getExecuteType() { 031 return executeType; 032 } 033 034 private TCustomSqlStatement stmt; 035 private boolean variableResolutionAttempted = false; 036 037 public TCustomSqlStatement getStmt() { 038 // For Snowflake, try lazy resolution of variable references 039 if (stmt == null && !variableResolutionAttempted && dbvendor == EDbVendor.dbvsnowflake) { 040 variableResolutionAttempted = true; 041 if (stmtString != null && stmtString.getExpressionType() == EExpressionType.simple_object_name_t) { 042 String tokenStr = stmtString.getStartToken().toString(); 043 String varName = tokenStr; 044 if (varName.startsWith(":")) { 045 varName = varName.substring(1); 046 } 047 String sqlContent = findVariableAssignmentValue(varName); 048 if (sqlContent != null && !sqlContent.isEmpty()) { 049 TGSqlParser parser = new TGSqlParser(EDbVendor.dbvsnowflake); 050 parser.sqltext = sqlContent; 051 int ret = parser.parse(); 052 if (ret == 0 && parser.sqlstatements.size() > 0) { 053 stmt = parser.sqlstatements.get(0); 054 } 055 } 056 } 057 } 058 return stmt; 059 } 060 061 public TExecuteSqlStatement(){ 062 this(EDbVendor.dbvpostgresql); 063 } 064 065 public TExecuteSqlStatement(EDbVendor dbvendor) { 066 super(dbvendor); 067 sqlstatementtype = ESqlStatementType.sstExecute; 068 } 069 070 public int doParseStatement(TCustomSqlStatement psql) { 071 072 if (rootNode == null) return -1; 073 super.doParseStatement(psql); 074 075 TExecuteSqlNode sqlNode = (TExecuteSqlNode)rootNode; 076 this.executeType = sqlNode.getExecuteType(); 077 078 switch (dbvendor){ 079 case dbvgaussdb: 080 if (this.executeType == EExecType.direct) { 081 // EXECUTE DIRECT ON (nodename_list) 'sql_string' 082 this.directOnNodes = sqlNode.getDirectOnNodes(); 083 this.stmtString = sqlNode.getStmtString(); 084 if (this.stmtString != null) { 085 sqlText = stmtString.toString(); 086 } 087 break; 088 } 089 // Fall through to postgresql handling for regular EXECUTE 090 case dbvpostgresql: 091 this.stmtString = sqlNode.getStmtString(); 092 this.intoVariable = sqlNode.getIntoVariable(); 093 this.usingVariables = sqlNode.getUsingVariables(); 094 095 sqlText = stmtString.toString(); 096 if (stmtString.getExpressionType() == EExpressionType.function_t){ 097 if (stmtString.toString().startsWith("format")){ 098 //postgresql format function 099 TFunctionCall functionCall = stmtString.getFunctionCall(); 100 sqlText = functionCall.getArgs().getExpression(0).toString(); 101 sqlText = sqlText.replaceAll("%s","PLACEHOLDER"); 102 } 103 } 104 // System.out.println(sqlText); 105 106 this.moduleName = sqlNode.getModuleName(); 107 this.statementName = sqlNode.getModuleName(); 108 this.parameters = sqlNode.getStringValues(); 109 110 break; 111 case dbvsnowflake: 112 if (this.executeType == EExecType.task) { 113 // EXECUTE TASK statement - just store the task name 114 this.moduleName = sqlNode.getModuleName(); 115 } else if (this.executeType == EExecType.from_stage) { 116 // EXECUTE IMMEDIATE FROM @stage/path or FROM 'path' 117 // Nothing to resolve - the source is a file reference 118 } else { 119 // EXECUTE IMMEDIATE statement 120 this.stmtString = sqlNode.getStmtString(); 121 //System.out.println(stmtString.toString()); 122 TSourceToken st = stmtString.getStartToken(); 123 String tokenStr = st.toString(); 124 if (tokenStr.startsWith("$$")){ 125 // Dollar-quoted string 126 TGSqlParser parser = new TGSqlParser(EDbVendor.dbvsnowflake); 127 parser.sqltext = TBaseType.stringBlock((int)st.lineNo - 1,(int)st.columnNo)+ TBaseType.getStringInsideLiteral(tokenStr); 128 int ret = parser.parse(); 129 //A whitespace-only or comment-only block parses with 130 //rc 0 and ZERO statements (mantis 4681) — guard get(0). 131 if (ret == 0 && parser.sqlstatements.size() > 0){ 132 stmt = parser.sqlstatements.get(0); 133 }else if (ret != 0){ 134 for(int j=0;j<parser.getErrorCount();j++){ 135 this.parseerrormessagehandle(parser.getSyntaxErrors().get(j)); 136 } 137 } 138 } else if (tokenStr.startsWith("'") && tokenStr.endsWith("'")){ 139 // Single-quoted string literal - extract SQL and parse it 140 String sqlContent = TBaseType.getStringInsideLiteral(tokenStr); 141 if (sqlContent != null && !sqlContent.isEmpty()){ 142 TGSqlParser parser = new TGSqlParser(EDbVendor.dbvsnowflake); 143 parser.sqltext = TBaseType.stringBlock((int)st.lineNo - 1,(int)st.columnNo + 1) + sqlContent; 144 int ret = parser.parse(); 145 if (ret == 0 && parser.sqlstatements.size() > 0){ 146 stmt = parser.sqlstatements.get(0); 147 } 148 // Silently ignore parse errors for dynamic SQL - it may contain placeholders 149 } 150 } 151 // Variable references (e.g., :SQLStmt) are resolved lazily in getStmt() 152 } 153 break; 154 case dbvnetezza: 155 this.moduleName = sqlNode.getModuleName(); 156 this.statementName = sqlNode.getModuleName(); 157 this.parameters = sqlNode.getStringValues(); 158 preparedValue = sqlNode.getPreparedValue(); 159 if (preparedValue != null){ 160 preparedValue.doParse(this, ESqlClause.unknown); 161 } 162 163 switch (executeType){ 164 case expr: 165 if (sqlNode.getPreparedValue().getExpressionType() == EExpressionType.function_t){ 166 TFunctionCall functionCall = sqlNode.getPreparedValue().getFunctionCall(); 167 this.moduleName = functionCall.getFunctionName(); 168 this.parameters = functionCall.getArgs(); 169 this.executeType = EExecType.module; 170 } 171 break; 172 default: 173 break; 174 } 175 176 break; 177 case dbvredshift: 178 this.moduleName = sqlNode.getModuleName(); 179 this.statementName = sqlNode.getModuleName(); 180 this.parameters = sqlNode.getStringValues(); 181 preparedValue = sqlNode.getPreparedValue(); 182 if (preparedValue != null){ 183 preparedValue.doParse(this, ESqlClause.unknown); 184 185 if (this.preparedValue.getExpressionType() == EExpressionType.simple_object_name_t){ 186 TObjectName var = this.preparedValue.getObjectOperand(); 187 // if (var.getDbObjectType() == EDbObjectType.variable){ 188 TVariable symbolVariable = TSymbolTableManager.searchSymbolVariable(this.getFrameStack(),var.toString()); 189 if (symbolVariable != null){ 190 this.sqlText = symbolVariable.getVariableStr(); 191 //System.out.println(this.sqlText); 192 } 193 // } 194 } 195 196 } 197 break; 198 default: 199 this.moduleName = sqlNode.getModuleName(); 200 this.statementName = sqlNode.getModuleName(); 201 this.parameters = sqlNode.getStringValues(); 202 preparedValue = sqlNode.getPreparedValue(); 203 if (preparedValue != null){ 204 preparedValue.doParse(this, ESqlClause.unknown); 205 } 206 207 // available in TMssqlExecute currently, maybe will be moved to here in later version 208 //this.stringValues = sqlNode.getStringValues(); 209 //this.linkServerName = sqlNode.getLinkServerName(); 210 211 break; 212 } 213 return 0; 214 } 215 216 private static final Object DYNAMIC_PARSE_LOCK = new Object(); 217 218 private TStatementList dynamicStatements = null; 219 220 /** 221 * 222 * @return sql statement instance that generated dynamically based on {@link #sqlText} 223 */ 224 public TStatementList getDynamicStatements() { 225 if (this.dynamicStatements != null) return this.dynamicStatements; 226 if (this.sqlText == null) return null; 227 228 String query = this.sqlText; 229 if((this.preparedValue != null) && (this.preparedValue.getPlainTextLineNo() != -1)){ 230 long lineNo = this.preparedValue.getPlainTextLineNo(); 231 long columnNo = this.preparedValue.getPlainTextColumnNo(); 232 query = TBaseType.stringBlock((int)lineNo - 1,(int)columnNo)+ this.sqlText; 233 } 234 235 // Use a local parser instance for thread safety instead of a shared static parser 236 TGSqlParser localParser = new TGSqlParser(EDbVendor.dbvredshift); 237 localParser.sqltext = query; 238 int ret = localParser.parse(); 239 240 if ( ret != 0){ 241 for(int j=0;j<localParser.getErrorCount();j++){ 242 this.parseerrormessagehandle(localParser.getSyntaxErrors().get(j)); 243 } 244 245 return null; 246 } 247 248 this.dynamicStatements = new TStatementList(); 249 for(int i=0;i<localParser.sqlstatements.size();i++){ 250 if (this.getParentStmt() == null){ 251 localParser.sqlstatements.get(i).setParentStmt(this); 252 }else{ 253 localParser.sqlstatements.get(i).setParentStmt(this.getParentStmt()); 254 } 255 256 this.dynamicStatements.add(localParser.sqlstatements.get(i)); 257 } 258 return dynamicStatements; 259 } 260 261 public TObjectName getLinkServerName() { 262 return linkServerName; 263 } 264 265 // GaussDB: EXECUTE DIRECT ON (nodename_list) 'sql' 266 private TObjectNameList directOnNodes; 267 268 public TObjectNameList getDirectOnNodes() { 269 return directOnNodes; 270 } 271 272 private TObjectName linkServerName; 273 private TExpressionList stringValues = null; 274 275 public TExpressionList getStringValues() { 276 return stringValues; 277 } 278 279 private TObjectName moduleName; 280 281 public TObjectName getModuleName() { 282 return moduleName; 283 } 284 285 private TExpression stmtString; 286 private TObjectName intoVariable; 287 private TExpressionList usingVariables; 288 289 private TObjectName statementName; 290 private TExpressionList parameters; 291 private TExpression preparedValue;//couchbase 292 293 public void setSqlText(String sqlText) { 294 this.sqlText = sqlText; 295 } 296 297 private String sqlText = null; 298 299 public String getSqlText() { 300 return sqlText; 301 } 302 303 public void init(Object arg1){ 304 stmtString = (TExpression)arg1; 305 sqlText = stmtString.toString(); 306 switch (stmtString.getExpressionType()){ 307 case simple_object_name_t: 308 moduleName = stmtString.getObjectOperand(); 309 //moduleName.setObjectType(TObjectName.ttobjProcedureName); 310 moduleName.setDbObjectType(EDbObjectType.procedure); 311 break; 312 case function_t: 313 if (stmtString.toString().startsWith("format")){ 314 //postgresql format function 315 TFunctionCall functionCall = stmtString.getFunctionCall(); 316 sqlText = functionCall.getArgs().getExpression(0).toString(); 317 sqlText = sqlText.replaceAll("%s","PLACEHOLDER"); 318 } 319 break; 320 } 321 322 } 323 public void init(Object arg1,Object arg2){ 324 init(arg1); 325 intoVariable = (TObjectName)arg2; 326 327 } 328 329 public TObjectName getIntoVariable() { 330 return intoVariable; 331 } 332 333 public TExpression getStmtString() { 334 return stmtString; 335 } 336 337 public TExpressionList getUsingVariables() { 338 return usingVariables; 339 } 340 341 public void init(Object arg1,Object arg2, Object arg3){ 342 init(arg1,arg2); 343 usingVariables = (TExpressionList)arg3; 344 345 } 346 347 public void accept(TParseTreeVisitor v){ 348 v.preVisit(this); 349 v.postVisit(this); 350 } 351 352 public void acceptChildren(TParseTreeVisitor v){ 353 v.preVisit(this); 354 v.postVisit(this); 355 } 356 357 public void setStmtString(TExpression stmtString) { 358 this.stmtString = stmtString; 359 } 360 361 public void setIntoVariable(TObjectName intoVariable) { 362 this.intoVariable = intoVariable; 363 } 364 365 public void setUsingVariables(TExpressionList usingVariables) { 366 this.usingVariables = usingVariables; 367 } 368 369 public TObjectName getStatementName() { 370 return statementName; 371 } 372 public TExpressionList getParameters() { 373 return parameters; 374 } 375 public void setStatementName(TObjectName statementName) { 376 this.statementName = statementName; 377 } 378 public void setParameters(TExpressionList parameters) { 379 this.parameters = parameters; 380 } 381 public TExpression getPreparedValue() { 382 return preparedValue; 383 } 384 385 /** 386 * Search for a variable assignment in the parent block statements. 387 * Used by Snowflake EXECUTE IMMEDIATE to resolve variable references. 388 * 389 * @param varName the variable name to search for 390 * @return the SQL string assigned to the variable, or null if not found 391 */ 392 private String findVariableAssignmentValue(String varName) { 393 TCustomSqlStatement parent = this.getParentStmt(); 394 // Debug output 395 // System.out.println("[DEBUG] findVariableAssignmentValue: varName=" + varName + ", parent=" + (parent != null ? parent.getClass().getSimpleName() : "null")); 396 if (parent == null) return null; 397 398 TStatementList bodyStmts = null; 399 400 // Get body statements from parent - check multiple levels 401 TCustomSqlStatement current = parent; 402 while (current != null && bodyStmts == null) { 403 if (current instanceof TBlockSqlStatement) { 404 bodyStmts = ((TBlockSqlStatement) current).getBodyStatements(); 405 } else if (current instanceof TCreateProcedureStmt) { 406 bodyStmts = ((TCreateProcedureStmt) current).getBodyStatements(); 407 } 408 if (bodyStmts == null || bodyStmts.size() == 0) { 409 bodyStmts = null; 410 current = current.getParentStmt(); 411 } 412 } 413 414 // System.out.println("[DEBUG] bodyStmts=" + (bodyStmts != null ? bodyStmts.size() : "null")); 415 416 if (bodyStmts == null) return null; 417 418 // Search backwards through statements to find the most recent assignment 419 for (int i = bodyStmts.size() - 1; i >= 0; i--) { 420 TCustomSqlStatement bodyStmt = bodyStmts.get(i); 421 // System.out.println("[DEBUG] bodyStmt[" + i + "]=" + bodyStmt.getClass().getSimpleName()); 422 if (bodyStmt instanceof TAssignStmt) { 423 TAssignStmt assignStmt = (TAssignStmt) bodyStmt; 424 TExpression leftExpr = assignStmt.getLeft(); 425 if (leftExpr != null) { 426 String leftName = leftExpr.toString(); 427 // Handle colon prefix if present 428 if (leftName.startsWith(":")) { 429 leftName = leftName.substring(1); 430 } 431 // System.out.println("[DEBUG] checking assignment: leftName=" + leftName + ", varName=" + varName); 432 if (leftName.equalsIgnoreCase(varName)) { 433 // Found the assignment - extract the SQL content 434 TExpression rightExpr = assignStmt.getExpression(); 435 if (rightExpr != null) { 436 String value = rightExpr.toString(); 437 // System.out.println("[DEBUG] found assignment: value=" + value); 438 // If it's a string literal, extract the content 439 if (value.startsWith("'") && value.endsWith("'")) { 440 return TBaseType.getStringInsideLiteral(value); 441 } else if (value.startsWith("$$") && value.endsWith("$$")) { 442 return TBaseType.getStringInsideLiteral(value); 443 } 444 return value; 445 } 446 } 447 } 448 } 449 } 450 return null; 451 } 452 453}