📄 ij.jj
字号:
options { STATIC = false; LOOKAHEAD = 2; DEBUG_PARSER = false; DEBUG_TOKEN_MANAGER = false; ERROR_REPORTING = true; USER_TOKEN_MANAGER = false; USER_CHAR_STREAM = true; JAVA_UNICODE_ESCAPE = false; UNICODE_INPUT = true; IGNORE_CASE = true; CACHE_TOKENS = true;}PARSER_BEGIN(ij)package org.apache.derby.impl.tools.ij;import org.apache.derby.iapi.reference.JDBC20Translation;import org.apache.derby.iapi.reference.JDBC30Translation;import org.apache.derby.tools.JDBCDisplayUtil;import org.apache.derby.iapi.tools.i18n.LocalizedInput;import org.apache.derby.iapi.tools.i18n.LocalizedResource;import org.apache.derby.iapi.services.info.JVMInfo;import org.apache.derby.tools.URLCheck;import java.lang.reflect.*;import java.sql.Connection;import java.sql.DatabaseMetaData;import java.sql.DriverManager;import java.sql.Statement;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.ResultSetMetaData;import java.sql.SQLException;import java.sql.SQLWarning;import java.util.Hashtable;import java.util.Properties;import java.util.StringTokenizer;import java.io.IOException;import java.util.Vector;import java.util.Enumeration;import java.util.Locale;/** This parser works on a statement-at-a-time basis. It maintains a connection environment that is set by the caller and contains a list of connections for the current thread/ij session. Multi-user frameworks that use this parser tend to maintain multiple connectionEnv's and pass in the current one to set ij up. A connectionEnv has a default connection in use, and the ij connect/set connection/disconnect commands are used to change the current connection. Each connection has associated with it a list of prepared statements and cursors, created by the ij prepare and get cursor statements and manipulated by additional ij statements. To enable multiple display modes, this parser will not output anything, but will return objects that the caller can then display. This means the caller is responsible for displaying thrown exceptions and also SQLWarnings. So, our return value is the JDBC object upon which warnings will be hung, i.e. the one manipulated by the statement, if any. If there is no object to display, then a null is returned. @author ames */class ij { static final String PROTOCOL_PROPERTY = "ij.protocol"; static final String USER_PROPERTY = "ij.user"; static final String PASSWORD_PROPERTY = "ij.password"; static final String FRAMEWORK_PROPERTY = "framework"; boolean elapsedTime = false; Connection theConnection = null; ConnectionEnv currentConnEnv = null; xaAbstractHelper xahelper = null; boolean exit = false; utilMain utilInstance = null; Hashtable ignoreErrors = null; String protocol = null; // the (single) unnamed protocol Hashtable namedProtocols; /** * A constructor that understands the local state that needs to be * initialized. * * @param tm The token manager to use * @param utilInstance The util to use */ ij(ijTokenManager tm, utilMain utilInstance) { this(tm); this.utilInstance = utilInstance; // load all protocols specified via properties // Properties p = System.getProperties(); protocol = p.getProperty(PROTOCOL_PROPERTY); String framework_property = p.getProperty(FRAMEWORK_PROPERTY); if (ij.JDBC20X() && ij.JTA() && ij.JNDI()) { try { xahelper = (xaAbstractHelper) Class.forName("org.apache.derby.impl.tools.ij.xaHelper").newInstance(); xahelper.setFramework(framework_property); } catch (Exception e) { } } namedProtocols = new Hashtable(); String prefix = PROTOCOL_PROPERTY + "."; for (Enumeration e = p.propertyNames(); e.hasMoreElements(); ) { String key = (String)e.nextElement(); if (key.startsWith(prefix)) { String name = key.substring(prefix.length()); installProtocol(name.toUpperCase(Locale.ENGLISH), p.getProperty(key)); } } } /** * Return whether or not JDBC 2.0 (and greater) extension classes can be loaded * * @return true if JDBC 2.0 (and greater) extension classes can be loaded */ private static boolean JDBC20X() { try { Class.forName("javax.sql.DataSource"); Class.forName("javax.sql.ConnectionPoolDataSource"); Class.forName("javax.sql.PooledConnection"); Class.forName("javax.sql.XAConnection"); Class.forName("javax.sql.XADataSource"); } catch(ClassNotFoundException cnfe) { return false; } return true; } /** * Return whether or not JTA classes can be loaded * * @return true if JTA classes can be loaded */ private static boolean JTA() { try { Class.forName("javax.transaction.xa.Xid"); Class.forName("javax.transaction.xa.XAResource"); Class.forName("javax.transaction.xa.XAException"); } catch(ClassNotFoundException cnfe) { return false; } return true; } /** * Return whether or not JNDI extension classes can be loaded * * @return true if JNDI extension classes can be loaded */ private static boolean JNDI() { try { Class.forName("javax.naming.spi.Resolver"); Class.forName("javax.naming.Referenceable"); Class.forName("javax.naming.directory.Attribute"); } catch(ClassNotFoundException cnfe) { return false; } return true; }// FIXME: caller has to deal with ignoreErrors and handleSQLException behavior /** Add the warnings of wTail to the end of those of wHead. */ SQLWarning appendWarnings(SQLWarning wHead, SQLWarning wTail) { if (wHead == null) return wTail; if (wHead.getNextException() == null) { wHead.setNextException(wTail); } else { appendWarnings(wHead.getNextWarning(), wTail); } return wHead; } /** * Get the "elapsedTime state". */ boolean getElapsedTimeState() { return elapsedTime; } /** this removes the outside quotes from the string. it will also swizzle the special characters into their actual characters, like '' for ', etc. */ String stringValue(String s) { String result = s.substring(1,s.length()-1); char quotes = '\''; int index; /* Find the first occurrence of adjacent quotes. */ index = result.indexOf(quotes); /* Replace each occurrence with a single quote and begin the * search for the next occurrence from where we left off. */ while (index != -1) { result = result.substring(0, index + 1) + result.substring(index + 2); index = result.indexOf(quotes, index + 1); } return result; } void installProtocol(String name, String value) { try { // `value' is a JDBC protocol; // we load the "driver" in the prototypical // manner, it will register itself with // the DriverManager. util.loadDriverIfKnown(value); } catch (ClassNotFoundException e) { throw ijException.classNotFoundForProtocol(value); } catch (IllegalArgumentException e) { throw ijException.classNotFoundForProtocol(value); } catch (IllegalAccessException e) { throw ijException.classNotFoundForProtocol(value); } catch (InstantiationException e) { throw ijException.classNotFoundForProtocol(value); } if (name == null) protocol = value; else namedProtocols.put(name, value); } void haveConnection() { JDBCDisplayUtil.checkNotNull(theConnection, "connection"); } /** We do not reuse statement objects at all, because some systems require you to close the object to release resources (JBMS), while others will not let you reuse the statement object once it is closed (WebLogic). If you want to reuse statement objects, you need to use the ij PREPARE and EXECUTE statements. @param stmt the statement **/ ijResult executeImmediate(String stmt) throws SQLException { Statement aStatement = null; try { long beginTime = 0; long endTime = 0; boolean cleanUpStmt = false; haveConnection(); aStatement = theConnection.createStatement(); // for JCC - remove comments at the beginning of the statement // and trim; do the same for Derby Clients that have versions // earlier than 10.2. if (currentConnEnv != null) { boolean trimForDNC = currentConnEnv.getSession().getIsDNC(); if (trimForDNC) { // we're using the Derby Client, but we only want to trim // if the version is earlier than 10.2. DatabaseMetaData dbmd = theConnection.getMetaData(); int majorVersion = dbmd.getDriverMajorVersion(); if ((majorVersion > 10) || ((majorVersion == 10) && (dbmd.getDriverMinorVersion() > 1))) { // 10.2 or later, so don't trim/remove comments. trimForDNC = false; } } if (currentConnEnv.getSession().getIsJCC() || trimForDNC) { // remove comments and trim. int nextline; while(stmt.startsWith("--")) { nextline = stmt.indexOf('\n')+1; stmt = stmt.substring(nextline); } stmt = stmt.trim(); } } aStatement.execute(stmt); // FIXME: display results. return start time. return new ijStatementResult(aStatement,true); } catch (SQLException e) { if (aStatement!=null) // free the resource aStatement.close(); throw e; } } ijResult quit() throws SQLException { exit = true; if (getExpect()) { // report stats // FIXME: replace with MVC... // FIXME: this is a kludgy way to quiet /0 and make 0/0=1... int numExpectOr1 = (numExpect==0?1:numExpect); int numPassOr1 = (numPass==numExpect && numPass==0)?1:numPass; int numFailOr1 = (numFail==numExpect && numFail==0)?1:numFail; int numUnxOr1 = (numUnx==numExpect && numUnx==0)?1:numUnx; LocalizedResource.OutputWriter().println(LocalizedResource.getMessage("IJ_TestsRun0Pass12Fail34", new Object[]{ LocalizedResource.getNumber(numExpect), LocalizedResource.getNumber(100*(numPassOr1/numExpectOr1)), LocalizedResource.getNumber(100*(numFailOr1/numExpectOr1))})); if (numUnx > 0) { LocalizedResource.OutputWriter().println(); LocalizedResource.OutputWriter().println(LocalizedResource.getMessage("IJ_UnexpResulUnx01", LocalizedResource.getNumber(numUnx), LocalizedResource.getNumber(100*(numUnxOr1/numExpectOr1)))); } } currentConnEnv.removeAllSessions(); theConnection = null; return null; } /** Async execution wants to return results off-cycle. We want to control their output, and so will hold it up until it is requested with a WAIT FOR asyncName statement. WAIT FOR will return the results of the async statement once they are ready. Note that using a select only waits for the execute to complete; the logic to step through the result set is in the caller. **/ ijResult executeAsync(String stmt, String name) { AsyncStatement as = new AsyncStatement(theConnection, stmt); currentConnEnv.getSession().addAsyncStatement(name,as); as.start(); return null; } void setConnection(ConnectionEnv connEnv, boolean multipleEnvironments) { Connection conn = connEnv.getConnection(); if (connEnv != currentConnEnv) // single connenv is common case currentConnEnv = connEnv; if (theConnection == conn) return; // not changed. if ((theConnection == null) || multipleEnvironments) { // must have switched env's (could check) theConnection = conn; } else { throw ijException.needToDisconnect(); } } /** Note the Expect Result in the output and in the stats. FIXME */ int numExpect, numPass, numFail, numUnx; private void noteExpect(boolean actual, boolean want) { numExpect++; if (actual) numPass++; else numFail++; LocalizedResource.OutputWriter().print(LocalizedResource.getMessage(actual?"IJ_Pass":"IJ_Fail")); if (actual != want) { numUnx++; LocalizedResource.OutputWriter().println(LocalizedResource.getMessage("IJ_Unx")); } else LocalizedResource.OutputWriter().println(); } private boolean getExpect() { return Boolean.getBoolean("ij.expect"); } private ijResult addSession ( Connection newConnection, String name ) throws SQLException { if (currentConnEnv.haveSession(name)) { throw ijException.alreadyHaveConnectionNamed(name); } currentConnEnv.addSession( newConnection, name ); return new ijConnectionResult( newConnection ); } private String[] sortConnectionNames() { int size = 100; int count = 0; String[] array = new String[size]; String key; Hashtable ss = currentConnEnv.getSessions(); // Calculate the number of connections in the sessions list and // build an array of all the connection names. for (Enumeration connectionNames = ss.keys(); connectionNames.hasMoreElements();) { if (count == size) { // need to expand the array size = size*2; String[] expandedArray = new String[size]; System.arraycopy(array, 0, expandedArray, 0, count); array = expandedArray; } key = (String)connectionNames.nextElement(); array[ count++ ] = key; } java.util.Arrays.sort(array, 0, count); return array; } /** This is used at the ij startup time to see if there are already some connections made and if so, show connections made so far. Following also gets executed when user types show connections command in ij. In the former case, ignore0Rows is set whereas in the later cas it's set to false. The reason for this is, at ij startup time, if there are no connections made so far, we don't want to show anything. Only if there are connections made, we show the connections. Whereas in show connection command case, we want to show the connection status either way ie if there are no connections, we say no connections. Otherwise we list all the connections made so far. */ public ijResult showConnectionsMethod(boolean ignore0Rows) throws SQLException { Hashtable ss = currentConnEnv.getSessions(); Vector v = new Vector(); SQLWarning w = null; if (ss == null || ss.size() == 0) { if (!ignore0Rows) v.addElement(LocalizedResource.getMessage("IJ_NoConneAvail")); } else { boolean haveCurrent=false; int count = 0; for (Enumeration connectionNames = ss.keys(); connectionNames.hasMoreElements(); connectionNames.nextElement()) count++; String[] array = sortConnectionNames(); for ( int ictr = 0; ictr < count; ictr++ ) { String connectionName = array[ ictr ]; Session s = (Session)ss.get(connectionName); if (s.getConnection().isClosed()) { if (currentConnEnv.getSession() != null && connectionName.equals(currentConnEnv.getSession().getName())) { currentConnEnv.removeCurrentSession(); theConnection = null; } else currentConnEnv.removeSession(connectionName); } else { StringBuffer row = new StringBuffer(); row.append(connectionName); if (currentConnEnv.getSession() != null && connectionName.equals(currentConnEnv.getSession().getName())) { row.append('*'); haveCurrent=true; } row.append(" - "); row.append(s.getConnection().getMetaData().getURL()); // save the warnings from these connections w = appendWarnings(w,s.getConnection().getWarnings()); s.getConnection().clearWarnings(); v.addElement(row.toString()); } } if (haveCurrent) v.addElement(LocalizedResource.getMessage("IJ_CurreConne")); else
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -