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