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 // The first token should be the command (EXEC, DEFINE, etc.) 234 TSourceToken commandToken = startToken; 235 236 // Skip any leading whitespace to find the actual command 237 while (commandToken != null && 238 (commandToken.tokentype == ETokenType.ttwhitespace || 239 commandToken.tokentype == ETokenType.ttreturn)) { 240 commandToken = commandToken.nextToken(); 241 } 242 243 if (commandToken == null) return; 244 245 // Store the command type based on the first token 246 commandType = searchCmd(commandToken.toString()); 247 248 // Now get the first token after the command 249 TSourceToken currentToken = commandToken.nextToken(); 250 251 // Skip whitespace after the command 252 while (currentToken != null && 253 (currentToken.tokentype == ETokenType.ttwhitespace || 254 currentToken.tokentype == ETokenType.ttreturn)) { 255 currentToken = currentToken.nextToken(); 256 } 257 258 // Start collecting the command text (everything after the command keyword) 259 StringBuilder sb = new StringBuilder(); 260 while (currentToken != null && currentToken != getEndToken()) { 261 sb.append(currentToken.astext); 262 263 // Check if this is a whitespace token 264 if (currentToken.tokentype == ETokenType.ttwhitespace || 265 currentToken.tokentype == ETokenType.ttreturn) { 266 // Add a single space for readability 267 sb.append(" "); 268 } 269 270 currentToken = currentToken.nextToken(); 271 } 272 273 // Store the raw command text 274 commandText = sb.toString().trim(); 275 276 // Parse parameters based on command type 277 parseParameters(); 278 } 279 280 // Parse parameters based on command type 281 private void parseParameters() { 282 if (commandText == null || commandText.isEmpty()) { 283 commandParameters = new String[0]; 284 return; 285 } 286 287 // Different parameter parsing strategies based on command type 288 switch(getCommandType()) { 289 case spcExecute: 290 // For EXEC commands, handle parameters differently if we have parentheses 291 if (commandText.contains("(") && commandText.contains(")")) { 292 int openParenPos = commandText.indexOf('('); 293 // Get procedure name (everything before the opening parenthesis) 294 String procNamePart = commandText.substring(0, openParenPos).trim(); 295 296 // Remove any semicolon at the end 297 if (procNamePart.endsWith(";")) { 298 procNamePart = procNamePart.substring(0, procNamePart.length() - 1).trim(); 299 } 300 301 // Extract arguments string 302 String argsStr = commandText.substring( 303 openParenPos + 1, 304 commandText.lastIndexOf(')') 305 ).trim(); 306 307 // Use pre-compiled pattern and handle nested structures 308 String[] args = parseArgumentList(argsStr); 309 310 // Create the parameters array with procedure name as first element 311 commandParameters = new String[args.length + 1]; 312 commandParameters[0] = procNamePart; 313 System.arraycopy(args, 0, commandParameters, 1, args.length); 314 } else { 315 // Simple case: EXEC PROC_A; 316 // Remove any semicolon at the end 317 String cmdText = commandText; 318 if (cmdText.endsWith(";")) { 319 cmdText = cmdText.substring(0, cmdText.length() - 1).trim(); 320 } 321 322 // Use pre-compiled pattern 323 commandParameters = WHITESPACE_PATTERN.split(cmdText); 324 } 325 break; 326 327 case spcDefine: 328 case spcSet: 329 // For commands like DEFINE and SET that use name=value syntax 330 if (commandText.contains("=")) { 331 // Manually split by first equals sign - faster than regex 332 int equalsPos = commandText.indexOf('='); 333 if (equalsPos > 0) { 334 commandParameters = new String[] { 335 commandText.substring(0, equalsPos).trim(), 336 commandText.substring(equalsPos + 1).trim() 337 }; 338 } else { 339 commandParameters = new String[] { commandText }; 340 } 341 } else { 342 commandParameters = WHITESPACE_PATTERN.split(commandText); 343 } 344 break; 345 346 default: 347 // Default simple parameter splitting for other commands 348 // Remove any semicolon at the end 349 String cmdText = commandText; 350 if (cmdText.endsWith(";")) { 351 cmdText = cmdText.substring(0, cmdText.length() - 1).trim(); 352 } 353 354 // Use pre-compiled pattern 355 commandParameters = WHITESPACE_PATTERN.split(cmdText); 356 break; 357 } 358 } 359 360 /** 361 * Parse an argument list with awareness of quotes and nested parentheses. 362 * This correctly handles cases like: 'arg1, arg2', "arg3", func(a, b) 363 * 364 * @param argStr String containing comma-separated arguments 365 * @return Array of parsed arguments 366 */ 367 private String[] parseArgumentList(String argStr) { 368 if (argStr == null || argStr.isEmpty()) { 369 return new String[0]; 370 } 371 372 // For simple cases with no quotes or nested structures, use the pattern directly 373 if (!argStr.contains("'") && !argStr.contains("\"") && !argStr.contains("(")) { 374 return COMMA_PATTERN.split(argStr); 375 } 376 377 // For complex cases, use character-by-character parsing 378 List<String> args = new ArrayList<>(); 379 StringBuilder currentArg = new StringBuilder(argStr.length() / 4); 380 boolean inSingleQuote = false; 381 boolean inDoubleQuote = false; 382 int nestedParenCount = 0; 383 384 for (int i = 0; i < argStr.length(); i++) { 385 char c = argStr.charAt(i); 386 387 // Handle quotes 388 if (c == '\'' && !inDoubleQuote) { 389 inSingleQuote = !inSingleQuote; 390 currentArg.append(c); 391 } 392 else if (c == '"' && !inSingleQuote) { 393 inDoubleQuote = !inDoubleQuote; 394 currentArg.append(c); 395 } 396 // Handle nested parentheses 397 else if (c == '(' && !inSingleQuote && !inDoubleQuote) { 398 nestedParenCount++; 399 currentArg.append(c); 400 } 401 else if (c == ')' && !inSingleQuote && !inDoubleQuote) { 402 nestedParenCount--; 403 currentArg.append(c); 404 } 405 // Handle commas - only split if not inside quotes or parentheses 406 else if (c == ',' && !inSingleQuote && !inDoubleQuote && nestedParenCount == 0) { 407 args.add(currentArg.toString().trim()); 408 currentArg.setLength(0); // Reset for next argument 409 } 410 else { 411 currentArg.append(c); 412 } 413 } 414 415 // Add the last argument if not empty 416 String lastArg = currentArg.toString().trim(); 417 if (!lastArg.isEmpty()) { 418 args.add(lastArg); 419 } 420 421 return args.toArray(new String[0]); 422 } 423 424 // Parse EXEC/EXECUTE commands specifically 425 private void parseExecuteCommand() { 426 if (commandText == null) return; 427 428 // For EXEC commands, the first parameter is typically the procedure name 429 // Handle both simple procedure calls and those with parameters 430 431 // Simple case: EXEC PROC_A 432 if (commandParameters.length > 0) { 433 String procNameStr = commandParameters[0]; 434 435 // Handle case where there might be parameters or semicolon 436 if (procNameStr.endsWith(";")) { 437 procNameStr = procNameStr.substring(0, procNameStr.length() - 1); 438 } 439 440 // Create a TObjectName for the procedure 441 procedureName = TObjectName.createObjectName(EDbVendor.dbvoracle, EDbObjectType.procedure, new TSourceToken(procNameStr)); 442 443 444 // If there are parameters, parse them (more complex implementation needed) 445 if (commandText.contains("(") && commandText.contains(")")) { 446 // Extract and parse arguments 447 // This would need a more sophisticated parser for complex arguments 448 String argsStr = commandText.substring( 449 commandText.indexOf('(') + 1, 450 commandText.lastIndexOf(')') 451 ); 452 453 // Create expression for arguments (simplified) 454 // In a real implementation, you would parse this properly 455 procedureArguments = new TExpression(); 456 // procedureArguments.setStringValue(argsStr); 457 } 458 } 459 } 460 461 // Parse other command types as needed 462 private void parseDefineCommand() { 463 // Implementation for DEFINE command 464 } 465 466 // Additional helper methods as needed 467 468 public void accept(TParseTreeVisitor v){ 469 v.preVisit(this); 470 v.postVisit(this); 471 } 472 473 public void acceptChildren(TParseTreeVisitor v){ 474 v.preVisit(this); 475 v.postVisit(this); 476 } 477 478}