Add "Describe" command, with support for describing both (a) specific table(s) and...
[squelch.git] / src / main / java / net / jaekl / squelch / Squelch.java
1 package net.jaekl.squelch;
2
3 import java.io.IOException;
4 import java.io.PrintWriter;
5 import java.sql.Connection;
6 import java.sql.SQLException;
7 import java.util.Locale;
8
9 import net.jaekl.squelch.db.DbDriver;
10 import net.jaekl.squelch.db.MsSqlDriver;
11 import net.jaekl.squelch.db.MySqlDriver;
12 import net.jaekl.squelch.db.OracleDriver;
13 import net.jaekl.squelch.db.PostgresqlDriver;
14 import net.jaekl.squelch.stmt.Describe;
15 import net.jaekl.squelch.stmt.Select;
16 import net.jaekl.squelch.stmt.Stmt;
17 import net.jaekl.squelch.util.ConsoleInput;
18 import net.jaekl.squelch.util.ConsoleInputImpl;
19 import net.jaekl.squelch.util.ConsoleUtil;
20
21 public class Squelch {
22         static final String PROMPT = "> ";
23         private static final DbDriver[] DB_DRIVERS = {
24                 new MsSqlDriver(),
25                 new MySqlDriver(),
26                 new OracleDriver(),
27                 new PostgresqlDriver()
28         };
29         private static final Stmt[] READ_ONLY_STATEMENTS = {
30                 new Describe(),
31                 new Select()
32         };
33         
34         private Args m_args;
35         private Stmt[] m_statements;
36
37         public Squelch() {
38                 m_args = new Args(this.getClass().getName());
39                 m_statements = READ_ONLY_STATEMENTS;
40         }
41         
42         public void doMain(String[] params)
43         {
44                 try (PrintWriter pw = new PrintWriter(System.out))
45                 {
46                         if (!m_args.parseArgs(pw, params)) {
47                                 return;
48                         }
49                         
50                         ConsoleInputImpl ci = ConsoleUtil.getInst().getInput();
51                         pumpLines(pw, ci);                      
52                 } catch (IOException | ClassNotFoundException | SQLException | SquelchException e) {
53                         e.printStackTrace();
54                 }
55         }
56         
57         public static void main(String[] args) {
58                 new Squelch().doMain(args);
59         }
60         
61         boolean isQuit(String line) 
62         {
63                 if ((null == line)) {
64                         return true;
65                 }
66                 String trimmed = line.trim();
67                 if (trimmed.endsWith(";")) {
68                         trimmed = trimmed.substring(0, trimmed.length() - 1).trim();
69                 }
70                 String upperCased = trimmed.toUpperCase(Locale.CANADA);
71                 
72                 if (  "EXIT".equals(upperCased)
73                    || "QUIT".equals(upperCased)
74                    || "\\q".equals(trimmed) ) 
75                 {
76                         return true;
77                 }
78                 return false;
79         }
80         
81         Connection getConnection(DbDriver driver, String jdbcUrl) 
82                 throws ClassNotFoundException, SQLException, SquelchException 
83         {
84                 return driver.connect(jdbcUrl, m_args.getUser(), m_args.getPass());
85         }
86         
87         DbDriver getDriverFor(String jdbcUrl) throws SquelchException 
88         {
89                 for (DbDriver driver : DB_DRIVERS) {
90                         if (driver.handles(jdbcUrl)) {
91                                 return driver;
92                         }
93                 }
94                 throw new SquelchException("Cannot determine DB Driver for JDBC URL:  \"" + jdbcUrl + "\".");
95         }
96         
97         void pumpLines(PrintWriter pw, ConsoleInput ci) throws IOException, ClassNotFoundException, SQLException, SquelchException 
98         {
99                 String line = null;
100                 String jdbcUrl = m_args.getUrl();
101                 DbDriver driver = getDriverFor(jdbcUrl);
102
103                 try (Connection conn = getConnection(driver, jdbcUrl))
104                 {
105                         while (true) {
106                                 boolean processed = false;
107                                 line = ci.readLine(PROMPT);
108                                 
109                                 for (Stmt statement : m_statements) {
110                                         if (statement.handles(line)) {
111                                                 try {
112                                                         statement.exec(driver, conn, pw, line);
113                                                 }
114                                                 catch (SQLException exc) {
115                                                         exc.printStackTrace(pw);
116                                                 }
117                                                 processed = true;
118                                                 break;
119                                         }
120                                 }
121                                 
122                                 if ((!processed)) {
123                                         if (isQuit(line)) {
124                                                 break;
125                                         }
126                                         // Unrecognized command
127                                         // TODO:  add a string table, and a natural-language error message.
128                                         pw.println("??? \"" + line + "\"");
129                                 }
130                                 pw.flush();
131                         }
132                 }
133         }       
134 }