001package gudusoft.gsqlparser.stmt.oracle; 002 003import gudusoft.gsqlparser.TCustomSqlStatement; 004import gudusoft.gsqlparser.*; 005import gudusoft.gsqlparser.nodes.*; 006 007 008import java.util.HashMap; 009import java.util.List; 010import java.util.ArrayList; 011 012 013public class TSqlplusCmdStatement extends TCustomSqlStatement { 014 015 static HashMap <String, ESqlPlusCmd> sqlpluskeywordList; 016 017 static { 018 sqlpluskeywordList = new HashMap(); 019 sqlpluskeywordList.put("ACC", ESqlPlusCmd.spcAccept); 020 sqlpluskeywordList.put("ACCEPT",ESqlPlusCmd.spcAccept); 021 sqlpluskeywordList.put("A",ESqlPlusCmd.spcAppend); 022 sqlpluskeywordList.put("APPEND",ESqlPlusCmd.spcAppend); 023 sqlpluskeywordList.put("ATTRIBUTE",ESqlPlusCmd.spcAttribute); 024 sqlpluskeywordList.put("BRE",ESqlPlusCmd.spcBreak); 025 sqlpluskeywordList.put("BREAK",ESqlPlusCmd.spcBreak); 026 sqlpluskeywordList.put("BTI",ESqlPlusCmd.spcBTitle); 027 sqlpluskeywordList.put("BTITLE",ESqlPlusCmd.spcBTitle); 028 sqlpluskeywordList.put("C",ESqlPlusCmd.spcChange); 029 sqlpluskeywordList.put("CHANGE",ESqlPlusCmd.spcChange); 030 sqlpluskeywordList.put("CL",ESqlPlusCmd.spcClear); 031 sqlpluskeywordList.put("CLEAR",ESqlPlusCmd.spcClear); 032 sqlpluskeywordList.put("COL",ESqlPlusCmd.spcColumn); 033 sqlpluskeywordList.put("COLUMN",ESqlPlusCmd.spcColumn); 034 sqlpluskeywordList.put("COMP",ESqlPlusCmd.spcCompute); 035 sqlpluskeywordList.put("COMPUTE",ESqlPlusCmd.spcCompute); 036 sqlpluskeywordList.put("CONN",ESqlPlusCmd.spcConnect); 037 sqlpluskeywordList.put("CONNECT",ESqlPlusCmd.spcConnect); 038 sqlpluskeywordList.put("COPY",ESqlPlusCmd.spcCopy); 039 sqlpluskeywordList.put("DEF",ESqlPlusCmd.spcDefine); 040 sqlpluskeywordList.put("DEFINE",ESqlPlusCmd.spcDefine); 041 sqlpluskeywordList.put("DEL",ESqlPlusCmd.spcDel); 042 sqlpluskeywordList.put("DESC",ESqlPlusCmd.spcDescribe); 043 sqlpluskeywordList.put("DESCRIBE",ESqlPlusCmd.spcDescribe); 044 sqlpluskeywordList.put("DISC",ESqlPlusCmd.spcDisconnect); 045 sqlpluskeywordList.put("DISCONNECT",ESqlPlusCmd.spcDisconnect); 046 sqlpluskeywordList.put("ED",ESqlPlusCmd.spcEdit); 047 sqlpluskeywordList.put("EDIT",ESqlPlusCmd.spcEdit); 048 sqlpluskeywordList.put("EXEC",ESqlPlusCmd.spcExecute); 049 // sqlpluskeywordList.put("EXECUTE",ESqlPlusCmd.spcExecute); 050 sqlpluskeywordList.put("EXIT",ESqlPlusCmd.spcExit); 051 sqlpluskeywordList.put("QUIT",ESqlPlusCmd.spcQuit); 052 sqlpluskeywordList.put("GET",ESqlPlusCmd.spcGet); 053 sqlpluskeywordList.put("HELP",ESqlPlusCmd.spcHelp); 054 sqlpluskeywordList.put("HO",ESqlPlusCmd.spcHost); 055 sqlpluskeywordList.put("HOST",ESqlPlusCmd.spcHost); 056 sqlpluskeywordList.put("I",ESqlPlusCmd.spcInput); 057 sqlpluskeywordList.put("INPUT",ESqlPlusCmd.spcInput); 058 sqlpluskeywordList.put("L",ESqlPlusCmd.spcList); 059 sqlpluskeywordList.put("LIST",ESqlPlusCmd.spcList); 060 sqlpluskeywordList.put("PASSW",ESqlPlusCmd.spcPassword); 061 sqlpluskeywordList.put("PASSWORD",ESqlPlusCmd.spcPassword); 062 sqlpluskeywordList.put("PAU",ESqlPlusCmd.spcPause); 063 sqlpluskeywordList.put("PAUSE",ESqlPlusCmd.spcPause); 064 sqlpluskeywordList.put("PRI",ESqlPlusCmd.spcPrint); 065 sqlpluskeywordList.put("PRINT",ESqlPlusCmd.spcPrint); 066 sqlpluskeywordList.put("PROMPT",ESqlPlusCmd.spcPrompt); 067 sqlpluskeywordList.put("REM",ESqlPlusCmd.spcRemark); 068 sqlpluskeywordList.put("REMARK",ESqlPlusCmd.spcRemark); 069 sqlpluskeywordList.put("REPF",ESqlPlusCmd.spcRepFooter); 070 sqlpluskeywordList.put("REPFOOTER",ESqlPlusCmd.spcRepFooter); 071 sqlpluskeywordList.put("REPH",ESqlPlusCmd.spcRepHeader); 072 sqlpluskeywordList.put("REPHEADER",ESqlPlusCmd.spcRepHeader); 073 sqlpluskeywordList.put("R",ESqlPlusCmd.spcRun); 074 sqlpluskeywordList.put("RUN",ESqlPlusCmd.spcRun); 075 sqlpluskeywordList.put("SAV",ESqlPlusCmd.spcSave); 076 sqlpluskeywordList.put("SAVE",ESqlPlusCmd.spcSave); 077 sqlpluskeywordList.put("SET",ESqlPlusCmd.spcSet); 078 sqlpluskeywordList.put("SHO",ESqlPlusCmd.spcShow); 079 sqlpluskeywordList.put("SHOW",ESqlPlusCmd.spcShow); 080 sqlpluskeywordList.put("SPO",ESqlPlusCmd.spcSpool); 081 sqlpluskeywordList.put("SPOOL",ESqlPlusCmd.spcSpool); 082 sqlpluskeywordList.put("STA",ESqlPlusCmd.spcStart); 083 sqlpluskeywordList.put("START",ESqlPlusCmd.spcStart); 084 sqlpluskeywordList.put("STORE",ESqlPlusCmd.spcStore); 085 sqlpluskeywordList.put("TIMI",ESqlPlusCmd.spcTiming); 086 sqlpluskeywordList.put("TIMING",ESqlPlusCmd.spcTiming); 087 sqlpluskeywordList.put("TTI",ESqlPlusCmd.spcTTitle); 088 sqlpluskeywordList.put("TTITLE",ESqlPlusCmd.spcTTitle); 089 sqlpluskeywordList.put("UNDEF",ESqlPlusCmd.spcUndefine); 090 sqlpluskeywordList.put("UNDEFINE",ESqlPlusCmd.spcUndefine); 091 sqlpluskeywordList.put("VAR",ESqlPlusCmd.spcVariable); 092 sqlpluskeywordList.put("VARIABLE",ESqlPlusCmd.spcVariable); 093 sqlpluskeywordList.put("WHENEVER",ESqlPlusCmd.spcWheneverOSError); 094 sqlpluskeywordList.put("@",ESqlPlusCmd.spcAtSign); 095 sqlpluskeywordList.put("@@",ESqlPlusCmd.spcDoubleAtSign); 096 } 097 098 public static ESqlPlusCmd searchCmd(String cmdStr){ 099 ESqlPlusCmd ret = null; 100 ret = sqlpluskeywordList.get(cmdStr.toUpperCase()); 101 if (ret == null){ 102 ret = ESqlPlusCmd.spcUnknown; 103 } 104 return ret; 105 } 106 107 public static ESqlPlusCmd searchCmd(String cmdStr, TSourceToken nextToken){ 108 109 ESqlPlusCmd ret = searchCmd(cmdStr); 110 if (ret != null) 111 { 112 switch (ret){ 113 case spcChange: 114 if (nextToken == null){ 115 ret = ESqlPlusCmd.spcUnknown; 116 }else if (!((nextToken.tokentype == ETokenType.ttwhitespace )||(nextToken.tokentype == ETokenType.ttreturn ))){ 117 ret = ESqlPlusCmd.spcUnknown; 118 } 119 break; 120 case spcHost: 121 if (nextToken == null){ 122 ret = ESqlPlusCmd.spcUnknown; 123 }else if (!((nextToken.tokentype == ETokenType.ttwhitespace )||(nextToken.tokentype == ETokenType.ttreturn ))){ 124 ret = ESqlPlusCmd.spcUnknown; 125 } 126 break; 127// case spcAppend: 128// if ((nextToken != null)&&(nextToken.tokencode == '.')) { 129// ret = ESqlPlusCmd.spcUnknown; 130// } 131// break; 132 } 133 }else { 134 ret = ESqlPlusCmd.spcUnknown; 135 } 136 return ret; 137 138 } 139 140 141 private ESqlPlusCmd commandType = ESqlPlusCmd.spcUnknown; 142 public ESqlPlusCmd getCommandType() { 143 if (commandType == ESqlPlusCmd.spcUnknown) { 144 commandType = searchCmd(getStartToken().toString()); 145 } 146 return commandType; 147 } 148 149 public TSqlplusCmdStatement(EDbVendor dbvendor) { 150 super(dbvendor); 151 sqlstatementtype = ESqlStatementType.sstsqlpluscmd; 152 } 153 154 void buildsql() { 155 } 156 157 void clear() { 158 } 159 160 String getasprettytext() { 161 return ""; 162 } 163 164 void iterate(TVisitorAbs pvisitor) { 165 } 166 167 // Add fields to store command parameters 168 private String commandText; // The full text after the command keyword 169 private String[] commandParameters; // Parsed parameters 170 private TObjectName procedureName; // For EXEC/EXECUTE commands 171 private TExpression procedureArguments; // For procedure arguments if any 172 173 // Method to get the procedure name for EXEC commands 174 public TObjectName getProcedureName() { 175 if (getCommandType() == ESqlPlusCmd.spcExecute && procedureName != null) { 176 return procedureName; 177 } 178 return null; 179 } 180 181 // Method to get procedure arguments for EXEC commands 182 public TExpression getProcedureArguments() { 183 if (getCommandType() == ESqlPlusCmd.spcExecute && procedureArguments != null) { 184 return procedureArguments; 185 } 186 return null; 187 } 188 189 // Method to get raw command text (everything after the command keyword) 190 public String getCommandText() { 191 return commandText; 192 } 193 194 // Method to get all parsed parameters as an array 195 public String[] getCommandParameters() { 196 return commandParameters; 197 } 198 199 // Pre-compiled regex patterns to avoid repeated compilation 200 private static final java.util.regex.Pattern WHITESPACE_PATTERN = java.util.regex.Pattern.compile("\\s+"); 201 private static final java.util.regex.Pattern COMMA_PATTERN = java.util.regex.Pattern.compile("\\s*,\\s*"); 202 203 // Override the existing parse method to extract command parameters 204 @Override 205 protected int dochecksyntax(TCustomSqlStatement psql){ 206 // Basic parsing is already done by the parser 207 // Now extract the command text and parameters 208 extractCommandText(); 209 210 // Based on command type, do specific parsing 211 switch(getCommandType()) { 212 case spcExecute: 213 parseExecuteCommand(); 214 break; 215 // Handle other command types as needed 216 case spcDefine: 217 parseDefineCommand(); 218 break; 219 // etc. 220 } 221 222 isparsed = true; 223 return 0; //not check syntax of sqlplus cmd ,always ok 224 } 225 226 227 228 // Extract the full text after the command keyword 229 private void extractCommandText() { 230 TSourceToken startToken = getStartToken(); 231 if (startToken == null) return; 232 233 TSourceToken endToken = getEndToken(); 234 235 // The first token should be the command (EXEC, DEFINE, etc.) 236 TSourceToken commandToken = startToken; 237 238 // Skip any leading whitespace to find the actual command 239 while (commandToken != null && commandToken != endToken && 240 (commandToken.tokentype == ETokenType.ttwhitespace || 241 commandToken.tokentype == ETokenType.ttreturn)) { 242 commandToken = commandToken.nextToken(); 243 } 244 245 if (commandToken == null || commandToken == endToken) return; 246 247 // Store the command type based on the first token 248 commandType = searchCmd(commandToken.toString()); 249 250 // If start and end tokens are adjacent (e.g., "/" separator), no text to extract 251 if (commandToken == endToken) return; 252 253 // Now get the first token after the command 254 TSourceToken currentToken = commandToken.nextToken(); 255 256 // Skip whitespace after the command, but stop at the end token 257 while (currentToken != null && currentToken != endToken && 258 (currentToken.tokentype == ETokenType.ttwhitespace || 259 currentToken.tokentype == ETokenType.ttreturn)) { 260 currentToken = currentToken.nextToken(); 261 } 262 263 if (currentToken == null || currentToken == endToken) return; 264 265 // Start collecting the command text (everything after the command keyword) 266 StringBuilder sb = new StringBuilder(); 267 while (currentToken != null && currentToken != endToken) { 268 sb.append(currentToken.getAstext()); 269 270 // Check if this is a whitespace token 271 if (currentToken.tokentype == ETokenType.ttwhitespace || 272 currentToken.tokentype == ETokenType.ttreturn) { 273 // Add a single space for readability 274 sb.append(" "); 275 } 276 277 currentToken = currentToken.nextToken(); 278 } 279 280 // Store the raw command text 281 commandText = sb.toString().trim(); 282 283 // Parse parameters based on command type 284 parseParameters(); 285 } 286 287 // Parse parameters based on command type 288 private void parseParameters() { 289 if (commandText == null || commandText.isEmpty()) { 290 commandParameters = new String[0]; 291 return; 292 } 293 294 // Different parameter parsing strategies based on command type 295 switch(getCommandType()) { 296 case spcExecute: 297 // For EXEC commands, handle parameters differently if we have parentheses 298 if (commandText.contains("(") && commandText.contains(")")) { 299 int openParenPos = commandText.indexOf('('); 300 // Get procedure name (everything before the opening parenthesis) 301 String procNamePart = commandText.substring(0, openParenPos).trim(); 302 303 // Remove any semicolon at the end 304 if (procNamePart.endsWith(";")) { 305 procNamePart = procNamePart.substring(0, procNamePart.length() - 1).trim(); 306 } 307 308 // Extract arguments string 309 String argsStr = commandText.substring( 310 openParenPos + 1, 311 commandText.lastIndexOf(')') 312 ).trim(); 313 314 // Use pre-compiled pattern and handle nested structures 315 String[] args = parseArgumentList(argsStr); 316 317 // Create the parameters array with procedure name as first element 318 commandParameters = new String[args.length + 1]; 319 commandParameters[0] = procNamePart; 320 System.arraycopy(args, 0, commandParameters, 1, args.length); 321 } else { 322 // Simple case: EXEC PROC_A; 323 // Remove any semicolon at the end 324 String cmdText = commandText; 325 if (cmdText.endsWith(";")) { 326 cmdText = cmdText.substring(0, cmdText.length() - 1).trim(); 327 } 328 329 // Use pre-compiled pattern 330 commandParameters = WHITESPACE_PATTERN.split(cmdText); 331 } 332 break; 333 334 case spcDefine: 335 case spcSet: 336 // For commands like DEFINE and SET that use name=value syntax 337 if (commandText.contains("=")) { 338 // Manually split by first equals sign - faster than regex 339 int equalsPos = commandText.indexOf('='); 340 if (equalsPos > 0) { 341 commandParameters = new String[] { 342 commandText.substring(0, equalsPos).trim(), 343 commandText.substring(equalsPos + 1).trim() 344 }; 345 } else { 346 commandParameters = new String[] { commandText }; 347 } 348 } else { 349 commandParameters = WHITESPACE_PATTERN.split(commandText); 350 } 351 break; 352 353 default: 354 // Default simple parameter splitting for other commands 355 // Remove any semicolon at the end 356 String cmdText = commandText; 357 if (cmdText.endsWith(";")) { 358 cmdText = cmdText.substring(0, cmdText.length() - 1).trim(); 359 } 360 361 // Use pre-compiled pattern 362 commandParameters = WHITESPACE_PATTERN.split(cmdText); 363 break; 364 } 365 } 366 367 /** 368 * Parse an argument list with awareness of quotes and nested parentheses. 369 * This correctly handles cases like: 'arg1, arg2', "arg3", func(a, b) 370 * 371 * @param argStr String containing comma-separated arguments 372 * @return Array of parsed arguments 373 */ 374 private String[] parseArgumentList(String argStr) { 375 if (argStr == null || argStr.isEmpty()) { 376 return new String[0]; 377 } 378 379 // For simple cases with no quotes or nested structures, use the pattern directly 380 if (!argStr.contains("'") && !argStr.contains("\"") && !argStr.contains("(")) { 381 return COMMA_PATTERN.split(argStr); 382 } 383 384 // For complex cases, use character-by-character parsing 385 List<String> args = new ArrayList<>(); 386 StringBuilder currentArg = new StringBuilder(argStr.length() / 4); 387 boolean inSingleQuote = false; 388 boolean inDoubleQuote = false; 389 int nestedParenCount = 0; 390 391 for (int i = 0; i < argStr.length(); i++) { 392 char c = argStr.charAt(i); 393 394 // Handle quotes 395 if (c == '\'' && !inDoubleQuote) { 396 inSingleQuote = !inSingleQuote; 397 currentArg.append(c); 398 } 399 else if (c == '"' && !inSingleQuote) { 400 inDoubleQuote = !inDoubleQuote; 401 currentArg.append(c); 402 } 403 // Handle nested parentheses 404 else if (c == '(' && !inSingleQuote && !inDoubleQuote) { 405 nestedParenCount++; 406 currentArg.append(c); 407 } 408 else if (c == ')' && !inSingleQuote && !inDoubleQuote) { 409 nestedParenCount--; 410 currentArg.append(c); 411 } 412 // Handle commas - only split if not inside quotes or parentheses 413 else if (c == ',' && !inSingleQuote && !inDoubleQuote && nestedParenCount == 0) { 414 args.add(currentArg.toString().trim()); 415 currentArg.setLength(0); // Reset for next argument 416 } 417 else { 418 currentArg.append(c); 419 } 420 } 421 422 // Add the last argument if not empty 423 String lastArg = currentArg.toString().trim(); 424 if (!lastArg.isEmpty()) { 425 args.add(lastArg); 426 } 427 428 return args.toArray(new String[0]); 429 } 430 431 // Parse EXEC/EXECUTE commands specifically 432 private void parseExecuteCommand() { 433 if (commandText == null) return; 434 435 // For EXEC commands, the first parameter is typically the procedure name 436 // Handle both simple procedure calls and those with parameters 437 438 // Simple case: EXEC PROC_A 439 if (commandParameters.length > 0) { 440 String procNameStr = commandParameters[0]; 441 442 // Handle case where there might be parameters or semicolon 443 if (procNameStr.endsWith(";")) { 444 procNameStr = procNameStr.substring(0, procNameStr.length() - 1); 445 } 446 447 // Create a TObjectName for the procedure 448 procedureName = TObjectName.createObjectName(EDbVendor.dbvoracle, EDbObjectType.procedure, new TSourceToken(procNameStr)); 449 450 451 // If there are parameters, parse them (more complex implementation needed) 452 if (commandText.contains("(") && commandText.contains(")")) { 453 // Extract and parse arguments 454 // This would need a more sophisticated parser for complex arguments 455 String argsStr = commandText.substring( 456 commandText.indexOf('(') + 1, 457 commandText.lastIndexOf(')') 458 ); 459 460 // Create expression for arguments (simplified) 461 // In a real implementation, you would parse this properly 462 procedureArguments = new TExpression(); 463 // procedureArguments.setStringValue(argsStr); 464 } 465 } 466 } 467 468 // Parse other command types as needed 469 private void parseDefineCommand() { 470 // Implementation for DEFINE command 471 } 472 473 // Additional helper methods as needed 474 475 public void accept(TParseTreeVisitor v){ 476 v.preVisit(this); 477 v.postVisit(this); 478 } 479 480 public void acceptChildren(TParseTreeVisitor v){ 481 v.preVisit(this); 482 v.postVisit(this); 483 } 484 485}