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}