Shell.java revision 417deb1db112103aff04231b6ca79772ff7d3a21
1package SQLite; 2 3import SQLite.*; 4import java.io.*; 5import java.util.*; 6 7/** 8 * SQLite command line shell. This is a partial reimplementaion 9 * of sqlite/src/shell.c and can be invoked by:<P> 10 * 11 * <verb> 12 * java SQLite.Shell [OPTIONS] database [SHELLCMD] 13 * or 14 * java -jar sqlite.jar [OPTIONS] database [SHELLCMD] 15 * </verb> 16 */ 17 18public class Shell implements Callback { 19 Database db; 20 boolean echo; 21 int count; 22 int mode; 23 boolean showHeader; 24 String tableName; 25 String sep; 26 String cols[]; 27 int colwidth[]; 28 String destTable; 29 PrintWriter pw; 30 PrintWriter err; 31 32 static final int MODE_Line = 0; 33 static final int MODE_Column = 1; 34 static final int MODE_List = 2; 35 static final int MODE_Semi = 3; 36 static final int MODE_Html = 4; 37 static final int MODE_Insert = 5; 38 static final int MODE_Insert2 = 6; 39 40 public Shell(PrintWriter pw, PrintWriter err) { 41 this.pw = pw; 42 this.err = err; 43 } 44 45 public Shell(PrintStream ps, PrintStream errs) { 46 pw = new PrintWriter(ps); 47 err = new PrintWriter(errs); 48 } 49 50 protected Object clone() { 51 Shell s = new Shell(this.pw, this.err); 52 s.db = db; 53 s.echo = echo; 54 s.mode = mode; 55 s.count = 0; 56 s.showHeader = showHeader; 57 s.tableName = tableName; 58 s.sep = sep; 59 s.colwidth = colwidth; 60 return s; 61 } 62 63 static public String sql_quote_dbl(String str) { 64 if (str == null) { 65 return "NULL"; 66 } 67 int i, single = 0, dbl = 0; 68 for (i = 0; i < str.length(); i++) { 69 if (str.charAt(i) == '\'') { 70 single++; 71 } else if (str.charAt(i) == '"') { 72 dbl++; 73 } 74 } 75 if (dbl == 0) { 76 return "\"" + str + "\""; 77 } 78 StringBuffer sb = new StringBuffer("\""); 79 for (i = 0; i < str.length(); i++) { 80 char c = str.charAt(i); 81 if (c == '"') { 82 sb.append("\"\""); 83 } else { 84 sb.append(c); 85 } 86 } 87 return sb.toString(); 88 } 89 90 static public String sql_quote(String str) { 91 if (str == null) { 92 return "NULL"; 93 } 94 int i, single = 0, dbl = 0; 95 for (i = 0; i < str.length(); i++) { 96 if (str.charAt(i) == '\'') { 97 single++; 98 } else if (str.charAt(i) == '"') { 99 dbl++; 100 } 101 } 102 if (single == 0) { 103 return "'" + str + "'"; 104 } 105 if (dbl == 0) { 106 return "\"" + str + "\""; 107 } 108 StringBuffer sb = new StringBuffer("'"); 109 for (i = 0; i < str.length(); i++) { 110 char c = str.charAt(i); 111 if (c == '\'') { 112 sb.append("''"); 113 } else { 114 sb.append(c); 115 } 116 } 117 return sb.toString(); 118 } 119 120 static String html_quote(String str) { 121 if (str == null) { 122 return "NULL"; 123 } 124 StringBuffer sb = new StringBuffer(); 125 for (int i = 0; i < str.length(); i++) { 126 char c = str.charAt(i); 127 if (c == '<') { 128 sb.append("<"); 129 } else if (c == '>') { 130 sb.append(">"); 131 } else if (c == '&') { 132 sb.append("&"); 133 } else { 134 int x = c; 135 if (x < 32 || x > 127) { 136 sb.append("&#" + x + ";"); 137 } else { 138 sb.append(c); 139 } 140 } 141 } 142 return sb.toString(); 143 } 144 145 static boolean is_numeric(String str) { 146 try { 147 Double d = Double.valueOf(str); 148 } catch (java.lang.Exception e) { 149 return false; 150 } 151 return true; 152 } 153 154 void set_table_name(String str) { 155 if (str == null) { 156 tableName = ""; 157 return; 158 } 159 tableName = Shell.sql_quote(str); 160 } 161 162 public void columns(String args[]) { 163 cols = args; 164 } 165 166 public void types(String args[]) { 167 /* Empty body to satisfy SQLite.Callback interface. */ 168 } 169 170 public boolean newrow(String args[]) { 171 int i; 172 String tname; 173 switch (mode) { 174 case Shell.MODE_Line: 175 if (args.length == 0) { 176 break; 177 } 178 if (count++ > 0) { 179 pw.println(""); 180 } 181 for (i = 0; i < args.length; i++) { 182 pw.println(cols[i] + " = " + 183 args[i] == null ? "NULL" : args[i]); 184 } 185 break; 186 case Shell.MODE_Column: 187 String csep = ""; 188 if (count++ == 0) { 189 colwidth = new int[args.length]; 190 for (i = 0; i < args.length; i++) { 191 int w, n; 192 w = cols[i].length(); 193 if (w < 10) { 194 w = 10; 195 } 196 colwidth[i] = w; 197 if (showHeader) { 198 pw.print(csep + cols[i]); 199 csep = " "; 200 } 201 } 202 if (showHeader) { 203 pw.println(""); 204 } 205 } 206 if (args.length == 0) { 207 break; 208 } 209 csep = ""; 210 for (i = 0; i < args.length; i++) { 211 pw.print(csep + (args[i] == null ? "NULL" : args[i])); 212 csep = " "; 213 } 214 pw.println(""); 215 break; 216 case Shell.MODE_Semi: 217 case Shell.MODE_List: 218 if (count++ == 0 && showHeader) { 219 for (i = 0; i < args.length; i++) { 220 pw.print(cols[i] + 221 (i == args.length - 1 ? "\n" : sep)); 222 } 223 } 224 if (args.length == 0) { 225 break; 226 } 227 for (i = 0; i < args.length; i++) { 228 pw.print(args[i] == null ? "NULL" : args[i]); 229 if (mode == Shell.MODE_Semi) { 230 pw.print(";"); 231 } else if (i < args.length - 1) { 232 pw.print(sep); 233 } 234 } 235 pw.println(""); 236 break; 237 case MODE_Html: 238 if (count++ == 0 && showHeader) { 239 pw.print("<TR>"); 240 for (i = 0; i < args.length; i++) { 241 pw.print("<TH>" + html_quote(cols[i]) + "</TH>"); 242 } 243 pw.println("</TR>"); 244 } 245 if (args.length == 0) { 246 break; 247 } 248 pw.print("<TR>"); 249 for (i = 0; i < args.length; i++) { 250 pw.print("<TD>" + html_quote(args[i]) + "</TD>"); 251 } 252 pw.println("</TR>"); 253 break; 254 case MODE_Insert: 255 if (args.length == 0) { 256 break; 257 } 258 tname = tableName; 259 if (destTable != null) { 260 tname = destTable; 261 } 262 pw.print("INSERT INTO " + tname + " VALUES("); 263 for (i = 0; i < args.length; i++) { 264 String tsep = i > 0 ? "," : ""; 265 if (args[i] == null) { 266 pw.print(tsep + "NULL"); 267 } else if (is_numeric(args[i])) { 268 pw.print(tsep + args[i]); 269 } else { 270 pw.print(tsep + sql_quote(args[i])); 271 } 272 } 273 pw.println(");"); 274 break; 275 case MODE_Insert2: 276 if (args.length == 0) { 277 break; 278 } 279 tname = tableName; 280 if (destTable != null) { 281 tname = destTable; 282 } 283 pw.print("INSERT INTO " + tname + " VALUES("); 284 for (i = 0; i < args.length; i++) { 285 String tsep = i > 0 ? "," : ""; 286 pw.print(tsep + args[i]); 287 } 288 pw.println(");"); 289 break; 290 } 291 return false; 292 } 293 294 void do_meta(String line) { 295 StringTokenizer st = new StringTokenizer(line.toLowerCase()); 296 int n = st.countTokens(); 297 if (n <= 0) { 298 return; 299 } 300 String cmd = st.nextToken(); 301 String args[] = new String[n - 1]; 302 int i = 0; 303 while (st.hasMoreTokens()) { 304 args[i] = st.nextToken(); 305 ++i; 306 } 307 if (cmd.compareTo(".dump") == 0) { 308 new DBDump(this, args); 309 return; 310 } 311 if (cmd.compareTo(".echo") == 0) { 312 if (args.length > 0 && 313 (args[0].startsWith("y") || args[0].startsWith("on"))) { 314 echo = true; 315 } 316 return; 317 } 318 if (cmd.compareTo(".exit") == 0) { 319 try { 320 db.close(); 321 } catch (Exception e) { 322 } 323 System.exit(0); 324 } 325 if (cmd.compareTo(".header") == 0) { 326 if (args.length > 0 && 327 (args[0].startsWith("y") || args[0].startsWith("on"))) { 328 showHeader = true; 329 } 330 return; 331 } 332 if (cmd.compareTo(".help") == 0) { 333 pw.println(".dump ?TABLE? ... Dump database in text fmt"); 334 pw.println(".echo ON|OFF Command echo on or off"); 335 pw.println(".enc ?NAME? Change encoding"); 336 pw.println(".exit Exit program"); 337 pw.println(".header ON|OFF Display headers on or off"); 338 pw.println(".help This message"); 339 pw.println(".mode MODE Set output mode to\n" + 340 " line, column, insert\n" + 341 " list, or html"); 342 pw.println(".mode insert TABLE Generate SQL insert stmts"); 343 pw.println(".schema ?PATTERN? List table schema"); 344 pw.println(".separator STRING Set separator string"); 345 pw.println(".tables ?PATTERN? List table names"); 346 return; 347 } 348 if (cmd.compareTo(".mode") == 0) { 349 if (args.length > 0) { 350 if (args[0].compareTo("line") == 0) { 351 mode = Shell.MODE_Line; 352 } else if (args[0].compareTo("column") == 0) { 353 mode = Shell.MODE_Column; 354 } else if (args[0].compareTo("list") == 0) { 355 mode = Shell.MODE_List; 356 } else if (args[0].compareTo("html") == 0) { 357 mode = Shell.MODE_Html; 358 } else if (args[0].compareTo("insert") == 0) { 359 mode = Shell.MODE_Insert; 360 if (args.length > 1) { 361 destTable = args[1]; 362 } 363 } 364 } 365 return; 366 } 367 if (cmd.compareTo(".separator") == 0) { 368 if (args.length > 0) { 369 sep = args[0]; 370 } 371 return; 372 } 373 if (cmd.compareTo(".tables") == 0) { 374 TableResult t = null; 375 if (args.length > 0) { 376 try { 377 String qarg[] = new String[1]; 378 qarg[0] = args[0]; 379 t = db.get_table("SELECT name FROM sqlite_master " + 380 "WHERE type='table' AND " + 381 "name LIKE '%%%q%%' " + 382 "ORDER BY name", qarg); 383 } catch (Exception e) { 384 err.println("SQL Error: " + e); 385 err.flush(); 386 } 387 } else { 388 try { 389 t = db.get_table("SELECT name FROM sqlite_master " + 390 "WHERE type='table' ORDER BY name"); 391 } catch (Exception e) { 392 err.println("SQL Error: " + e); 393 err.flush(); 394 } 395 } 396 if (t != null) { 397 for (i = 0; i < t.nrows; i++) { 398 String tab = ((String[]) t.rows.elementAt(i))[0]; 399 if (tab != null) { 400 pw.println(tab); 401 } 402 } 403 } 404 return; 405 } 406 if (cmd.compareTo(".schema") == 0) { 407 if (args.length > 0) { 408 try { 409 String qarg[] = new String[1]; 410 qarg[0] = args[0]; 411 db.exec("SELECT sql FROM sqlite_master " + 412 "WHERE type!='meta' AND " + 413 "name LIKE '%%%q%%' AND " + 414 "sql NOTNULL " + 415 "ORDER BY type DESC, name", 416 this, qarg); 417 } catch (Exception e) { 418 err.println("SQL Error: " + e); 419 err.flush(); 420 } 421 } else { 422 try { 423 db.exec("SELECT sql FROM sqlite_master " + 424 "WHERE type!='meta' AND " + 425 "sql NOTNULL " + 426 "ORDER BY tbl_name, type DESC, name", 427 this); 428 } catch (Exception e) { 429 err.println("SQL Error: " + e); 430 err.flush(); 431 } 432 } 433 return; 434 } 435 if (cmd.compareTo(".enc") == 0) { 436 try { 437 db.set_encoding(args.length > 0 ? args[0] : null); 438 } catch (Exception e) { 439 err.println("" + e); 440 err.flush(); 441 } 442 return; 443 } 444 err.println("Unknown command '" + cmd + "'"); 445 err.flush(); 446 } 447 448 String read_line(BufferedReader is, String prompt) { 449 try { 450 if (prompt != null) { 451 pw.print(prompt); 452 pw.flush(); 453 } 454 String line = is.readLine(); 455 return line; 456 } catch (IOException e) { 457 return null; 458 } 459 } 460 461 void do_input(BufferedReader is) { 462 String line, sql = null; 463 String prompt = "SQLITE> "; 464 while ((line = read_line(is, prompt)) != null) { 465 if (echo) { 466 pw.println(line); 467 } 468 if (line.length() > 0 && line.charAt(0) == '.') { 469 do_meta(line); 470 } else { 471 if (sql == null) { 472 sql = line; 473 } else { 474 sql = sql + " " + line; 475 } 476 if (Database.complete(sql)) { 477 try { 478 db.exec(sql, this); 479 } catch (Exception e) { 480 if (!echo) { 481 err.println(sql); 482 } 483 err.println("SQL Error: " + e); 484 err.flush(); 485 } 486 sql = null; 487 prompt = "SQLITE> "; 488 } else { 489 prompt = "SQLITE? "; 490 } 491 } 492 pw.flush(); 493 } 494 if (sql != null) { 495 err.println("Incomplete SQL: " + sql); 496 err.flush(); 497 } 498 } 499 500 void do_cmd(String sql) { 501 if (db == null) { 502 return; 503 } 504 if (sql.length() > 0 && sql.charAt(0) == '.') { 505 do_meta(sql); 506 } else { 507 try { 508 db.exec(sql, this); 509 } catch (Exception e) { 510 err.println("SQL Error: " + e); 511 err.flush(); 512 } 513 } 514 } 515 516 public static void main(String args[]) { 517 Shell s = new Shell(System.out, System.err); 518 s.mode = Shell.MODE_List; 519 s.sep = "|"; 520 s.showHeader = false; 521 s.db = new Database(); 522 String dbname = null, sql = null; 523 for (int i = 0; i < args.length; i++) { 524 if(args[i].compareTo("-html") ==0) { 525 s.mode = Shell.MODE_Html; 526 } else if (args[i].compareTo("-list") == 0) { 527 s.mode = Shell.MODE_List; 528 } else if (args[i].compareTo("-line") == 0) { 529 s.mode = Shell.MODE_Line; 530 } else if (i < args.length - 1 && 531 args[i].compareTo("-separator") == 0) { 532 ++i; 533 s.sep = args[i]; 534 } else if (args[i].compareTo("-header") == 0) { 535 s.showHeader = true; 536 } else if (args[i].compareTo("-noheader") == 0) { 537 s.showHeader = false; 538 } else if (args[i].compareTo("-echo") == 0) { 539 s.echo = true; 540 } else if (dbname == null) { 541 dbname = args[i]; 542 } else if (sql == null) { 543 sql = args[i]; 544 } else { 545 System.err.println("Arguments: ?OPTIONS? FILENAME ?SQL?"); 546 System.exit(1); 547 } 548 } 549 if (dbname == null) { 550 System.err.println("No database file given"); 551 System.exit(1); 552 } 553 try { 554 s.db.open(dbname, 0); 555 } catch (Exception e) { 556 System.err.println("Unable to open database: " + e); 557 System.exit(1); 558 } 559 if (sql != null) { 560 s.do_cmd(sql); 561 } else { 562 // BEGIN android-modified 563 BufferedReader is = 564 new BufferedReader(new InputStreamReader(System.in), 8192); 565 // END android-modified 566 s.do_input(is); 567 } 568 try { 569 s.db.close(); 570 } catch (Exception ee) { 571 } 572 } 573} 574 575/** 576 * Internal class for dumping an entire database. 577 * It contains a special callback interface to traverse the 578 * tables of the current database and output create SQL statements 579 * and for the data insert SQL statements. 580 */ 581 582class DBDump implements Callback { 583 Shell s; 584 585 DBDump(Shell s, String tables[]) { 586 this.s = s; 587 s.pw.println("BEGIN TRANSACTION;"); 588 if (tables == null || tables.length == 0) { 589 try { 590 s.db.exec("SELECT name, type, sql FROM sqlite_master " + 591 "WHERE type!='meta' AND sql NOT NULL " + 592 "ORDER BY substr(type,2,1), name", this); 593 } catch (Exception e) { 594 s.err.println("SQL Error: " + e); 595 s.err.flush(); 596 } 597 } else { 598 String arg[] = new String[1]; 599 for (int i = 0; i < tables.length; i++) { 600 arg[0] = tables[i]; 601 try { 602 s.db.exec("SELECT name, type, sql FROM sqlite_master " + 603 "WHERE tbl_name LIKE '%q' AND type!='meta' " + 604 " AND sql NOT NULL " + 605 " ORDER BY substr(type,2,1), name", 606 this, arg); 607 } catch (Exception e) { 608 s.err.println("SQL Error: " + e); 609 s.err.flush(); 610 } 611 } 612 } 613 s.pw.println("COMMIT;"); 614 } 615 616 public void columns(String col[]) { 617 /* Empty body to satisfy SQLite.Callback interface. */ 618 } 619 620 public void types(String args[]) { 621 /* Empty body to satisfy SQLite.Callback interface. */ 622 } 623 624 public boolean newrow(String args[]) { 625 if (args.length != 3) { 626 return true; 627 } 628 s.pw.println(args[2] + ";"); 629 if (args[1].compareTo("table") == 0) { 630 Shell s2 = (Shell) s.clone(); 631 s2.mode = Shell.MODE_Insert; 632 s2.set_table_name(args[0]); 633 String qargs[] = new String[1]; 634 qargs[0] = args[0]; 635 try { 636 if (s2.db.is3()) { 637 TableResult t = null; 638 t = s2.db.get_table("PRAGMA table_info('%q')", qargs); 639 String query; 640 if (t != null) { 641 StringBuffer sb = new StringBuffer(); 642 String sep = ""; 643 644 sb.append("SELECT "); 645 for (int i = 0; i < t.nrows; i++) { 646 String col = ((String[]) t.rows.elementAt(i))[1]; 647 sb.append(sep + "quote(" + 648 Shell.sql_quote_dbl(col) + ")"); 649 sep = ","; 650 } 651 sb.append(" from '%q'"); 652 query = sb.toString(); 653 s2.mode = Shell.MODE_Insert2; 654 } else { 655 query = "SELECT * from '%q'"; 656 } 657 s2.db.exec(query, s2, qargs); 658 } else { 659 s2.db.exec("SELECT * from '%q'", s2, qargs); 660 } 661 } catch (Exception e) { 662 s.err.println("SQL Error: " + e); 663 s.err.flush(); 664 return true; 665 } 666 } 667 return false; 668 } 669} 670