1 package com.quadcap.sql.tools; 2 3 40 41 import java.io.BufferedWriter ; 42 import java.io.FileInputStream ; 43 import java.io.FileOutputStream ; 44 import java.io.IOException ; 45 import java.io.InputStream ; 46 import java.io.OutputStream ; 47 import java.io.OutputStreamWriter ; 48 import java.io.Reader ; 49 import java.io.Writer ; 50 51 52 import java.util.Iterator ; 53 import java.util.List ; 54 import java.util.HashMap ; 55 import java.util.Map ; 56 57 58 import java.util.ArrayList ; 59 import java.util.Hashtable ; 60 import java.util.Properties ; 61 import java.util.Vector ; 62 63 import java.sql.Blob ; 64 import java.sql.Clob ; 65 import java.sql.Connection ; 66 import java.sql.DatabaseMetaData ; 67 import java.sql.ResultSet ; 68 import java.sql.ResultSetMetaData ; 69 import java.sql.Statement ; 70 71 import java.sql.SQLException ; 72 import java.sql.Types ; 73 74 import java.util.zip.GZIPOutputStream ; 75 76 import com.quadcap.sql.Backup; 77 78 import com.quadcap.sql.types.Type; 79 80 import com.quadcap.util.Config; 81 import com.quadcap.util.Debug; 82 83 import com.quadcap.util.collections.DiGraph; 84 85 97 98 public class XmlDump implements Backup { 99 Connection conn; 100 int indentLevel = 0; 101 String lineSep; 102 boolean wasBegin = false; 103 boolean wasEnd = false; 104 105 109 public XmlDump() { 110 lineSep = System.getProperty("line.separator"); 111 } 112 113 118 public XmlDump(Connection conn) { 119 this.conn = conn; 120 lineSep = System.getProperty("line.separator"); 121 } 122 123 131 public XmlDump(Connection conn, int indentLevel) { 132 this.indentLevel = indentLevel; 133 this.conn = conn; 134 lineSep = System.getProperty("line.separator"); 135 } 136 137 142 public void setConnection(Connection conn) { 143 this.conn = conn; 144 } 145 146 151 public Connection getConnection() { 152 return conn; 153 } 154 155 final void beginTag(Writer w, String tag) throws IOException { 156 if (wasBegin) w.write(lineSep); 157 for (int i = 0; i < indentLevel; i++) w.write(' '); 158 indentLevel++; 159 w.write('<'); 160 w.write(tag); 161 w.write('>'); 162 wasBegin = true; 163 wasEnd = false; 164 } 165 166 final void endTag(Writer w, String tag) throws IOException { 167 indentLevel--; 168 if (wasEnd) { 169 for (int i = 0; i < indentLevel; i++) w.write(' '); 170 } 171 w.write('<'); 172 w.write('/'); 173 w.write(tag); 174 w.write('>'); 175 w.write(lineSep); 176 wasBegin = false; 177 wasEnd = true; 178 } 179 180 static final String charsLt = "<"; 181 static final String charsGt = ">"; 182 static final String charsAmp = "&"; 183 184 final void writeString(Writer w, String str) throws IOException { 185 for (int i = 0; i < str.length(); i++) { 186 char c = str.charAt(i); 187 switch (c) { 188 case '<': 189 w.write(charsLt); 190 break; 191 case '>': 192 w.write(charsGt); 193 break; 194 case '&': 195 w.write(charsAmp); 196 break; 197 default: 198 w.write(c); 199 } 200 } 201 wasBegin = false; 202 wasEnd = false; 203 } 204 205 static final char[] hexBytes = { 206 '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' 207 }; 208 209 final void writeBinaryStream(Writer w, InputStream is) throws IOException { 210 int c; 211 while ((c = is.read()) >= 0) { 212 w.write(hexBytes[(c >> 4) & 0xf]); 213 w.write(hexBytes[c & 0xf]); 214 } 215 } 216 217 final void writeCharacterStream(Writer w, Reader r) throws IOException { 218 int ci; 219 while ((ci = r.read()) >= 0) { 220 char c = (char)ci; 221 switch (c) { 222 case '<': 223 w.write(charsLt); 224 break; 225 case '>': 226 w.write(charsGt); 227 break; 228 case '&': 229 w.write(charsAmp); 230 break; 231 default: 232 w.write(c); 233 } 234 } 235 } 236 237 final void writeBinary(Writer w, Object obj) 238 throws IOException 239 { 240 if (obj instanceof Boolean ) { 241 w.write((((Boolean )obj).booleanValue()) ? '1' : '0'); 242 } else { 243 byte[] buf = (byte[])obj; 244 for (int i = 0; i < buf.length; i++) { 245 byte c = buf[i]; 246 w.write(hexBytes[(c >> 4) & 0xf]); 247 w.write(hexBytes[c & 0xf]); 248 } 249 } 250 } 251 252 final void writeValue(Writer w, Object obj, int jdbcType) 253 throws SQLException , IOException 254 { 255 switch (jdbcType) { 256 case Types.BIT: 257 case Types.TINYINT: 258 case Types.SMALLINT: 259 case Types.INTEGER: 260 case Types.BIGINT: 261 case Types.FLOAT: 262 case Types.REAL: 263 case Types.DOUBLE: 264 case Types.NUMERIC: 265 case Types.DECIMAL: 266 case Types.CHAR: 267 case Types.VARCHAR: 268 case Types.LONGVARCHAR: 269 case Types.DATE: 270 case Types.TIME: 271 case Types.TIMESTAMP: 272 case Types.OTHER: 273 writeString(w, obj.toString()); 274 break; 275 case Types.CLOB: 279 writeCharacterStream(w, ((Clob )obj).getCharacterStream()); 280 break; 282 case Types.BLOB: 286 writeBinaryStream(w, ((Blob )obj).getBinaryStream()); 288 break; 289 case Types.BINARY: 290 case Types.VARBINARY: 291 case Types.LONGVARBINARY: 292 writeBinary(w, obj); 293 break; 294 case Types.NULL: 295 case Types.JAVA_OBJECT: 297 case Types.DISTINCT: 298 case Types.STRUCT: 299 case Types.ARRAY: 300 case Types.REF: 301 throw new IOException ("Not supported, jdbc type: " + jdbcType + 303 ", object = " + obj.getClass().getName() + ": " + obj); 304 } 305 } 306 307 List parseViewDef(String s) { 308 List ret = new ArrayList (); 309 StringBuffer sb = new StringBuffer (); 310 int idx = s.indexOf(" AS "); 311 if (idx >= 0) { 312 s = s.substring(idx+4); 313 } 314 boolean l = false; 315 for (int i = 0; i < s.length(); i++) { 316 char c = s.charAt(i); 317 boolean d = Character.isLetterOrDigit(c) || c == '.'; 318 if (d ^ l) { 319 if (sb.length() > 0) { 320 ret.add(sb.toString()); 321 sb.setLength(0); 322 } 323 } 324 if (d) { 325 sb.append(c); 326 } 327 l = d; 328 } 329 if (sb.length() > 0) ret.add(sb.toString()); 330 return ret; 331 } 332 333 void dumpViewDefinitions(Writer w) throws SQLException , IOException { 334 DatabaseMetaData dbMeta = (DatabaseMetaData )conn.getMetaData(); 335 ResultSet rs = (ResultSet )dbMeta.getTables(null, "%", "%", null); 336 Map map = new HashMap (); 337 DiGraph graph = new DiGraph(); 338 try { 339 while (rs.next()) { 340 String type = rs.getString(4); 341 if (type.toLowerCase().indexOf("view") >= 0) { 342 String schema = rs.getString(2); 343 String table = rs.getString(3); 344 if (schema != null && schema.length() > 0) { 345 table = schema + "." + table; 346 } 347 String view = rs.getString(5); 348 if (view != null) { 349 Iterator it = parseViewDef(view).iterator(); 350 while (it.hasNext()) { 351 String base = it.next().toString(); 352 graph.addArc(base, table); 353 } 355 map.put(table, view); 356 } 357 } 358 } 359 Iterator iter = graph.levelize(true); 360 while (iter.hasNext()) { 361 String s = iter.next().toString(); 362 Object x = map.get(s); 363 if (x != null) { 364 beginTag(w, "ddl"); 365 writeString(w, "CREATE " + x.toString()); 366 endTag(w, "ddl"); 367 } 368 } 369 } finally { 370 rs.close(); 371 } 372 } 373 374 void dumpIndexDefinitions(Writer w, String tableName) 375 throws SQLException , IOException 376 { 377 String schemaP = null; 378 String tableP = tableName; 379 int idx = tableName.indexOf('.'); 380 if (idx > 0) { 381 schemaP = tableName.substring(0, idx); 382 tableP = tableName.substring(idx+1); 383 } 384 DatabaseMetaData dbMeta = (DatabaseMetaData )conn.getMetaData(); 385 ResultSet rs = (ResultSet )dbMeta.getIndexInfo(null, schemaP, tableP, false, true); 386 try { 387 StringBuffer columns = new StringBuffer (); 388 boolean unique = false; 389 String table = null; 390 String index = null; 391 while (rs.next()) { 392 if (rs.getObject(5) != null) { 393 String sch = rs.getString(2); 394 String tab = rs.getString(3); 395 if (sch != null && sch.length() > 0) { 396 tab = sch + "." + tab; 397 } 398 if (index != null && !index.equals(rs.getString(6))) { 399 beginTag(w, "ddl"); 400 writeString(w, "CREATE " + (unique ? "UNIQUE " : "") + 401 " INDEX " + index + " ON " + 402 table + "(" + columns + ")"); 403 endTag(w, "ddl"); 404 columns.setLength(0); 405 } 406 String column = rs.getString(9); 407 unique = !rs.getBoolean(4); 408 index = rs.getString(6); 409 table = tab; 410 if (columns.length() > 0) columns.append(", "); 411 columns.append(column); 412 } 413 } 414 if (index != null) { 415 beginTag(w, "ddl"); 416 writeString(w, "CREATE " + (unique ? "UNIQUE " : "") + 417 " INDEX " + index + " ON " + 418 table + "(" + columns + ")"); 419 endTag(w, "ddl"); 420 } 421 } finally { 422 rs.close(); 423 } 424 } 425 426 427 final Iterator orderTables() throws SQLException { 428 ArrayList t = new ArrayList (); 429 DatabaseMetaData dbMeta = (DatabaseMetaData )conn.getMetaData(); 430 ResultSet rs = (ResultSet )dbMeta.getTables(null, "%", "%", null); 431 try { 432 while (rs.next()) { 433 String type = rs.getString(4); 434 if (type.toLowerCase().indexOf("table") >= 0) { 435 String schema = rs.getString(2); 436 String table = rs.getString(3); 437 if (schema != null && schema.length() > 0) { 438 table = schema + "." + table; 439 } 440 t.add(table); 441 } 442 } 443 } finally { 444 rs.close(); 445 } 446 return t.iterator(); 447 } 448 449 class DbType { 450 int type; 451 String prefix; 452 String suffix; 453 String createParams; 454 DbType(int type, String prefix, String suffix, String createParams) { 455 this.type = type; 456 this.prefix = prefix == null ? "" : prefix; 457 this.suffix = suffix == null ? "" : suffix; 458 this.createParams = createParams == null ? "" : createParams; 459 } 460 461 String getTypePrefix() { return prefix; } 462 String getTypeSuffix() { return suffix; } 463 464 String getCreateParams(ResultSet rs) throws SQLException { 465 StringBuffer sb = new StringBuffer (""); 466 String cp = createParams; 467 while (cp.length() > 0) { 468 String param = cp; 469 int idx = cp.indexOf(','); 470 if (idx > 0) { 471 param = cp.substring(0, idx).trim(); 472 cp = cp.substring(idx+1).trim(); 473 } else { 474 cp = ""; 475 } 476 if (param.equalsIgnoreCase("length") || 477 param.equalsIgnoreCase("precision")) { 478 if (sb.length() == 0) { 479 sb.append('('); 480 } else { 481 sb.append(','); 482 } 483 sb.append(rs.getString(7)); 484 } else if (param.equalsIgnoreCase("scale")) { 485 if (sb.length() == 0) { 486 sb.append('('); 487 } else { 488 sb.append(','); 489 } 490 sb.append(rs.getString(9)); 491 } 492 } 493 if (sb.length() > 0) { 494 sb.append(')'); 495 } 496 return sb.toString(); 497 } 498 }; 499 500 Hashtable types = null; 501 502 Hashtable getTypes() throws SQLException { 503 if (types == null) { 504 types = new Hashtable (); 505 DatabaseMetaData dbMeta = (DatabaseMetaData )conn.getMetaData(); 506 ResultSet rs = (ResultSet )dbMeta.getTypeInfo(); 507 while (rs.next()) { 508 int type = rs.getInt(2); 509 String prefix = rs.getString(4); 510 String suffix = rs.getString(5); 511 String createParams = rs.getString(6); 512 DbType t = new DbType(type, prefix, suffix, createParams); 513 types.put(new Integer (type), t); 514 } 515 } 516 return types; 517 } 518 519 DbType getType(int type) throws SQLException { 520 DbType t = null; 521 getTypes(); 522 t = (DbType)types.get(new Integer (type)); 523 if (t == null) { 524 throw new RuntimeException ("no type: " + type); 525 } 526 return t; 527 } 528 529 532 public void dumpTableForeignKeys(Writer w, String tableName) 533 throws SQLException , IOException 534 { 535 String schema = null; 536 String table = tableName; 537 int idx = tableName.indexOf('.'); 538 if (idx > 0) { 539 schema = tableName.substring(0, idx); 540 table = tableName.substring(idx+1); 541 } 542 DatabaseMetaData dbMeta = (DatabaseMetaData )conn.getMetaData(); 543 ResultSet rs = (ResultSet )dbMeta.getImportedKeys(null, schema, table); 544 String cname = ""; 545 Vector pkeys = null; 546 String pkTable = null; 547 StringBuffer sb = new StringBuffer (""); 548 while (rs.next()) { 549 String constraintName = rs.getString(12); 550 if (!constraintName.equals(cname)) { 551 if (cname.length() > 0) { 552 sb.append(") references "); 553 sb.append(pkTable); 554 sb.append("("); 555 for (int i = 0; i < pkeys.size(); i++) { 556 if (i > 0) sb.append(", "); 557 sb.append(pkeys.elementAt(i).toString()); 558 } 559 sb.append(")\n"); 560 beginTag(w, "ddl"); 561 writeString(w, sb.toString()); 562 endTag(w, "ddl"); 563 } 564 String pkSchema = rs.getString(2); 565 if (pkSchema != null && pkSchema.length() > 0) { 566 pkTable = pkSchema + "." + rs.getString(3); 567 } else { 568 pkTable = rs.getString(3); 569 } 570 cname = constraintName; 571 sb.setLength(0); 572 sb.append("\nalter table "); 573 sb.append(tableName); 574 sb.append(" add constraint "); 575 sb.append(cname); 576 sb.append(" foreign key("); 577 pkeys = new Vector (); 578 } else { 579 sb.append(", "); 580 } 581 sb.append(rs.getString(8)); 582 pkeys.addElement(rs.getString(4)); 583 } 584 if (cname.length() > 0) { 585 sb.append(") references "); 586 sb.append(pkTable); 587 sb.append("("); 588 for (int i = 0; i < pkeys.size(); i++) { 589 if (i > 0) sb.append(", "); 590 sb.append(pkeys.elementAt(i).toString()); 591 } 592 sb.append(")"); 593 beginTag(w, "ddl"); 594 writeString(w, sb.toString()); 595 endTag(w, "ddl"); 596 } 597 } 598 599 602 public void dumpTableDefinition(Writer w, String tableName) 603 throws SQLException , IOException 604 { 605 Hashtable constraints = new Hashtable (); 606 String schema = null; 607 String table = tableName; 608 int idx = tableName.indexOf('.'); 609 if (idx > 0) { 610 schema = tableName.substring(0, idx); 611 table = tableName.substring(idx+1); 612 } 613 DatabaseMetaData dbMeta = (DatabaseMetaData )conn.getMetaData(); 614 ResultSet rs = (ResultSet )dbMeta.getColumns(null, schema, table, "%"); 615 616 ResultSet rs2 = 617 conn.createStatement().executeQuery("select * from " + tableName); 618 ResultSetMetaData rsMeta = rs2.getMetaData(); 619 620 StringBuffer sb = new StringBuffer ("\ncreate table "); 621 sb.append(tableName); 622 sb.append('('); 623 boolean first = true; 624 while (rs.next()) { 625 sb.append("\n\t"); 626 if (!first) sb.append(", "); 627 first = false; 628 int type = rs.getInt(5); 629 DbType t = getType(type); 630 sb.append(rs.getString(4)); sb.append(' '); 632 sb.append(rs.getString(6)); sb.append(t.getCreateParams(rs)); 634 if (rs.getInt(11) == ResultSetMetaData.columnNoNulls) { 635 sb.append(" NOT NULL"); 636 } 637 String dflt = rs.getString(13); 638 if (dflt != null) { 639 sb.append(" DEFAULT "); 640 sb.append(t.getTypePrefix()); 641 sb.append(dflt); 642 sb.append(t.getTypeSuffix()); 643 } 644 if (rsMeta.isAutoIncrement(rs.getInt(17))) { 645 sb.append(" WITH IDENTITY"); 646 } 647 } 648 rs.close(); 649 rs2.close(); 650 651 rs = (ResultSet )dbMeta.getPrimaryKeys(null, schema, table); 652 first = true; 653 while (rs.next()) { 654 if (first) { 655 constraints.put(rs.getString(6), ""); 656 sb.append("\n\t, constraint "); 657 sb.append(rs.getString(6)); sb.append(" primary key ("); 659 first = false; 660 } else { 661 sb.append(", "); 662 } 663 sb.append(rs.getString(4)); } 665 if (!first) sb.append(')'); 666 rs.close(); 667 668 String iname = ""; 669 rs = (ResultSet )dbMeta.getIndexInfo(null, schema, table, true, false); 670 first = true; 671 while (rs.next()) { 672 String indexName = rs.getString(6); 673 if (constraints.get(indexName) != null) continue; 674 if (rs.getObject(5) != null) continue; 675 if (indexName.equals(iname)) { 676 sb.append(", "); 677 } else { 678 if (iname.length() > 0) { 679 sb.append(")"); 680 } 681 sb.append("\n\t, constraint "); 682 sb.append(indexName); 683 sb.append(" unique("); 684 iname = indexName; 685 } 686 sb.append(rs.getString(9)); } 688 if (iname.length() > 0) { 689 sb.append(')'); 690 } 691 rs.close(); 692 693 sb.append(")\n"); 694 beginTag(w, "ddl"); 695 writeString(w, sb.toString()); 696 endTag(w, "ddl"); 697 } 698 699 711 public void dumpTable(Writer w, String tableName) 712 throws IOException , SQLException 713 { 714 tableName = tableName.toUpperCase(); 715 Statement s = (Statement )conn.createStatement(); 716 try { 717 ResultSet rs = (ResultSet )s.executeQuery("select * from " + tableName); 718 try { 719 ResultSetMetaData rm = (ResultSetMetaData )rs.getMetaData(); 720 int cols = rm.getColumnCount(); 721 while (rs.next()) { 722 beginTag(w, tableName); 723 for (int i = 1; i <= cols; i++) { 724 Object obj = rs.getObject(i); 725 if (!rs.wasNull()) { 726 beginTag(w, rm.getColumnName(i)); 727 try { 728 writeValue(w, obj, rm.getColumnType(i)); 729 } catch (IOException e) { 730 Debug.println("Table: " + tableName + 731 ", column = " + i + ": " + 732 rm.getColumnName(i)); 733 throw e; 734 } 735 endTag(w, rm.getColumnName(i)); 736 } 737 } 738 endTag(w, tableName); 739 } 740 } finally { 741 rs.close(); 742 } 743 } finally { 744 s.close(); 745 } 746 } 747 748 765 public void dumpTables(Writer w) 766 throws IOException , SQLException 767 { 768 w.write("<?xml version=\"1.0\"?>\n"); 769 beginTag(w, "database"); 770 Iterator iter = orderTables(); 771 while (iter.hasNext()) { 772 String table = iter.next().toString(); 773 dumpTableDefinition(w, table); 774 } 775 dumpViewDefinitions(w); 776 777 iter = orderTables(); 778 beginTag(w, "dml"); 779 while (iter.hasNext()) { 780 String table = iter.next().toString(); 781 dumpTable(w, table); 782 } 783 endTag(w, "dml"); 784 785 iter = orderTables(); 786 while (iter.hasNext()) { 787 String table = iter.next().toString(); 788 dumpIndexDefinitions(w, table); 789 } 790 791 iter = orderTables(); 792 while (iter.hasNext()) { 793 String table = iter.next().toString(); 794 dumpTableForeignKeys(w, table); 795 } 796 endTag(w, "database"); 797 } 798 799 809 public void backup(java.sql.Connection conn, Writer w) 810 throws IOException , SQLException 811 { 812 this.conn = (Connection )conn; 813 dumpTables(w); 814 } 815 816 852 public static Connection makeConnection() throws Exception { 853 Config.reset(); 854 String driver = Config.getProperty("jdbc.driver", 855 "com.quadcap.jdbc.JdbcDriver"); 856 String url = Config.getProperty("jdbc.url"); 857 String user = Config.getProperty("jdbc.user"); 858 String pass = Config.getProperty("jdbc.password"); 859 String propsfile = Config.getProperty("jdbc.props"); 860 Connection xconn = null; 861 Class.forName(driver); 862 if (propsfile != null) { 863 Properties props; 864 if (propsfile.equals("system")) { 865 props = System.getProperties(); 866 } else { 867 props = new Properties (); 868 FileInputStream pfile = new FileInputStream (propsfile); 869 props.load(pfile); 870 } 871 xconn = 872 (Connection )java.sql.DriverManager.getConnection(url, props); 873 } else if (user != null) { 874 xconn = 875 (Connection )java.sql.DriverManager.getConnection(url, user, 876 pass); 877 } else { 878 xconn = (Connection )java.sql.DriverManager.getConnection(url); 879 } 880 return xconn; 881 } 882 883 895 public static void main(String [] args) { 896 try { 897 Connection xconn = makeConnection(); 898 try { 899 XmlDump dump = new XmlDump(xconn); 900 String outfile = args[0]; 901 if (outfile.endsWith(".xml")) { 902 FileOutputStream fos = new FileOutputStream (outfile); 903 OutputStreamWriter ow = new OutputStreamWriter (fos); 904 BufferedWriter bw = new BufferedWriter (ow); 905 dump.dumpTables(bw); 906 bw.close(); 907 } else if (outfile.endsWith(".xml.gz")) { 908 FileOutputStream fos = new FileOutputStream (outfile); 909 GZIPOutputStream gos = new GZIPOutputStream (fos); 910 OutputStreamWriter ow = new OutputStreamWriter (gos); 911 BufferedWriter bw = new BufferedWriter (ow); 912 dump.dumpTables(bw); 913 bw.flush(); 914 bw.close(); 915 } else { 916 throw new Exception ("Unrecognized output type for file: " + 917 outfile); 918 } 919 } finally { 920 xconn.close(); 921 } 922 } catch (Exception e) { 923 com.quadcap.util.Debug.print(e); 924 } 925 } 926 } 927 | Popular Tags |