blob: b25e8ca89fedc5cc227c95bcf278ead23c8a8cac [file] [log] [blame]
package SQLite;
import SQLite.*;
import java.io.*;
import java.util.*;
/**
* SQLite command line shell. This is a partial reimplementaion
* of sqlite/src/shell.c and can be invoked by:<P>
*
* <verb>
* java SQLite.Shell [OPTIONS] database [SHELLCMD]
* or
* java -jar sqlite.jar [OPTIONS] database [SHELLCMD]
* </verb>
*/
public class Shell implements Callback {
Database db;
boolean echo;
int count;
int mode;
boolean showHeader;
String tableName;
String sep;
String cols[];
int colwidth[];
String destTable;
PrintWriter pw;
PrintWriter err;
static final int MODE_Line = 0;
static final int MODE_Column = 1;
static final int MODE_List = 2;
static final int MODE_Semi = 3;
static final int MODE_Html = 4;
static final int MODE_Insert = 5;
static final int MODE_Insert2 = 6;
public Shell(PrintWriter pw, PrintWriter err) {
this.pw = pw;
this.err = err;
}
public Shell(PrintStream ps, PrintStream errs) {
pw = new PrintWriter(ps);
err = new PrintWriter(errs);
}
protected Object clone() {
Shell s = new Shell(this.pw, this.err);
s.db = db;
s.echo = echo;
s.mode = mode;
s.count = 0;
s.showHeader = showHeader;
s.tableName = tableName;
s.sep = sep;
s.colwidth = colwidth;
return s;
}
static public String sql_quote_dbl(String str) {
if (str == null) {
return "NULL";
}
int i, single = 0, dbl = 0;
for (i = 0; i < str.length(); i++) {
if (str.charAt(i) == '\'') {
single++;
} else if (str.charAt(i) == '"') {
dbl++;
}
}
if (dbl == 0) {
return "\"" + str + "\"";
}
StringBuffer sb = new StringBuffer("\"");
for (i = 0; i < str.length(); i++) {
char c = str.charAt(i);
if (c == '"') {
sb.append("\"\"");
} else {
sb.append(c);
}
}
return sb.toString();
}
static public String sql_quote(String str) {
if (str == null) {
return "NULL";
}
int i, single = 0, dbl = 0;
for (i = 0; i < str.length(); i++) {
if (str.charAt(i) == '\'') {
single++;
} else if (str.charAt(i) == '"') {
dbl++;
}
}
if (single == 0) {
return "'" + str + "'";
}
if (dbl == 0) {
return "\"" + str + "\"";
}
StringBuffer sb = new StringBuffer("'");
for (i = 0; i < str.length(); i++) {
char c = str.charAt(i);
if (c == '\'') {
sb.append("''");
} else {
sb.append(c);
}
}
return sb.toString();
}
static String html_quote(String str) {
if (str == null) {
return "NULL";
}
StringBuffer sb = new StringBuffer();
for (int i = 0; i < str.length(); i++) {
char c = str.charAt(i);
if (c == '<') {
sb.append("&lt;");
} else if (c == '>') {
sb.append("&gt;");
} else if (c == '&') {
sb.append("&amp;");
} else {
int x = c;
if (x < 32 || x > 127) {
sb.append("&#" + x + ";");
} else {
sb.append(c);
}
}
}
return sb.toString();
}
static boolean is_numeric(String str) {
try {
Double d = Double.valueOf(str);
} catch (java.lang.Exception e) {
return false;
}
return true;
}
void set_table_name(String str) {
if (str == null) {
tableName = "";
return;
}
if (db.is3()) {
tableName = Shell.sql_quote_dbl(str);
} else {
tableName = Shell.sql_quote(str);
}
}
public void columns(String args[]) {
cols = args;
}
public void types(String args[]) {
/* Empty body to satisfy SQLite.Callback interface. */
}
public boolean newrow(String args[]) {
int i;
String tname;
switch (mode) {
case Shell.MODE_Line:
if (args.length == 0) {
break;
}
if (count++ > 0) {
pw.println("");
}
for (i = 0; i < args.length; i++) {
pw.println(cols[i] + " = " +
args[i] == null ? "NULL" : args[i]);
}
break;
case Shell.MODE_Column:
String csep = "";
if (count++ == 0) {
colwidth = new int[args.length];
for (i = 0; i < args.length; i++) {
int w, n;
w = cols[i].length();
if (w < 10) {
w = 10;
}
colwidth[i] = w;
if (showHeader) {
pw.print(csep + cols[i]);
csep = " ";
}
}
if (showHeader) {
pw.println("");
}
}
if (args.length == 0) {
break;
}
csep = "";
for (i = 0; i < args.length; i++) {
pw.print(csep + (args[i] == null ? "NULL" : args[i]));
csep = " ";
}
pw.println("");
break;
case Shell.MODE_Semi:
case Shell.MODE_List:
if (count++ == 0 && showHeader) {
for (i = 0; i < args.length; i++) {
pw.print(cols[i] +
(i == args.length - 1 ? "\n" : sep));
}
}
if (args.length == 0) {
break;
}
for (i = 0; i < args.length; i++) {
pw.print(args[i] == null ? "NULL" : args[i]);
if (mode == Shell.MODE_Semi) {
pw.print(";");
} else if (i < args.length - 1) {
pw.print(sep);
}
}
pw.println("");
break;
case MODE_Html:
if (count++ == 0 && showHeader) {
pw.print("<TR>");
for (i = 0; i < args.length; i++) {
pw.print("<TH>" + html_quote(cols[i]) + "</TH>");
}
pw.println("</TR>");
}
if (args.length == 0) {
break;
}
pw.print("<TR>");
for (i = 0; i < args.length; i++) {
pw.print("<TD>" + html_quote(args[i]) + "</TD>");
}
pw.println("</TR>");
break;
case MODE_Insert:
if (args.length == 0) {
break;
}
tname = tableName;
if (destTable != null) {
tname = destTable;
}
pw.print("INSERT INTO " + tname + " VALUES(");
for (i = 0; i < args.length; i++) {
String tsep = i > 0 ? "," : "";
if (args[i] == null) {
pw.print(tsep + "NULL");
} else if (is_numeric(args[i])) {
pw.print(tsep + args[i]);
} else {
pw.print(tsep + sql_quote(args[i]));
}
}
pw.println(");");
break;
case MODE_Insert2:
if (args.length == 0) {
break;
}
tname = tableName;
if (destTable != null) {
tname = destTable;
}
pw.print("INSERT INTO " + tname + " VALUES(");
for (i = 0; i < args.length; i++) {
String tsep = i > 0 ? "," : "";
pw.print(tsep + args[i]);
}
pw.println(");");
break;
}
return false;
}
void do_meta(String line) {
StringTokenizer st = new StringTokenizer(line.toLowerCase());
int n = st.countTokens();
if (n <= 0) {
return;
}
String cmd = st.nextToken();
String args[] = new String[n - 1];
int i = 0;
while (st.hasMoreTokens()) {
args[i] = st.nextToken();
++i;
}
if (cmd.compareTo(".dump") == 0) {
new DBDump(this, args);
return;
}
if (cmd.compareTo(".echo") == 0) {
if (args.length > 0 &&
(args[0].startsWith("y") || args[0].startsWith("on"))) {
echo = true;
}
return;
}
if (cmd.compareTo(".exit") == 0) {
try {
db.close();
} catch (Exception e) {
}
System.exit(0);
}
if (cmd.compareTo(".header") == 0) {
if (args.length > 0 &&
(args[0].startsWith("y") || args[0].startsWith("on"))) {
showHeader = true;
}
return;
}
if (cmd.compareTo(".help") == 0) {
pw.println(".dump ?TABLE? ... Dump database in text fmt");
pw.println(".echo ON|OFF Command echo on or off");
pw.println(".enc ?NAME? Change encoding");
pw.println(".exit Exit program");
pw.println(".header ON|OFF Display headers on or off");
pw.println(".help This message");
pw.println(".mode MODE Set output mode to\n" +
" line, column, insert\n" +
" list, or html");
pw.println(".mode insert TABLE Generate SQL insert stmts");
pw.println(".schema ?PATTERN? List table schema");
pw.println(".separator STRING Set separator string");
pw.println(".tables ?PATTERN? List table names");
return;
}
if (cmd.compareTo(".mode") == 0) {
if (args.length > 0) {
if (args[0].compareTo("line") == 0) {
mode = Shell.MODE_Line;
} else if (args[0].compareTo("column") == 0) {
mode = Shell.MODE_Column;
} else if (args[0].compareTo("list") == 0) {
mode = Shell.MODE_List;
} else if (args[0].compareTo("html") == 0) {
mode = Shell.MODE_Html;
} else if (args[0].compareTo("insert") == 0) {
mode = Shell.MODE_Insert;
if (args.length > 1) {
destTable = args[1];
}
}
}
return;
}
if (cmd.compareTo(".separator") == 0) {
if (args.length > 0) {
sep = args[0];
}
return;
}
if (cmd.compareTo(".tables") == 0) {
TableResult t = null;
if (args.length > 0) {
try {
String qarg[] = new String[1];
qarg[0] = args[0];
t = db.get_table("SELECT name FROM sqlite_master " +
"WHERE type='table' AND " +
"name LIKE '%%%q%%' " +
"ORDER BY name", qarg);
} catch (Exception e) {
err.println("SQL Error: " + e);
err.flush();
}
} else {
try {
t = db.get_table("SELECT name FROM sqlite_master " +
"WHERE type='table' ORDER BY name");
} catch (Exception e) {
err.println("SQL Error: " + e);
err.flush();
}
}
if (t != null) {
for (i = 0; i < t.nrows; i++) {
String tab = ((String[]) t.rows.elementAt(i))[0];
if (tab != null) {
pw.println(tab);
}
}
}
return;
}
if (cmd.compareTo(".schema") == 0) {
if (args.length > 0) {
try {
String qarg[] = new String[1];
qarg[0] = args[0];
db.exec("SELECT sql FROM sqlite_master " +
"WHERE type!='meta' AND " +
"name LIKE '%%%q%%' AND " +
"sql NOTNULL " +
"ORDER BY type DESC, name",
this, qarg);
} catch (Exception e) {
err.println("SQL Error: " + e);
err.flush();
}
} else {
try {
db.exec("SELECT sql FROM sqlite_master " +
"WHERE type!='meta' AND " +
"sql NOTNULL " +
"ORDER BY tbl_name, type DESC, name",
this);
} catch (Exception e) {
err.println("SQL Error: " + e);
err.flush();
}
}
return;
}
if (cmd.compareTo(".enc") == 0) {
try {
db.set_encoding(args.length > 0 ? args[0] : null);
} catch (Exception e) {
err.println("" + e);
err.flush();
}
return;
}
if (cmd.compareTo(".rekey") == 0) {
try {
db.rekey(args.length > 0 ? args[0] : null);
} catch (Exception e) {
err.println("" + e);
err.flush();
}
return;
}
err.println("Unknown command '" + cmd + "'");
err.flush();
}
String read_line(BufferedReader is, String prompt) {
try {
if (prompt != null) {
pw.print(prompt);
pw.flush();
}
String line = is.readLine();
return line;
} catch (IOException e) {
return null;
}
}
void do_input(BufferedReader is) {
String line, sql = null;
String prompt = "SQLITE> ";
while ((line = read_line(is, prompt)) != null) {
if (echo) {
pw.println(line);
}
if (line.length() > 0 && line.charAt(0) == '.') {
do_meta(line);
} else {
if (sql == null) {
sql = line;
} else {
sql = sql + " " + line;
}
if (Database.complete(sql)) {
try {
db.exec(sql, this);
} catch (Exception e) {
if (!echo) {
err.println(sql);
}
err.println("SQL Error: " + e);
err.flush();
}
sql = null;
prompt = "SQLITE> ";
} else {
prompt = "SQLITE? ";
}
}
pw.flush();
}
if (sql != null) {
err.println("Incomplete SQL: " + sql);
err.flush();
}
}
void do_cmd(String sql) {
if (db == null) {
return;
}
if (sql.length() > 0 && sql.charAt(0) == '.') {
do_meta(sql);
} else {
try {
db.exec(sql, this);
} catch (Exception e) {
err.println("SQL Error: " + e);
err.flush();
}
}
}
public static void main(String args[]) {
String key = null;
Shell s = new Shell(System.out, System.err);
s.mode = Shell.MODE_List;
s.sep = "|";
s.showHeader = false;
s.db = new Database();
String dbname = null, sql = null;
for (int i = 0; i < args.length; i++) {
if(args[i].compareTo("-html") ==0) {
s.mode = Shell.MODE_Html;
} else if (args[i].compareTo("-list") == 0) {
s.mode = Shell.MODE_List;
} else if (args[i].compareTo("-line") == 0) {
s.mode = Shell.MODE_Line;
} else if (i < args.length - 1 &&
args[i].compareTo("-separator") == 0) {
++i;
s.sep = args[i];
} else if (args[i].compareTo("-header") == 0) {
s.showHeader = true;
} else if (args[i].compareTo("-noheader") == 0) {
s.showHeader = false;
} else if (args[i].compareTo("-echo") == 0) {
s.echo = true;
} else if (args[i].compareTo("-key") == 0) {
++i;
key = args[i];
} else if (dbname == null) {
dbname = args[i];
} else if (sql == null) {
sql = args[i];
} else {
System.err.println("Arguments: ?OPTIONS? FILENAME ?SQL?");
System.exit(1);
}
}
if (dbname == null) {
System.err.println("No database file given");
System.exit(1);
}
try {
s.db.open(dbname, 0);
} catch (Exception e) {
System.err.println("Unable to open database: " + e);
System.exit(1);
}
if (key != null) {
try {
s.db.key(key);
} catch (Exception e) {
System.err.println("Unable to set key: " + e);
System.exit(1);
}
}
if (sql != null) {
s.do_cmd(sql);
s.pw.flush();
} else {
BufferedReader is =
new BufferedReader(new InputStreamReader(System.in));
s.do_input(is);
s.pw.flush();
}
try {
s.db.close();
} catch (Exception ee) {
}
}
}
/**
* Internal class for dumping an entire database.
* It contains a special callback interface to traverse the
* tables of the current database and output create SQL statements
* and for the data insert SQL statements.
*/
class DBDump implements Callback {
Shell s;
DBDump(Shell s, String tables[]) {
this.s = s;
s.pw.println("BEGIN TRANSACTION;");
if (tables == null || tables.length == 0) {
try {
s.db.exec("SELECT name, type, sql FROM sqlite_master " +
"WHERE type!='meta' AND sql NOT NULL " +
"ORDER BY substr(type,2,1), name", this);
} catch (Exception e) {
s.err.println("SQL Error: " + e);
s.err.flush();
}
} else {
String arg[] = new String[1];
for (int i = 0; i < tables.length; i++) {
arg[0] = tables[i];
try {
s.db.exec("SELECT name, type, sql FROM sqlite_master " +
"WHERE tbl_name LIKE '%q' AND type!='meta' " +
" AND sql NOT NULL " +
" ORDER BY substr(type,2,1), name",
this, arg);
} catch (Exception e) {
s.err.println("SQL Error: " + e);
s.err.flush();
}
}
}
s.pw.println("COMMIT;");
}
public void columns(String col[]) {
/* Empty body to satisfy SQLite.Callback interface. */
}
public void types(String args[]) {
/* Empty body to satisfy SQLite.Callback interface. */
}
public boolean newrow(String args[]) {
if (args.length != 3) {
return true;
}
s.pw.println(args[2] + ";");
if (args[1].compareTo("table") == 0) {
Shell s2 = (Shell) s.clone();
s2.mode = Shell.MODE_Insert;
s2.set_table_name(args[0]);
String qargs[] = new String[1];
qargs[0] = args[0];
try {
if (s2.db.is3()) {
TableResult t = null;
t = s2.db.get_table("PRAGMA table_info('%q')", qargs);
String query;
if (t != null) {
StringBuffer sb = new StringBuffer();
String sep = "";
sb.append("SELECT ");
for (int i = 0; i < t.nrows; i++) {
String col = ((String[]) t.rows.elementAt(i))[1];
sb.append(sep + "quote(" +
Shell.sql_quote_dbl(col) + ")");
sep = ",";
}
sb.append(" from '%q'");
query = sb.toString();
s2.mode = Shell.MODE_Insert2;
} else {
query = "SELECT * from '%q'";
}
s2.db.exec(query, s2, qargs);
} else {
s2.db.exec("SELECT * from '%q'", s2, qargs);
}
} catch (Exception e) {
s.err.println("SQL Error: " + e);
s.err.flush();
return true;
}
}
return false;
}
}