Quadcap Embeddable Database

com/quadcap/sql/tools/Loader.java

Go to the documentation of this file.
00001 package com.quadcap.sql.tools; 00002 00003 /* Copyright 1999 - 2003 Quadcap Software. All rights reserved. 00004 * 00005 * This software is distributed under the Quadcap Free Software License. 00006 * This software may be used or modified for any purpose, personal or 00007 * commercial. Open Source redistributions are permitted. Commercial 00008 * redistribution of larger works derived from, or works which bundle 00009 * this software requires a "Commercial Redistribution License"; see 00010 * http://www.quadcap.com/purchase. 00011 * 00012 * Redistributions qualify as "Open Source" under one of the following terms: 00013 * 00014 * Redistributions are made at no charge beyond the reasonable cost of 00015 * materials and delivery. 00016 * 00017 * Redistributions are accompanied by a copy of the Source Code or by an 00018 * irrevocable offer to provide a copy of the Source Code for up to three 00019 * years at the cost of materials and delivery. Such redistributions 00020 * must allow further use, modification, and redistribution of the Source 00021 * Code under substantially the same terms as this license. 00022 * 00023 * Redistributions of source code must retain the copyright notices as they 00024 * appear in each source code file, these license terms, and the 00025 * disclaimer/limitation of liability set forth as paragraph 6 below. 00026 * 00027 * Redistributions in binary form must reproduce this Copyright Notice, 00028 * these license terms, and the disclaimer/limitation of liability set 00029 * forth as paragraph 6 below, in the documentation and/or other materials 00030 * provided with the distribution. 00031 * 00032 * The Software is provided on an "AS IS" basis. No warranty is 00033 * provided that the Software is free of defects, or fit for a 00034 * particular purpose. 00035 * 00036 * Limitation of Liability. Quadcap Software shall not be liable 00037 * for any damages suffered by the Licensee or any third party resulting 00038 * from use of the Software. 00039 */ 00040 00041 import java.io.BufferedInputStream; 00042 import java.io.File; 00043 import java.io.FileInputStream; 00044 import java.io.IOException; 00045 import java.io.InputStream; 00046 import java.io.PrintWriter; 00047 00048 import java.sql.Connection; 00049 import java.sql.DriverManager; 00050 import java.sql.ResultSet; 00051 import java.sql.ResultSetMetaData; 00052 import java.sql.SQLException; 00053 import java.sql.Statement; 00054 00055 import com.quadcap.util.ConfigNumber; 00056 import com.quadcap.util.ConfigString; 00057 import com.quadcap.util.Debug; 00058 import com.quadcap.util.Util; 00059 00060 /** 00061 * A simple SQL loader utility which scans the input file for 00062 * semicolon-delimited SQL statements which are executed using JDBC 00063 * against a database connection. If a statement generates a ResultSet, 00064 * the ResultSet is displayed to a PrintWriter. 00065 * 00066 * @author Stan Bailes 00067 */ 00068 public class Loader { 00069 static final ConfigNumber trace = 00070 ConfigNumber.find("trace.sql.tools.Loader", "0"); 00071 00072 Connection conn; 00073 StringBuffer buffer = new StringBuffer(); 00074 PrintWriter writer = null; 00075 int rsLimit = Integer.MAX_VALUE; 00076 static final int COLMAX = 12; 00077 00078 /** 00079 * No-argument bean constructor. We need a connection to do something 00080 * useful. 00081 */ 00082 public Loader() { 00083 } 00084 00085 /** 00086 * Construct a new Loader object bound to the specified database 00087 * connection 00088 * 00089 * @param conn the database connection 00090 */ 00091 public Loader(Connection conn) { 00092 this.conn = conn; 00093 } 00094 00095 /** 00096 * Set this loader's database connection 00097 * 00098 * @param conn the new database connection 00099 */ 00100 public void setConnection(Connection conn) { 00101 this.conn = conn; 00102 } 00103 00104 /** 00105 * Get this loader's database connection 00106 * 00107 * @return the current database connection 00108 */ 00109 public Connection getConnection() { 00110 return this.conn; 00111 } 00112 00113 /** 00114 * Set the loader's writer object. Depending on the loader's 00115 * trace level, the loader will write a trace of SQL statement execution 00116 * to this writer. 00117 * 00118 * @param writer the new writer object 00119 */ 00120 public void setWriter(PrintWriter writer) { 00121 this.writer = writer; 00122 } 00123 00124 /** 00125 * Return the loader's writer. 00126 * 00127 * @return the current writer. 00128 */ 00129 public PrintWriter getWriter() { 00130 return writer; 00131 } 00132 00133 final void print(String s) { 00134 if (trace.intValue() > 1) { 00135 buffer.append(s); 00136 } 00137 if (writer != null) writer.print(s); 00138 } 00139 00140 final void println(String s) { 00141 if (trace.intValue() > 1) { 00142 buffer.append(s); 00143 Debug.println(buffer.toString()); 00144 buffer.setLength(0); 00145 } 00146 if (writer != null) writer.println(s); 00147 } 00148 00149 final void print(Throwable t) { 00150 if (trace.intValue() > 1) Debug.print(t); 00151 if (writer != null) { 00152 t.printStackTrace(writer); 00153 } 00154 if (t instanceof SQLException) { 00155 SQLException e = ((SQLException)t).getNextException(); 00156 if (e != null) { 00157 print(e); 00158 } 00159 } 00160 } 00161 00162 static final String pad(int wid, String s) { 00163 StringBuffer sb = new StringBuffer(); 00164 if (s.length() > wid) { 00165 sb.append(s.substring(0, wid)); 00166 } else { 00167 sb.append(s); 00168 } 00169 while (sb.length() < wid) sb.append(' '); 00170 return sb.toString(); 00171 } 00172 00173 final void showResultSet(ResultSet rs) throws SQLException { 00174 showResultSet(rs, Integer.MAX_VALUE); 00175 } 00176 00177 final void showResultSet(ResultSet rs, int lim) throws SQLException { 00178 ResultSetMetaData rmeta = rs.getMetaData(); 00179 String delim = ""; 00180 for (int i = 1; i <= rmeta.getColumnCount(); i++) { 00181 int wid = Math.max(rmeta.getColumnDisplaySize(i), 00182 rmeta.getColumnLabel(i).length()); 00183 if (wid > COLMAX) wid = COLMAX; 00184 print(delim); 00185 delim = " "; 00186 print(pad(wid, rmeta.getColumnName(i))); 00187 } 00188 println(""); 00189 while (lim-- > 0 && rs.next()) { 00190 delim = ""; 00191 for (int i = 1; i <= rmeta.getColumnCount(); i++) { 00192 int wid = Math.max(rmeta.getColumnDisplaySize(i), 00193 rmeta.getColumnLabel(i).length()); 00194 if (wid > COLMAX) wid = COLMAX; 00195 print(delim); 00196 delim = " "; 00197 Object obj = rs.getObject(i); 00198 if (obj == null) obj = "<null>"; 00199 if (obj instanceof byte[]) { 00200 obj = Util.hexBytes((byte[])obj); 00201 } 00202 print(pad(wid, obj.toString())); 00203 } 00204 println(""); 00205 } 00206 } 00207 00208 public static String getLine(InputStream is) throws IOException { 00209 StringBuffer sb = new StringBuffer(); 00210 int ch; 00211 int state = 0; 00212 while ((ch = is.read()) > 0) { 00213 char c = (char)ch; 00214 switch (state) { 00215 case 0: 00216 if (c == '-') { 00217 state = 1; 00218 } else if (c == ';') { 00219 if (sb.length() > 0) { 00220 return sb.toString(); // Found ';', we're outta here 00221 } 00222 } else if (Character.isWhitespace(c) && sb.length() == 0) { 00223 } else { 00224 sb.append(c); 00225 } 00226 break; 00227 case 1: 00228 if (c == '-') { 00229 state = 2; 00230 } else { 00231 sb.append('-'); 00232 sb.append(c); 00233 state = 0; 00234 } 00235 break; 00236 case 2: 00237 if (c == '\r') state = 3; 00238 if (c == '\n') state = 0; 00239 break; 00240 case 3: 00241 if (c == '\n') state = 0; 00242 break; 00243 } 00244 } 00245 if (sb.length() > 0) { 00246 return sb.toString(); // 'add' missing trailing ';' 00247 } 00248 return null; 00249 } 00250 00251 /** 00252 * Execute a single SQL statement. There are several statements that 00253 * are not passed directly to the JDBC driver, but instead are executed 00254 * by the loader: 00255 * 00256 * <p><table border=1> 00257 * <tr><th align=left>BEGINTRANSACTION</th> 00258 * <td>Upon seeing this, the loader performs a 00259 * <code>Connection.setAutoCommit(false)</code> call on the 00260 * current database connection.</td></tr> 00261 * <tr><th align=left>ENDTRANSACTION</th> 00262 * <td>The loader performs: 00263 * <pre><code> 00264 * Connection.commit() 00265 * Connection.setAutocommit(true); 00266 * </code></pre></td></tr> 00267 * <tr><th align=left>LIMITK</th> 00268 * <td>The loader performs: 00269 * <pre><code> 00270 * Connection.rollback() 00271 * Connection.setAutocommit(true); 00272 * </code></pre></td></tr> 00273 * </table></p> 00274 * 00275 * @param sql the SQL statement to execute. 00276 */ 00277 public void execute(String sql) { 00278 try { 00279 Statement stmt = conn.createStatement(); 00280 try { 00281 if (sql.equals("BEGINTRANSACTION")) { 00282 conn.setAutoCommit(false); 00283 } else if (sql.equals("ENDTRANSACTION")) { 00284 conn.commit(); 00285 conn.setAutoCommit(true); 00286 } else if (sql.startsWith("LIMIT")) { 00287 rsLimit = Integer.parseInt(sql.substring(5).trim()); 00288 } else { 00289 if (stmt.execute(sql)) { 00290 ResultSet rs = stmt.getResultSet(); 00291 try { 00292 showResultSet(rs, rsLimit); 00293 } finally { 00294 rs.close(); 00295 } 00296 } 00297 } 00298 } catch (Throwable t) { 00299 print(t); 00300 } finally { 00301 stmt.close(); 00302 } 00303 } catch (Throwable t) { 00304 print(t); 00305 } 00306 } 00307 00308 public void loadStream(InputStream in) 00309 throws IOException, SQLException 00310 { 00311 Statement stmt = conn.createStatement(); 00312 try { 00313 String sql = null; 00314 while ((sql = getLine(in)) != null) { 00315 if (trace.intValue() > 0) { 00316 Debug.println(0, "Executing: " + sql); 00317 } 00318 try { 00319 if (sql.equals("BEGINTRANSACTION")) { 00320 conn.setAutoCommit(false); 00321 } else if (sql.equals("ENDTRANSACTION") || 00322 sql.equals("COMMIT")) { 00323 conn.commit(); 00324 conn.setAutoCommit(true); 00325 } else if (sql.equals("ROLLBACK")) { 00326 conn.rollback(); 00327 conn.setAutoCommit(true); 00328 } else if (sql.startsWith("LIMIT")) { 00329 rsLimit = Integer.parseInt(sql.substring(5).trim()); 00330 } else { 00331 if (stmt.execute(sql)) { 00332 ResultSet rs = stmt.getResultSet(); 00333 showResultSet(rs, rsLimit); 00334 } 00335 } 00336 } catch (Throwable t) { 00337 print("Exception: " + t.toString()); 00338 print("Statement: " + sql); 00339 print(t); 00340 } 00341 } 00342 } finally { 00343 stmt.close(); 00344 } 00345 } 00346 00347 /** 00348 * The specified file is scanned for semicolon-delimited SQL statements 00349 * which are executed one at a time. 00350 * 00351 * @param filename the name of the input file 00352 * @see execute 00353 */ 00354 public void loadFile(String filename) { 00355 try { 00356 FileInputStream fis = new FileInputStream(filename); 00357 BufferedInputStream bis = new BufferedInputStream(fis); 00358 try { 00359 loadStream(bis); 00360 } finally { 00361 fis.close(); 00362 } 00363 } catch (Throwable t) { 00364 print(t); 00365 } 00366 } 00367 }