adds support for null suppression
[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.PSet;
16 import net.jaekl.squelch.stmt.Select;
17 import net.jaekl.squelch.stmt.Stmt;
18 import net.jaekl.squelch.util.ConsoleInput;
19 import net.jaekl.squelch.util.ConsoleInputImpl;
20 import net.jaekl.squelch.util.ConsoleUtil;
21
22 public class Squelch {
23         static final String PROMPT = "> ";
24         private static final DbDriver[] DB_DRIVERS = {
25                 new MsSqlDriver(),
26                 new MySqlDriver(),
27                 new OracleDriver(),
28                 new PostgresqlDriver()
29         };
30         private static final Stmt[] READ_ONLY_STATEMENTS = {
31                 new Describe(),
32                 new PSet(),
33                 new Select()
34         };
35         
36         private Args m_args;
37         private Stmt[] m_statements;
38
39         public Squelch() {
40                 m_args = new Args(this.getClass().getName());
41                 m_statements = READ_ONLY_STATEMENTS;
42         }
43         
44         public void doMain(String[] params)
45         {
46                 try (PrintWriter pw = new PrintWriter(System.out))
47                 {
48                         if (!m_args.parseArgs(pw, params)) {
49                                 return;
50                         }
51                         
52                         ConsoleInputImpl ci = ConsoleUtil.getInst().getInput();
53                         pumpLines(pw, ci);                      
54                 } catch (IOException | ClassNotFoundException | SQLException | SquelchException e) {
55                         e.printStackTrace();
56                 }
57         }
58         
59         public static void main(String[] args) {
60                 new Squelch().doMain(args);
61         }
62         
63         boolean isQuit(String line) 
64         {
65                 if ((null == line)) {
66                         return true;
67                 }
68                 String trimmed = line.trim();
69                 if (trimmed.endsWith(";")) {
70                         trimmed = trimmed.substring(0, trimmed.length() - 1).trim();
71                 }
72                 String upperCased = trimmed.toUpperCase(Locale.CANADA);
73                 
74                 if (  "EXIT".equals(upperCased)
75                    || "QUIT".equals(upperCased)
76                    || "\\q".equals(trimmed) ) 
77                 {
78                         return true;
79                 }
80                 return false;
81         }
82         
83         Connection getConnection(DbDriver driver, String jdbcUrl) 
84                 throws ClassNotFoundException, SQLException, SquelchException 
85         {
86                 return driver.connect(jdbcUrl, m_args.getUser(), m_args.getPass());
87         }
88         
89         DbDriver getDriverFor(String jdbcUrl) throws SquelchException 
90         {
91                 for (DbDriver driver : DB_DRIVERS) {
92                         if (driver.handles(jdbcUrl)) {
93                                 return driver;
94                         }
95                 }
96                 throw new SquelchException("Cannot determine DB Driver for JDBC URL:  \"" + jdbcUrl + "\".");
97         }
98         
99         void pumpLines(PrintWriter pw, ConsoleInput ci) throws IOException, ClassNotFoundException, SQLException, SquelchException 
100         {
101                 String line = null;
102                 String jdbcUrl = m_args.getUrl();
103                 DbDriver driver = getDriverFor(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 (Connection conn = getConnection(driver, jdbcUrl)){
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 }