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 dbvpostgresql: 080 this.stmtString = sqlNode.getStmtString(); 081 this.intoVariable = sqlNode.getIntoVariable(); 082 this.usingVariables = sqlNode.getUsingVariables(); 083 084 sqlText = stmtString.toString(); 085 if (stmtString.getExpressionType() == EExpressionType.function_t){ 086 if (stmtString.toString().startsWith("format")){ 087 //postgresql format function 088 TFunctionCall functionCall = stmtString.getFunctionCall(); 089 sqlText = functionCall.getArgs().getExpression(0).toString(); 090 sqlText = sqlText.replaceAll("%s","PLACEHOLDER"); 091 } 092 } 093 // System.out.println(sqlText); 094 095 this.moduleName = sqlNode.getModuleName(); 096 this.statementName = sqlNode.getModuleName(); 097 this.parameters = sqlNode.getStringValues(); 098 099 break; 100 case dbvsnowflake: 101 if (this.executeType == EExecType.task) { 102 // EXECUTE TASK statement - just store the task name 103 this.moduleName = sqlNode.getModuleName(); 104 } else if (this.executeType == EExecType.from_stage) { 105 // EXECUTE IMMEDIATE FROM @stage/path or FROM 'path' 106 // Nothing to resolve - the source is a file reference 107 } else { 108 // EXECUTE IMMEDIATE statement 109 this.stmtString = sqlNode.getStmtString(); 110 //System.out.println(stmtString.toString()); 111 TSourceToken st = stmtString.getStartToken(); 112 String tokenStr = st.toString(); 113 if (tokenStr.startsWith("$$")){ 114 // Dollar-quoted string 115 TGSqlParser parser = new TGSqlParser(EDbVendor.dbvsnowflake); 116 parser.sqltext = TBaseType.stringBlock((int)st.lineNo - 1,(int)st.columnNo)+ TBaseType.getStringInsideLiteral(tokenStr); 117 int ret = parser.parse(); 118 if (ret == 0){ 119 stmt = parser.sqlstatements.get(0); 120 }else{ 121 for(int j=0;j<parser.getErrorCount();j++){ 122 this.parseerrormessagehandle(parser.getSyntaxErrors().get(j)); 123 } 124 } 125 } else if (tokenStr.startsWith("'") && tokenStr.endsWith("'")){ 126 // Single-quoted string literal - extract SQL and parse it 127 String sqlContent = TBaseType.getStringInsideLiteral(tokenStr); 128 if (sqlContent != null && !sqlContent.isEmpty()){ 129 TGSqlParser parser = new TGSqlParser(EDbVendor.dbvsnowflake); 130 parser.sqltext = TBaseType.stringBlock((int)st.lineNo - 1,(int)st.columnNo + 1) + sqlContent; 131 int ret = parser.parse(); 132 if (ret == 0 && parser.sqlstatements.size() > 0){ 133 stmt = parser.sqlstatements.get(0); 134 } 135 // Silently ignore parse errors for dynamic SQL - it may contain placeholders 136 } 137 } 138 // Variable references (e.g., :SQLStmt) are resolved lazily in getStmt() 139 } 140 break; 141 case dbvnetezza: 142 this.moduleName = sqlNode.getModuleName(); 143 this.statementName = sqlNode.getModuleName(); 144 this.parameters = sqlNode.getStringValues(); 145 preparedValue = sqlNode.getPreparedValue(); 146 if (preparedValue != null){ 147 preparedValue.doParse(this, ESqlClause.unknown); 148 } 149 150 switch (executeType){ 151 case expr: 152 if (sqlNode.getPreparedValue().getExpressionType() == EExpressionType.function_t){ 153 TFunctionCall functionCall = sqlNode.getPreparedValue().getFunctionCall(); 154 this.moduleName = functionCall.getFunctionName(); 155 this.parameters = functionCall.getArgs(); 156 this.executeType = EExecType.module; 157 } 158 break; 159 default: 160 break; 161 } 162 163 break; 164 case dbvredshift: 165 this.moduleName = sqlNode.getModuleName(); 166 this.statementName = sqlNode.getModuleName(); 167 this.parameters = sqlNode.getStringValues(); 168 preparedValue = sqlNode.getPreparedValue(); 169 if (preparedValue != null){ 170 preparedValue.doParse(this, ESqlClause.unknown); 171 172 if (this.preparedValue.getExpressionType() == EExpressionType.simple_object_name_t){ 173 TObjectName var = this.preparedValue.getObjectOperand(); 174 // if (var.getDbObjectType() == EDbObjectType.variable){ 175 TVariable symbolVariable = TSymbolTableManager.searchSymbolVariable(this.getFrameStack(),var.toString()); 176 if (symbolVariable != null){ 177 this.sqlText = symbolVariable.getVariableStr(); 178 //System.out.println(this.sqlText); 179 } 180 // } 181 } 182 183 } 184 break; 185 default: 186 this.moduleName = sqlNode.getModuleName(); 187 this.statementName = sqlNode.getModuleName(); 188 this.parameters = sqlNode.getStringValues(); 189 preparedValue = sqlNode.getPreparedValue(); 190 if (preparedValue != null){ 191 preparedValue.doParse(this, ESqlClause.unknown); 192 } 193 194 // available in TMssqlExecute currently, maybe will be moved to here in later version 195 //this.stringValues = sqlNode.getStringValues(); 196 //this.linkServerName = sqlNode.getLinkServerName(); 197 198 break; 199 } 200 return 0; 201 } 202 203 static TGSqlParser sqlparser = null; 204 static { 205 // Todo: hardcode to redshift, need to change according to dbvendor 206 sqlparser = new TGSqlParser(EDbVendor.dbvredshift); 207 } 208 209 private TStatementList dynamicStatements = null; 210 211 /** 212 * 213 * @return sql statement instance that generated dynamically based on {@link #sqlText} 214 */ 215 public synchronized TStatementList getDynamicStatements() { 216 if (this.dynamicStatements != null) return this.dynamicStatements; 217 if (this.sqlText == null) return null; 218 219 220 String query = this.sqlText; 221 if((this.preparedValue != null) && (this.preparedValue.getPlainTextLineNo() != -1)){ 222 long lineNo = this.preparedValue.getPlainTextLineNo(); 223 long columnNo = this.preparedValue.getPlainTextColumnNo(); 224 query = TBaseType.stringBlock((int)lineNo - 1,(int)columnNo)+ this.sqlText; 225 } 226 227 228 sqlparser.sqltext = query; 229 int ret = sqlparser.parse(); 230 231 if ( ret != 0){ 232 for(int j=0;j<sqlparser.getErrorCount();j++){ 233 this.parseerrormessagehandle(sqlparser.getSyntaxErrors().get(j)); 234 } 235 236 return null; 237 } 238 239 this.dynamicStatements = new TStatementList(); 240 for(int i=0;i<sqlparser.sqlstatements.size();i++){ 241 if (this.getParentStmt() == null){ 242 sqlparser.sqlstatements.get(i).setParentStmt(this); 243 }else{ 244 sqlparser.sqlstatements.get(i).setParentStmt(this.getParentStmt()); 245 } 246 247 this.dynamicStatements.add(sqlparser.sqlstatements.get(i)); 248 } 249 return dynamicStatements; 250 } 251 252 public TObjectName getLinkServerName() { 253 return linkServerName; 254 } 255 256 private TObjectName linkServerName; 257 private TExpressionList stringValues = null; 258 259 public TExpressionList getStringValues() { 260 return stringValues; 261 } 262 263 private TObjectName moduleName; 264 265 public TObjectName getModuleName() { 266 return moduleName; 267 } 268 269 private TExpression stmtString; 270 private TObjectName intoVariable; 271 private TExpressionList usingVariables; 272 273 private TObjectName statementName; 274 private TExpressionList parameters; 275 private TExpression preparedValue;//couchbase 276 277 public void setSqlText(String sqlText) { 278 this.sqlText = sqlText; 279 } 280 281 private String sqlText = null; 282 283 public String getSqlText() { 284 return sqlText; 285 } 286 287 public void init(Object arg1){ 288 stmtString = (TExpression)arg1; 289 sqlText = stmtString.toString(); 290 switch (stmtString.getExpressionType()){ 291 case simple_object_name_t: 292 moduleName = stmtString.getObjectOperand(); 293 //moduleName.setObjectType(TObjectName.ttobjProcedureName); 294 moduleName.setDbObjectType(EDbObjectType.procedure); 295 break; 296 case function_t: 297 if (stmtString.toString().startsWith("format")){ 298 //postgresql format function 299 TFunctionCall functionCall = stmtString.getFunctionCall(); 300 sqlText = functionCall.getArgs().getExpression(0).toString(); 301 sqlText = sqlText.replaceAll("%s","PLACEHOLDER"); 302 } 303 break; 304 } 305 306 } 307 public void init(Object arg1,Object arg2){ 308 init(arg1); 309 intoVariable = (TObjectName)arg2; 310 311 } 312 313 public TObjectName getIntoVariable() { 314 return intoVariable; 315 } 316 317 public TExpression getStmtString() { 318 return stmtString; 319 } 320 321 public TExpressionList getUsingVariables() { 322 return usingVariables; 323 } 324 325 public void init(Object arg1,Object arg2, Object arg3){ 326 init(arg1,arg2); 327 usingVariables = (TExpressionList)arg3; 328 329 } 330 331 public void accept(TParseTreeVisitor v){ 332 v.preVisit(this); 333 v.postVisit(this); 334 } 335 336 public void acceptChildren(TParseTreeVisitor v){ 337 v.preVisit(this); 338 v.postVisit(this); 339 } 340 341 public void setStmtString(TExpression stmtString) { 342 this.stmtString = stmtString; 343 } 344 345 public void setIntoVariable(TObjectName intoVariable) { 346 this.intoVariable = intoVariable; 347 } 348 349 public void setUsingVariables(TExpressionList usingVariables) { 350 this.usingVariables = usingVariables; 351 } 352 353 public TObjectName getStatementName() { 354 return statementName; 355 } 356 public TExpressionList getParameters() { 357 return parameters; 358 } 359 public void setStatementName(TObjectName statementName) { 360 this.statementName = statementName; 361 } 362 public void setParameters(TExpressionList parameters) { 363 this.parameters = parameters; 364 } 365 public TExpression getPreparedValue() { 366 return preparedValue; 367 } 368 369 /** 370 * Search for a variable assignment in the parent block statements. 371 * Used by Snowflake EXECUTE IMMEDIATE to resolve variable references. 372 * 373 * @param varName the variable name to search for 374 * @return the SQL string assigned to the variable, or null if not found 375 */ 376 private String findVariableAssignmentValue(String varName) { 377 TCustomSqlStatement parent = this.getParentStmt(); 378 // Debug output 379 // System.out.println("[DEBUG] findVariableAssignmentValue: varName=" + varName + ", parent=" + (parent != null ? parent.getClass().getSimpleName() : "null")); 380 if (parent == null) return null; 381 382 TStatementList bodyStmts = null; 383 384 // Get body statements from parent - check multiple levels 385 TCustomSqlStatement current = parent; 386 while (current != null && bodyStmts == null) { 387 if (current instanceof TBlockSqlStatement) { 388 bodyStmts = ((TBlockSqlStatement) current).getBodyStatements(); 389 } else if (current instanceof TCreateProcedureStmt) { 390 bodyStmts = ((TCreateProcedureStmt) current).getBodyStatements(); 391 } 392 if (bodyStmts == null || bodyStmts.size() == 0) { 393 bodyStmts = null; 394 current = current.getParentStmt(); 395 } 396 } 397 398 // System.out.println("[DEBUG] bodyStmts=" + (bodyStmts != null ? bodyStmts.size() : "null")); 399 400 if (bodyStmts == null) return null; 401 402 // Search backwards through statements to find the most recent assignment 403 for (int i = bodyStmts.size() - 1; i >= 0; i--) { 404 TCustomSqlStatement bodyStmt = bodyStmts.get(i); 405 // System.out.println("[DEBUG] bodyStmt[" + i + "]=" + bodyStmt.getClass().getSimpleName()); 406 if (bodyStmt instanceof TAssignStmt) { 407 TAssignStmt assignStmt = (TAssignStmt) bodyStmt; 408 TExpression leftExpr = assignStmt.getLeft(); 409 if (leftExpr != null) { 410 String leftName = leftExpr.toString(); 411 // Handle colon prefix if present 412 if (leftName.startsWith(":")) { 413 leftName = leftName.substring(1); 414 } 415 // System.out.println("[DEBUG] checking assignment: leftName=" + leftName + ", varName=" + varName); 416 if (leftName.equalsIgnoreCase(varName)) { 417 // Found the assignment - extract the SQL content 418 TExpression rightExpr = assignStmt.getExpression(); 419 if (rightExpr != null) { 420 String value = rightExpr.toString(); 421 // System.out.println("[DEBUG] found assignment: value=" + value); 422 // If it's a string literal, extract the content 423 if (value.startsWith("'") && value.endsWith("'")) { 424 return TBaseType.getStringInsideLiteral(value); 425 } else if (value.startsWith("$$") && value.endsWith("$$")) { 426 return TBaseType.getStringInsideLiteral(value); 427 } 428 return value; 429 } 430 } 431 } 432 } 433 } 434 return null; 435 } 436 437}