0001: /* Copyrights and Licenses
0002: *
0003: * This product includes Hypersonic SQL.
0004: * Originally developed by Thomas Mueller and the Hypersonic SQL Group.
0005: *
0006: * Copyright (c) 1995-2000 by the Hypersonic SQL Group. All rights reserved.
0007: * Redistribution and use in source and binary forms, with or without modification, are permitted
0008: * provided that the following conditions are met:
0009: * - Redistributions of source code must retain the above copyright notice, this list of conditions
0010: * and the following disclaimer.
0011: * - Redistributions in binary form must reproduce the above copyright notice, this list of
0012: * conditions and the following disclaimer in the documentation and/or other materials
0013: * provided with the distribution.
0014: * - All advertising materials mentioning features or use of this software must display the
0015: * following acknowledgment: "This product includes Hypersonic SQL."
0016: * - Products derived from this software may not be called "Hypersonic SQL" nor may
0017: * "Hypersonic SQL" appear in their names without prior written permission of the
0018: * Hypersonic SQL Group.
0019: * - Redistributions of any form whatsoever must retain the following acknowledgment: "This
0020: * product includes Hypersonic SQL."
0021: * This software is provided "as is" and any expressed or implied warranties, including, but
0022: * not limited to, the implied warranties of merchantability and fitness for a particular purpose are
0023: * disclaimed. In no event shall the Hypersonic SQL Group or its contributors be liable for any
0024: * direct, indirect, incidental, special, exemplary, or consequential damages (including, but
0025: * not limited to, procurement of substitute goods or services; loss of use, data, or profits;
0026: * or business interruption). However caused any on any theory of liability, whether in contract,
0027: * strict liability, or tort (including negligence or otherwise) arising in any way out of the use of this
0028: * software, even if advised of the possibility of such damage.
0029: * This software consists of voluntary contributions made by many individuals on behalf of the
0030: * Hypersonic SQL Group.
0031: *
0032: *
0033: * For work added by the HSQL Development Group:
0034: *
0035: * Copyright (c) 2001-2002, The HSQL Development Group
0036: * All rights reserved.
0037: *
0038: * Redistribution and use in source and binary forms, with or without
0039: * modification, are permitted provided that the following conditions are met:
0040: *
0041: * Redistributions of source code must retain the above copyright notice, this
0042: * list of conditions and the following disclaimer, including earlier
0043: * license statements (above) and comply with all above license conditions.
0044: *
0045: * Redistributions in binary form must reproduce the above copyright notice,
0046: * this list of conditions and the following disclaimer in the documentation
0047: * and/or other materials provided with the distribution, including earlier
0048: * license statements (above) and comply with all above license conditions.
0049: *
0050: * Neither the name of the HSQL Development Group nor the names of its
0051: * contributors may be used to endorse or promote products derived from this
0052: * software without specific prior written permission.
0053: *
0054: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0055: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0056: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0057: * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
0058: * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
0059: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
0060: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
0061: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
0062: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0063: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
0064: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0065: */
0066:
0067: package org.hsqldb;
0068:
0069: import org.hsqldb.lib.HsqlArrayList;
0070: import org.hsqldb.lib.HsqlHashMap;
0071: import org.hsqldb.replication.*;
0072: import java.io.File;
0073: import java.sql.SQLException;
0074: import java.sql.Types;
0075: import java.util.Calendar;
0076: import java.util.Date;
0077: import java.util.Enumeration;
0078: import java.util.Vector;
0079: import org.javagroups.*;
0080: import org.javagroups.blocks.*;
0081: import org.javagroups.util.Promise;
0082: import org.javagroups.util.Util;
0083:
0084: /**
0085: * Database is the root class for HSQL Database Engine database. <p>
0086: *
0087: * Although it either directly or indirectly provides all or most of the
0088: * services required for DBMS functionality, this class should not be used
0089: * directly by an application. Instead, to achieve portability and
0090: * generality, the jdbc* classes should be used.
0091: *
0092: * @version 1.7.0
0093: */
0094:
0095: // fredt@users 20020130 - patch 476694 by velichko - transaction savepoints
0096: // additions to different parts to support savepoint transactions
0097: // fredt@users 20020215 - patch 1.7.0 by fredt - new HsqlProperties class
0098: // support use of properties from database.properties file
0099: // fredt@users 20020218 - patch 1.7.0 by fredt - DEFAULT keyword
0100: // support for default values for table columns
0101: // fredt@users 20020305 - patch 1.7.0 - restructuring
0102: // some methods move to Table.java, some removed
0103: // fredt@users 20020221 - patch 513005 by sqlbob@users (RMP) - restructuring
0104: // fredt@users 20020221 - patch 513005 by sqlbob@users (RMP) - error trapping
0105: // boucherb@users 20020130 - patch 1.7.0 - use lookup for speed
0106: // idents listed in alpha-order for easy check of stats...
0107: // fredt@users 20020420 - patch523880 by leptipre@users - VIEW support
0108: // fredt@users 20020430 - patch 549741 by velichko - ALTER TABLE RENAME
0109: // fredt@users 20020405 - patch 1.7.0 by fredt - other ALTER TABLE statements
0110: // boucherb@users - added javadoc comments
0111: // tony_lai@users 20020820 - patch 595099 by tlai@users - use user define PK name
0112: // tony_lai@users 20020820 - patch 595073 by tlai@users - duplicated exception msg
0113: // tony_lai@users 20020820 - patch 595156 by tlai@users - violation of Integrity constraint name
0114: // tony_lai@users 20020820 - patch 1.7.1 - modification to shutdown compact process to save memory usage
0115: // boucherb@users 20020828 - patch 1.7.1 - allow reconnect to local db that has shutdown
0116: class Database implements MessageListener, MembershipListener {
0117:
0118: private String sName;
0119: private UserManager aAccess;
0120: private HsqlArrayList tTable;
0121: private DatabaseInformation dInfo;
0122: Logger logger;
0123: private boolean bReadOnly;
0124: private boolean bShutdown;
0125: private HsqlHashMap hAlias;
0126: private boolean bIgnoreCase;
0127: private boolean bReferentialIntegrity;
0128: private HsqlArrayList cSession;
0129: private HsqlDatabaseProperties databaseProperties;
0130: private Session sysSession;
0131: private Channel channel;
0132: private Address local_addr = null;
0133: private PullPushAdapter adapter = null;
0134: private boolean replicate = Boolean.getBoolean("repl");
0135: private Promise state_promise = new Promise();
0136: private long STATE_TIMEOUT = 0; // 0 = wait forever
0137: private boolean sending_disabled = false; // don't replicate while true
0138:
0139: private static final int CALL = 1;
0140: private static final int CHECKPOINT = 2;
0141: private static final int COMMIT = 3;
0142: private static final int CONNECT = 4;
0143: private static final int CREATE = 5;
0144: private static final int DELETE = 6;
0145: private static final int DISCONNECT = 7;
0146: private static final int DROP = 8;
0147: private static final int GRANT = 9;
0148: private static final int INSERT = 10;
0149: private static final int REVOKE = 11;
0150: private static final int ROLLBACK = 12;
0151: private static final int SAVEPOINT = 13;
0152: private static final int SCRIPT = 14;
0153: private static final int SELECT = 15;
0154: private static final int SET = 16;
0155: private static final int SHUTDOWN = 17;
0156: private static final int UPDATE = 18;
0157: private static final int SEMICOLON = 19;
0158: private static final int ALTER = 20;
0159: private static final HsqlHashMap hCommands = new HsqlHashMap(37);
0160:
0161: static {
0162: hCommands.put("ALTER", new Integer(ALTER));
0163: hCommands.put("CALL", new Integer(CALL));
0164: hCommands.put("CHECKPOINT", new Integer(CHECKPOINT));
0165: hCommands.put("COMMIT", new Integer(COMMIT));
0166: hCommands.put("CONNECT", new Integer(CONNECT));
0167: hCommands.put("CREATE", new Integer(CREATE));
0168: hCommands.put("DELETE", new Integer(DELETE));
0169: hCommands.put("DISCONNECT", new Integer(DISCONNECT));
0170: hCommands.put("DROP", new Integer(DROP));
0171: hCommands.put("GRANT", new Integer(GRANT));
0172: hCommands.put("INSERT", new Integer(INSERT));
0173: hCommands.put("REVOKE", new Integer(REVOKE));
0174: hCommands.put("ROLLBACK", new Integer(ROLLBACK));
0175: hCommands.put("SAVEPOINT", new Integer(SAVEPOINT));
0176: hCommands.put("SCRIPT", new Integer(SCRIPT));
0177: hCommands.put("SELECT", new Integer(SELECT));
0178: hCommands.put("SET", new Integer(SET));
0179: hCommands.put("SHUTDOWN", new Integer(SHUTDOWN));
0180: hCommands.put("UPDATE", new Integer(UPDATE));
0181: hCommands.put(";", new Integer(SEMICOLON));
0182: }
0183:
0184: /**
0185: * Constructs a new Database object that mounts or creates the database
0186: * files specified by the supplied name.
0187: *
0188: * @param name the path to and common name shared by the database files
0189: * this Database uses
0190: * @exception SQLException if the specified path and common name
0191: * combination is illegal or unavailable, or the database files the
0192: * name resolves to are in use by another process
0193: */
0194: Database(String name) throws SQLException {
0195:
0196: if (Trace.TRACE) {
0197: Trace.trace();
0198: }
0199:
0200: sName = name;
0201:
0202: open();
0203: }
0204:
0205: /**
0206: * Opens the database. The database can be opened by the constructor,
0207: * or reopened by the close(int closemode) method during a
0208: * "shutdown compact".
0209: * @see close(int closemode)
0210: */
0211:
0212: // tony_lai@users 20020820
0213: private void open() throws SQLException {
0214:
0215: tTable = new HsqlArrayList();
0216: aAccess = new UserManager();
0217: cSession = new HsqlArrayList();
0218: hAlias = new HsqlHashMap();
0219: logger = new Logger();
0220: bReferentialIntegrity = true;
0221:
0222: Library.register(hAlias);
0223:
0224: dInfo = new DatabaseInformation(this , tTable, aAccess);
0225:
0226: boolean newdatabase = false;
0227:
0228: sysSession = new Session(this ,
0229: new User(null, null, true, null), true, false, 0);
0230:
0231: registerSession(sysSession);
0232:
0233: databaseProperties = new HsqlDatabaseProperties(sName);
0234:
0235: /* ============================= Initialization of replication ================================ */
0236:
0237: String group = System.getProperty("group");
0238: String props = System.getProperty("props");
0239: byte[] state = null;
0240: Address coord;
0241: long start, stop;
0242:
0243: // if either groups or props is set, we assume replication is to be used, otherwise we don't use it
0244: if (group != null || props != null) {
0245: replicate = true;
0246: }
0247:
0248: if (replicate) {
0249: if (props == null) {
0250: props = "UDP(mcast_port=45566;mcast_recv_buf_size=64000;mcast_send_buf_size=32000;"
0251: + "use_packet_handler=false;mcast_addr=228.8.8.8;loopback=true;"
0252: + "ucast_recv_buf_size=64000;ip_ttl=32;ucast_send_buf_size=32000):"
0253: + "PING(num_initial_members=3;timeout=2000):"
0254: +
0255: // "MERGE2(max_interval=10000;min_interval=5000):" + // no merge should take place !
0256: "FD_SOCK:"
0257: + "VERIFY_SUSPECT(timeout=1500):"
0258: + "pbcast.STABLE(desired_avg_gossip=20000):"
0259: + "pbcast.NAKACK(max_xmit_size=8192;retransmit_timeout=1200,2400,4800;gc_lag=50):"
0260: + "UNICAST(timeout=1000,1500,3000):"
0261: + "FRAG(up_thread=false;frag_size=8192;down_thread=false):"
0262: + "pbcast.GMS(shun=false;print_local_addr=true;join_timeout=5000;join_retry_timeout=2000):"
0263: + "pbcast.STATE_TRANSFER";
0264: }
0265: if (group == null) {
0266: group = "hsqldb-group";
0267: }
0268: }
0269:
0270: org.javagroups.log.Trace.init();
0271:
0272: System.out.println("Database(): group=\"" + group
0273: + "\"\nprops=\"" + props + "\"");
0274:
0275: if (replicate) {
0276: try {
0277: if (Trace.TRACE)
0278: System.out.println("Database(): creating channel");
0279: channel = new JChannel(props);
0280: channel.setOpt(Channel.GET_STATE_EVENTS, new Boolean(
0281: true));
0282: if (Trace.TRACE)
0283: System.out.println("Database(): connecting to "
0284: + group);
0285: channel.connect(group);
0286: if (Trace.TRACE)
0287: System.out.println("Database(): connecting to "
0288: + group + ": done");
0289: local_addr = channel.getLocalAddress();
0290: adapter = new PullPushAdapter(channel, this , this );
0291:
0292: org.javagroups.View v = channel.getView();
0293: Vector mbrs = v != null ? v.getMembers() : null;
0294: System.out.println("Database(): initial members="
0295: + mbrs);
0296:
0297: if (mbrs != null && mbrs.size() > 1) {
0298: coord = (Address) mbrs.firstElement();
0299: System.out
0300: .println("Database(): Trying to retrieve state from "
0301: + coord);
0302:
0303: state_promise.reset();
0304: start = System.currentTimeMillis();
0305: if (channel.getState(null, STATE_TIMEOUT) == false) // wait forever
0306: System.err
0307: .println("Database(): could not get the state (possibly the first member)");
0308: else {
0309: state = (byte[]) state_promise.getResult(0);
0310: stop = System.currentTimeMillis();
0311: if (state == null) {
0312: System.err
0313: .println("Database(): failed getting the state from "
0314: + coord);
0315: } else {
0316: // set the initial state of the DB:
0317: System.out
0318: .println("** Database(): state is "
0319: + state.length + " bytes\n"
0320: + "(state transfer took "
0321: + (stop - start) + "ms)");
0322:
0323: // 1. Rename the old database files (*.script and *.data) if they exist
0324: // so the next statement will create a fresh database
0325: renameExistingLogs();
0326:
0327: // 2. Create a fresh database. This will always set newdatabase to true
0328: newdatabase = logger.openLog(this ,
0329: sysSession, sName);
0330:
0331: // 3. Initialize the fresh DB with the state received
0332: try {
0333: // don't trigger multicasts when updating my own state. this would result in
0334: // existing members receiving the existing state once more, which would either
0335: // result in duplicate keys (for PKs) or other strange effects
0336: sending_disabled = true;
0337: logger
0338: .initializeDatabaseFromBuffer(state);
0339: } catch (SQLException ex) {
0340: throw ex;
0341: } finally {
0342: sending_disabled = false;
0343: }
0344: }
0345: }
0346: } else
0347: System.out
0348: .println("Database(): I am the initial member, will not try to retrieve state");
0349: } catch (Exception ex) {
0350: ex.printStackTrace();
0351: throw Trace.getError(Trace.DATABASE_IS_SHUTDOWN,
0352: "exception=" + ex);
0353: }
0354: }
0355:
0356: /* =========================== End Initialization of replication =============================== */
0357:
0358: if (sName.equals(".")) {
0359: newdatabase = true;
0360: } else {
0361: if (!logger.hasLog()) // create only if not yet created
0362: newdatabase = logger.openLog(this , sysSession, sName);
0363: }
0364:
0365: HsqlName.sysNumber = 0;
0366:
0367: Library.setSqlMonth(databaseProperties
0368: .isPropertyTrue("sql.month"));
0369: Parser.setEnforceSize(databaseProperties
0370: .isPropertyTrue("sql.enforce_size"));
0371: Column.setCompareInLocal(databaseProperties
0372: .isPropertyTrue("sql.compare_in_locale"));
0373: jdbcResultSet.setGetColumnName(databaseProperties
0374: .isPropertyTrue("jdbc.get_column_name"));
0375:
0376: Record.gcFrequency = databaseProperties.getIntegerProperty(
0377: "hsqldb.gc_interval", 0);
0378:
0379: if (newdatabase) {
0380: databaseProperties.setProperty("sql.strict_fk", true);
0381: execute("CREATE USER SA PASSWORD \"\" ADMIN", sysSession);
0382: }
0383:
0384: aAccess.grant("PUBLIC", "CLASS \"java.lang.Math\"",
0385: UserManager.ALL);
0386: aAccess.grant("PUBLIC", "CLASS \"org.hsqldb.Library\"",
0387: UserManager.ALL);
0388:
0389: }
0390:
0391: /** -------------------------- MessageListener interface -------------------------- **/
0392: public void receive(Message msg) {
0393: ReplicationData repl_data;
0394: byte[] buf = msg != null ? msg.getBuffer() : null;
0395: Address sender_addr;
0396: String statement = null;
0397:
0398: // System.out.println("Database.receive(): received " + printMessage(msg));
0399: if (buf == null) {
0400: System.err
0401: .println("Database.receive(): message buffer was empty");
0402: return;
0403: }
0404:
0405: try {
0406: repl_data = (ReplicationData) Util
0407: .objectFromByteBuffer(buf);
0408: if (local_addr != null
0409: && (sender_addr = repl_data.getSenderAddress()) != null) {
0410: if (local_addr.equals(sender_addr)) {
0411: // System.out.println("Database.receive(): received my own multicast, don't need to apply changes");
0412: return;
0413: }
0414:
0415: // apply the changes to the database
0416: statement = repl_data.getStatement();
0417: if (statement == null)
0418: System.err
0419: .println("Database.receive(): statement was null");
0420: else {
0421: System.out
0422: .println("Database.receive(): applying statement "
0423: + statement);
0424: execute(statement, sysSession, false); // don't replicate !
0425: }
0426: }
0427: } catch (Throwable ex) {
0428: System.err.println("Database.receive(): exception=" + ex);
0429: }
0430: }
0431:
0432: public Object getState() {
0433: byte[] retval;
0434: try {
0435: retval = Log.scriptToBuffer(this , true, sysSession);
0436: System.out
0437: .println("** getState(): state is " + retval != null ? (retval.length + " bytes")
0438: : null);
0439: return retval;
0440: } catch (Throwable ex) {
0441: System.err
0442: .println("Database.getState(): could not generate state, exception="
0443: + ex);
0444: return null;
0445: }
0446: }
0447:
0448: public void setState(Object state) {
0449: state_promise.setResult(state);
0450: }
0451:
0452: /** ---------------------- End of MessageListener interface ----------------------- **/
0453:
0454: /** -------------------------- MembershipListener interface -------------------------- **/
0455: public void viewAccepted(org.javagroups.View new_view) {
0456: System.out.println("Database.viewAccepted(): " + new_view);
0457: }
0458:
0459: /** Called when a member is suspected */
0460: public void suspect(Address suspected_mbr) {
0461:
0462: }
0463:
0464: /** Block sending and receiving of messages until viewAccepted() is called */
0465: public void block() {
0466: ;
0467: }
0468:
0469: /** ---------------------- End of MembershipListener interface ----------------------- **/
0470:
0471: String printMessage(Message msg) {
0472: byte[] data;
0473: ReplicationData d;
0474:
0475: if (msg == null)
0476: return null;
0477:
0478: if ((data = msg.getBuffer()) != null) {
0479: try {
0480: d = (ReplicationData) Util.objectFromByteBuffer(data);
0481: return d.toString();
0482: } catch (Exception ex) {
0483: System.err
0484: .println("Database.printMessage(): exception converting message: "
0485: + ex);
0486: }
0487: }
0488: return msg.toString();
0489: }
0490:
0491: /**
0492: * Retrieves this Database object's name, as know to this Database
0493: * object.
0494: *
0495: * @return this Database object's name
0496: */
0497: String getName() {
0498: return sName;
0499: }
0500:
0501: /**
0502: * Retrieves this Database object's properties.
0503: *
0504: * @return this Database object's properties object
0505: */
0506: HsqlDatabaseProperties getProperties() {
0507: return databaseProperties;
0508: }
0509:
0510: /**
0511: * isShutdown attribute getter.
0512: *
0513: * @return the value of this Database object's isShutdown attribute
0514: */
0515: boolean isShutdown() {
0516: return bShutdown;
0517: }
0518:
0519: /**
0520: * Constructs a new Session that operates within (is connected to) the
0521: * context of this Database object. <p>
0522: *
0523: * If successful, the new Session object initially operates on behalf of
0524: * the user specified by the supplied user name.
0525: *
0526: * @param username the name of the initial user of this session. The user
0527: * must already exist in this Database object.
0528: * @param password the password of the specified user. This must match
0529: * the password, as known to this Database object, of the specified
0530: * user
0531: * @return a new Session object that initially that initially operates on
0532: * behalf of the specified user
0533: * @throws SQLException if the specified user does not exist or a bad
0534: * password is specified
0535: */
0536: synchronized Session connect(String username, String password)
0537: throws SQLException {
0538:
0539: User user = aAccess.getUser(username.toUpperCase(), password
0540: .toUpperCase());
0541: int size = cSession.size();
0542: int id = size;
0543:
0544: for (int i = 0; i < size; i++) {
0545: if (cSession.get(i) == null) {
0546: id = i;
0547:
0548: break;
0549: }
0550: }
0551:
0552: Session session = new Session(this , user, true, bReadOnly, id);
0553:
0554: logger.writeToLog(session, "CONNECT USER " + username
0555: + " PASSWORD \"" + password + "\"");
0556: registerSession(session);
0557:
0558: return session;
0559: }
0560:
0561: /**
0562: * Binds the specified Session object into this Database object's active
0563: * session registry. This method is typically called from {@link
0564: * #connect} as the final step, when a successful connection has been
0565: * made.
0566: *
0567: * @param session the Session object to register
0568: */
0569: void registerSession(Session session) {
0570:
0571: int size = cSession.size();
0572: int id = session.getId();
0573:
0574: if (id >= size) {
0575: cSession.setSize(id + 1);
0576: }
0577:
0578: cSession.set(id, session);
0579: }
0580:
0581: /**
0582: * A specialized SQL statement executor, tailored for use by {@link
0583: * WebServerConnection}. Calling this method fully connects the specified
0584: * user, executes the specifed statement, and then disconects.
0585: *
0586: * @param user the name of the user for which to execute the specified
0587: * statement. The user must already exist in this Database object.
0588: * @param password the password of the specified user. This must match
0589: * the password, as known to this Database object, of the specified
0590: * user
0591: * @param statement the SQL statement to execute
0592: * @return the result of executing the specified statement, in a form
0593: * already suitable for transmitting as part of an HTTP response.
0594: */
0595: byte[] execute(String user, String password, String statement) {
0596:
0597: Result r = null;
0598:
0599: try {
0600: Session session = connect(user, password);
0601:
0602: r = execute(statement, session);
0603:
0604: execute("DISCONNECT", session);
0605:
0606: // fredt@users 20020221 - patch 513005 by sqlbob@users (RMP)
0607: } catch (SQLException e) {
0608: r = new Result(e.getMessage(), e.getErrorCode());
0609: } catch (Exception e) {
0610: r = new Result(e.getMessage(), Trace.GENERAL_ERROR);
0611: }
0612:
0613: try {
0614: return r.getBytes();
0615: } catch (Exception e) {
0616: return new byte[0];
0617: }
0618: }
0619:
0620: synchronized Result execute(String statement, Session session) {
0621: return execute(statement, session, true);
0622: }
0623:
0624: /**
0625: * The main SQL statement executor. <p>
0626: *
0627: * All requests to execute SQL statements against this Database object
0628: * eventually go through this method.
0629: *
0630: * @param statement the SQL statement to execute
0631: * @param session an object representing a connected user and a
0632: * collection of session state attributes
0633: * @param replicate If set the change will be replicated to other replicas. This is used
0634: * by the internal replication
0635: * @return the result of executing the specified statement, in a form
0636: * suitable for either wrapping in a local ResultSet object or for
0637: * transmitting to a remote client via the native HSQLDB protocol
0638: */
0639: synchronized Result execute(String statement, Session session,
0640: boolean replicate) {
0641: boolean send_update = false;
0642:
0643: if (Record.gcFrequency != 0
0644: && Record.memoryRecords > Record.gcFrequency) {
0645: System.gc();
0646: Trace.printSystemOut("gc at " + Record.memoryRecords);
0647:
0648: Record.memoryRecords = 0;
0649: }
0650:
0651: if (Trace.TRACE) {
0652: Trace.trace(statement);
0653: }
0654:
0655: // System.out.println("** " + statement);
0656:
0657: Result rResult = null;
0658:
0659: try {
0660: Tokenizer c = new Tokenizer(statement);
0661: Parser p = new Parser(this , c, session);
0662:
0663: logger.cleanUp();
0664:
0665: if (Trace.DOASSERT) {
0666: Trace.doAssert(!session.isNestedTransaction());
0667: }
0668:
0669: Trace.check(session != null, Trace.ACCESS_IS_DENIED);
0670: Trace.check(!bShutdown, Trace.DATABASE_IS_SHUTDOWN);
0671:
0672: while (true) {
0673: c.setPartMarker();
0674:
0675: // fredt@users 20020221 - patch 513005 by sqlbob@users (RMP)
0676: session.setScripting(false);
0677:
0678: String sToken = c.getString();
0679:
0680: if (sToken.length() == 0) {
0681: break;
0682: }
0683:
0684: // boucherb@users 20020306 - patch 1.7.0 - use lookup for tokens
0685: Integer command = (Integer) hCommands.get(sToken);
0686:
0687: if (command == null) {
0688: throw Trace.error(Trace.UNEXPECTED_TOKEN, sToken);
0689: }
0690:
0691: int cmd = command.intValue();
0692:
0693: switch (cmd) {
0694:
0695: case SELECT:
0696: rResult = p.processSelect();
0697: break;
0698:
0699: case INSERT:
0700: rResult = p.processInsert();
0701: send_update = true;
0702: break;
0703:
0704: case UPDATE:
0705: rResult = p.processUpdate();
0706: send_update = true;
0707: break;
0708:
0709: case DELETE:
0710: rResult = p.processDelete();
0711: send_update = true;
0712: break;
0713:
0714: case CALL:
0715: rResult = p.processCall();
0716: send_update = true;
0717: break;
0718:
0719: case SET:
0720: rResult = processSet(c, session);
0721: send_update = true;
0722: break;
0723:
0724: case COMMIT:
0725: rResult = processCommit(c, session);
0726:
0727: session.setScripting(true);
0728: send_update = true;
0729: break;
0730:
0731: case ROLLBACK:
0732: rResult = processRollback(c, session);
0733:
0734: session.setScripting(true);
0735: send_update = true;
0736: break;
0737:
0738: case SAVEPOINT:
0739: rResult = processSavepoint(c, session);
0740:
0741: session.setScripting(true);
0742: send_update = true;
0743: break;
0744:
0745: case CREATE:
0746: rResult = processCreate(c, session);
0747: send_update = true;
0748: break;
0749:
0750: case ALTER:
0751: rResult = processAlter(c, session);
0752: send_update = true;
0753: break;
0754:
0755: case DROP:
0756: rResult = processDrop(c, session);
0757: send_update = true;
0758: break;
0759:
0760: case GRANT:
0761: rResult = processGrantOrRevoke(c, session, true);
0762: send_update = true;
0763: break;
0764:
0765: case REVOKE:
0766: rResult = processGrantOrRevoke(c, session, false);
0767: send_update = true;
0768: break;
0769:
0770: case CONNECT:
0771: rResult = processConnect(c, session);
0772: break;
0773:
0774: case DISCONNECT:
0775: rResult = processDisconnect(session);
0776: break;
0777:
0778: case SCRIPT:
0779: rResult = processScript(c, session);
0780: break;
0781:
0782: case SHUTDOWN:
0783: rResult = processShutdown(c, session);
0784: break;
0785:
0786: case CHECKPOINT:
0787: rResult = processCheckpoint(session);
0788: send_update = true;
0789: break;
0790:
0791: case SEMICOLON:
0792: break;
0793: }
0794:
0795: if (session.getScripting()) {
0796: logger.writeToLog(session, c.getLastPart());
0797: }
0798:
0799: if (replicate && send_update && channel != null
0800: && !sending_disabled) {
0801: try {
0802: ReplicationData repl_data;
0803: repl_data = new ReplicationData(
0804: ReplicationData.UPDATE, local_addr, c
0805: .getLastPart());
0806: channel
0807: .send(new Message(null, null, repl_data));
0808: } catch (Throwable ex) {
0809: System.err.println("Database(): " + ex);
0810: }
0811: }
0812: }
0813: } catch (SQLException e) {
0814:
0815: // e.printStackTrace();
0816: // fredt@users 20020221 - patch 513005 by sqlbob@users (RMP)
0817: // tony_lai@users 20020820 - patch 595073
0818: // rResult = new Result(Trace.getMessage(e) + " in statement ["
0819: rResult = new Result(e.getMessage() + " in statement ["
0820: + statement + "]", e.getErrorCode());
0821: } catch (Exception e) {
0822: e.printStackTrace();
0823:
0824: String s = Trace.getMessage(Trace.GENERAL_ERROR) + " " + e;
0825:
0826: rResult = new Result(s + " in statement [" + statement
0827: + "]", Trace.GENERAL_ERROR);
0828: } catch (java.lang.OutOfMemoryError e) {
0829: e.printStackTrace();
0830:
0831: rResult = new Result("out of memory", Trace.GENERAL_ERROR);
0832: }
0833:
0834: return rResult == null ? new Result() : rResult;
0835: }
0836:
0837: /**
0838: * Puts this Database object in global read-only mode. That is, after
0839: * this call, all existing and future sessions are limited to read-only
0840: * transactions. Any following attempts to update the state of the
0841: * database will result in throwing a SQLException.
0842: */
0843: void setReadOnly() {
0844: bReadOnly = true;
0845: }
0846:
0847: /**
0848: * Retrieves a HsqlArrayList containing references to all registered non-system
0849: * tables and views. This includes all tables and views registered with
0850: * this Database object via a call to {@link #linkTable linkTable}.
0851: *
0852: * @return a HsqlArrayList of all registered non-system tables and views
0853: */
0854: HsqlArrayList getTables() {
0855: return tTable;
0856: }
0857:
0858: /**
0859: * Retrieves the UserManager object for this Database.
0860: *
0861: * @return UserManager object
0862: */
0863: UserManager getUserManager() {
0864: return aAccess;
0865: }
0866:
0867: /**
0868: * isReferentialIntegrity attribute setter.
0869: *
0870: * @param ref if true, this Database object enforces referential
0871: * integrity, else not
0872: */
0873: void setReferentialIntegrity(boolean ref) {
0874: bReferentialIntegrity = ref;
0875: }
0876:
0877: /**
0878: * isReferentialIntegrity attribute getter.
0879: *
0880: * @return indicates whether this Database object is currently enforcing
0881: * referential integrity
0882: */
0883: boolean isReferentialIntegrity() {
0884: return bReferentialIntegrity;
0885: }
0886:
0887: /**
0888: * Retrieves a map from Java method-call name aliases to the
0889: * fully-qualified names of the Java methods themsleves.
0890: *
0891: * @return a map in the form of a HsqlHashMap
0892: */
0893: HsqlHashMap getAlias() {
0894: return hAlias;
0895: }
0896:
0897: /**
0898: * Retieves a Java method's fully qualified name, given a String that is
0899: * supposedly an alias for it. <p>
0900: *
0901: * This is somewhat of a misnomer, since it is not an alias that is being
0902: * retrieved, but rather what the supplied alias maps to. If the
0903: * specified alias does not map to any registered Java method
0904: * fully-qualified name, then the specified String itself is returned.
0905: *
0906: * @param s a call name alias that supposedly maps to a registered Java
0907: * method
0908: * @return a Java method fully-qualified name, or null if no method is
0909: * registered with the given alias
0910: */
0911: String getAlias(String s) {
0912:
0913: String alias = (String) hAlias.get(s);
0914:
0915: return (alias == null) ? s : alias;
0916: }
0917:
0918: // fredt@users 20020221 - patch 513005 by sqlbob@users (RMP)
0919: // temp tables should be accessed by the owner and not scripted in the log
0920:
0921: /**
0922: * Retrieves the specified user defined table or view visible within the
0923: * context of the specified Session, or any system table of the given
0924: * name. This excludes any temp tables created in different Sessions.
0925: *
0926: * @param name of the table or view to retrieve
0927: * @param session the Session within which to search for user tables
0928: * @return the user table or view, or system table
0929: * @throws SQLException if there is no such table or view
0930: */
0931: Table getTable(String name, Session session) throws SQLException {
0932:
0933: Table t = findUserTable(name, session);
0934:
0935: if (t == null) {
0936: t = dInfo.getSystemTable(name, session);
0937: }
0938:
0939: if (t == null) {
0940: throw Trace.error(Trace.TABLE_NOT_FOUND, name);
0941: }
0942:
0943: return t;
0944: }
0945:
0946: /**
0947: * get a user
0948: *
0949: * @param name
0950: * @param session
0951: * @return
0952: * @throws SQLException
0953: */
0954: Table getUserTable(String name, Session session)
0955: throws SQLException {
0956:
0957: Table t = findUserTable(name, session);
0958:
0959: if (t == null) {
0960: throw Trace.error(Trace.TABLE_NOT_FOUND, name);
0961: }
0962:
0963: return t;
0964: }
0965:
0966: Table getUserTable(String name) throws SQLException {
0967:
0968: Table t = findUserTable(name);
0969:
0970: if (t == null) {
0971: throw Trace.error(Trace.TABLE_NOT_FOUND, name);
0972: }
0973:
0974: return t;
0975: }
0976:
0977: Table findUserTable(String name) {
0978:
0979: for (int i = 0, tsize = tTable.size(); i < tsize; i++) {
0980: Table t = (Table) tTable.get(i);
0981:
0982: if (t.equals(name)) {
0983: return t;
0984: }
0985: }
0986:
0987: return null;
0988: }
0989:
0990: // fredt@users 20020221 - patch 513005 by sqlbob@users (RMP)
0991: Table findUserTable(String name, Session session) {
0992:
0993: for (int i = 0, tsize = tTable.size(); i < tsize; i++) {
0994: Table t = (Table) tTable.get(i);
0995:
0996: if (t.equals(name, session)) {
0997: return t;
0998: }
0999: }
1000:
1001: return null;
1002: }
1003:
1004: /**
1005: * Generates a SQL script containing all or part of the SQL statements
1006: * required to recreate the current state of this Database object.
1007: *
1008: * @param drop if true, include drop statements for each droppable
1009: * database object
1010: * @param insert if true, include the insert statements required to
1011: * populate each of this Database object's memory tables to match
1012: * their current state.
1013: * @param cached if true, include the insert statement required to
1014: * populate each of this Database object's CACHED tables to match
1015: * their current state.
1016: * @param session the Session in which to generate the requested SQL
1017: * script
1018: * @return A Result object consisting of one VARCHAR column with a row
1019: * for each statement in the generated script
1020: * @throws SQLException if the specified Session's currently connected
1021: * User does not have the right to call this method or there is some
1022: * problem generating the result
1023: */
1024: Result getScript(boolean drop, boolean insert, boolean cached,
1025: Session session) throws SQLException {
1026: return DatabaseScript.getScript(this , drop, insert, cached,
1027: session);
1028: }
1029:
1030: /**
1031: * Attempts to register the specified table or view with this Database
1032: * object.
1033: *
1034: * @param t the table of view to register
1035: * @throws SQLException if there is a problem
1036: */
1037: void linkTable(Table t) throws SQLException {
1038: tTable.add(t);
1039: }
1040:
1041: /**
1042: * isIgnoreCase attribute getter.
1043: *
1044: * @return the value of this Database object's isIgnoreCase attribute
1045: */
1046: boolean isIgnoreCase() {
1047: return bIgnoreCase;
1048: }
1049:
1050: /**
1051: * Responsible for parsing and executing the SCRIPT SQL statement
1052: *
1053: * @param c the tokenized representation of the statement being processed
1054: * @param session
1055: * @return
1056: * @throws SQLException
1057: */
1058: private Result processScript(Tokenizer c, Session session)
1059: throws SQLException {
1060:
1061: String sToken = c.getString();
1062:
1063: if (c.wasValue()) {
1064: sToken = (String) c.getAsValue();
1065:
1066: Log.scriptToFile(this , sToken, true, session);
1067:
1068: return new Result();
1069: } else {
1070: c.back();
1071:
1072: // fredt@users - patch 1.7.0 - no DROP TABLE statements with SCRIPT command
1073: // try to script all but drop, insert; but no positions for cached tables
1074: return getScript(false, true, false, session);
1075: }
1076: }
1077:
1078: /**
1079: * Responsible for handling the parse and execution of CREATE SQL
1080: * statements.
1081: *
1082: * @param c the tokenized representation of the statement being processed
1083: * @param session
1084: * @return
1085: * @throws SQLException
1086: */
1087: private Result processCreate(Tokenizer c, Session session)
1088: throws SQLException {
1089:
1090: session.checkReadWrite();
1091: session.checkAdmin();
1092:
1093: String sToken = c.getString();
1094:
1095: // fredt@users 20020221 - patch 513005 by sqlbob@users (RMP)
1096: boolean isTemp = false;
1097:
1098: if (sToken.equals("TEMP")) {
1099: isTemp = true;
1100: sToken = c.getString();
1101:
1102: Trace.check(
1103: sToken.equals("TABLE") || sToken.equals("MEMORY")
1104: || sToken.equals("TEXT"),
1105: Trace.UNEXPECTED_TOKEN, sToken);
1106: session.setScripting(false);
1107: } else {
1108: session.checkReadWrite();
1109: session.checkAdmin();
1110: session.setScripting(true);
1111: }
1112:
1113: // fredt@users 20020221 - patch 513005 by sqlbob@users (RMP)
1114: if (sToken.equals("TABLE")) {
1115: int tableType = isTemp ? Table.TEMP_TABLE
1116: : Table.MEMORY_TABLE;
1117:
1118: processCreateTable(c, session, tableType);
1119: } else if (sToken.equals("MEMORY")) {
1120: c.getThis("TABLE");
1121:
1122: int tableType = isTemp ? Table.TEMP_TABLE
1123: : Table.MEMORY_TABLE;
1124:
1125: processCreateTable(c, session, tableType);
1126: } else if (sToken.equals("CACHED")) {
1127: c.getThis("TABLE");
1128: processCreateTable(c, session, Table.CACHED_TABLE);
1129: } else if (sToken.equals("TEXT")) {
1130: c.getThis("TABLE");
1131:
1132: int tableType = isTemp ? Table.TEMP_TEXT_TABLE
1133: : Table.TEXT_TABLE;
1134:
1135: processCreateTable(c, session, tableType);
1136: } else if (sToken.equals("VIEW")) {
1137: processCreateView(c, session);
1138: } else if (sToken.equals("TRIGGER")) {
1139: processCreateTrigger(c, session);
1140: } else if (sToken.equals("USER")) {
1141: String u = c.getStringToken();
1142:
1143: c.getThis("PASSWORD");
1144:
1145: String p = c.getStringToken();
1146: boolean admin;
1147:
1148: if (c.getString().equals("ADMIN")) {
1149: admin = true;
1150: } else {
1151: admin = false;
1152: }
1153:
1154: aAccess.createUser(u, p, admin);
1155: } else if (sToken.equals("ALIAS")) {
1156: String name = c.getString();
1157:
1158: sToken = c.getString();
1159:
1160: Trace.check(sToken.equals("FOR"), Trace.UNEXPECTED_TOKEN,
1161: sToken);
1162:
1163: sToken = c.getString();
1164:
1165: // fredt@users 20010701 - patch 1.6.1 by fredt - open <1.60 db files
1166: // convert org.hsql.Library aliases from versions < 1.60 to org.hsqldb
1167: // fredt@users 20020221 - patch 513005 by sqlbob@users (RMP) - ABS function
1168: if (sToken.startsWith("org.hsql.Library.")) {
1169: sToken = "org.hsqldb.Library."
1170: + sToken
1171: .substring("org.hsql.Library.".length());
1172: } else if (sToken.equals("java.lang.Math.abs")) {
1173: sToken = "org.hsqldb.Library.abs";
1174: }
1175:
1176: hAlias.put(name, sToken);
1177: } else {
1178: boolean unique = false;
1179:
1180: if (sToken.equals("UNIQUE")) {
1181: unique = true;
1182: sToken = c.getString();
1183: }
1184:
1185: if (!sToken.equals("INDEX")) {
1186: throw Trace.error(Trace.UNEXPECTED_TOKEN, sToken);
1187: }
1188:
1189: String name = c.getName();
1190: boolean isnamequoted = c.wasQuotedIdentifier();
1191:
1192: c.getThis("ON");
1193:
1194: Table t = getTable(c.getName(), session);
1195:
1196: addIndexOn(c, session, name, isnamequoted, t, unique);
1197: }
1198:
1199: return new Result();
1200: }
1201:
1202: /**
1203: * Process a bracketed column list as used in the declaration of SQL
1204: * CONSTRAINTS and return an array containing the indexes of the columns
1205: * within the table.
1206: *
1207: * @param c
1208: * @param t table that contains the columns
1209: * @return
1210: * @throws SQLException if a column is not found or is duplicated
1211: */
1212: private int[] processColumnList(Tokenizer c, Table t)
1213: throws SQLException {
1214:
1215: HsqlArrayList v = new HsqlArrayList();
1216: HsqlHashMap h = new HsqlHashMap();
1217:
1218: c.getThis("(");
1219:
1220: while (true) {
1221: String colname = c.getName();
1222:
1223: v.add(colname);
1224: h.put(colname, colname);
1225:
1226: String sToken = c.getString();
1227:
1228: if (sToken.equals(")")) {
1229: break;
1230: }
1231:
1232: if (!sToken.equals(",")) {
1233: throw Trace.error(Trace.UNEXPECTED_TOKEN, sToken);
1234: }
1235: }
1236:
1237: int s = v.size();
1238:
1239: if (s != h.size()) {
1240: throw Trace.error(Trace.COLUMN_ALREADY_EXISTS,
1241: "duplicate column in list");
1242: }
1243:
1244: int col[] = new int[s];
1245:
1246: for (int i = 0; i < s; i++) {
1247: col[i] = t.getColumnNr((String) v.get(i));
1248: }
1249:
1250: return col;
1251: }
1252:
1253: /**
1254: * Indexes defined in DDL scripts are handled by this method. If the
1255: * name of an existing index begins with "SYS_", the name is changed to
1256: * begin with "USER_". The name should be unique within the database.
1257: * For compatibility with old database, non-unique names are modified
1258: * and assigned a new name<br>
1259: * (fredt@users)
1260: *
1261: * @param c
1262: * @param session
1263: * @param name
1264: * @param t
1265: * @param unique
1266: * @param namequoted The feature to be added to the IndexOn attribute
1267: * @throws SQLException
1268: */
1269: private void addIndexOn(Tokenizer c, Session session, String name,
1270: boolean namequoted, Table t, boolean unique)
1271: throws SQLException {
1272:
1273: HsqlName indexname;
1274: int col[] = processColumnList(c, t);
1275:
1276: if (HsqlName.isReservedName(name)) {
1277: indexname = HsqlName.makeAutoName("USER", name);
1278: } else {
1279: indexname = new HsqlName(name, namequoted);
1280: }
1281:
1282: // fredt@users - to check further - this is confined only to old scripts
1283: // rename duplicate indexes
1284: /*
1285: if (findIndex(name) != null && session == sysSession
1286: && databaseProperties.getProperty("hsqldb.compatible_version")
1287: .equals("1.6.0")) {
1288: indexname = HsqlName.makeAutoName("USER", name);
1289: name = indexname.name;
1290: }
1291: */
1292: if (findIndex(name) != null) {
1293: throw Trace.error(Trace.INDEX_ALREADY_EXISTS);
1294: }
1295:
1296: session.commit();
1297: session.setScripting(!t.isTemp());
1298:
1299: TableWorks tw = new TableWorks(t);
1300:
1301: tw.createIndex(col, indexname, unique);
1302: }
1303:
1304: /**
1305: * Finds an index with the given name in the whole database.
1306: *
1307: * @param name Description of the Parameter
1308: * @return Description of the Return Value
1309: */
1310: private Index findIndex(String name) {
1311:
1312: Table t = findTableForIndex(name);
1313:
1314: if (t == null) {
1315: return null;
1316: } else {
1317: return t.getIndex(name);
1318: }
1319: }
1320:
1321: /**
1322: * Finds the table that has an index with the given name in the
1323: * whole database.
1324: *
1325: * @param name Description of the Parameter
1326: * @return Description of the Return Value
1327: */
1328: private Table findTableForIndex(String name) {
1329:
1330: for (int i = 0, tsize = tTable.size(); i < tsize; i++) {
1331: Table t = (Table) tTable.get(i);
1332:
1333: if (t.getIndex(name) != null) {
1334: return t;
1335: }
1336: }
1337:
1338: return null;
1339: }
1340:
1341: /**
1342: * Retrieves the index of a table or view in the HsqlArrayList that contains
1343: * these objects for a Database.
1344: *
1345: * @param table the Table object
1346: * @return the index of the specified table or view, or -1 if not found
1347: */
1348: int getTableIndex(Table table) {
1349:
1350: for (int i = 0, tsize = tTable.size(); i < tsize; i++) {
1351: Table t = (Table) tTable.get(i);
1352:
1353: if (t == table) {
1354: return i;
1355: }
1356: }
1357:
1358: return -1;
1359: }
1360:
1361: /**
1362: * Responsible for handling the execution of CREATE TRIGGER SQL
1363: * statements. <p>
1364: *
1365: * typical sql is: CREATE TRIGGER tr1 AFTER INSERT ON tab1 CALL "pkg.cls"
1366: *
1367: * @param c the tokenized representation of the statement being processed
1368: * @param session
1369: * @throws SQLException
1370: */
1371: private void processCreateTrigger(Tokenizer c, Session session)
1372: throws SQLException {
1373:
1374: Table t;
1375: boolean bForEach = false;
1376: boolean bNowait = false;
1377: int nQueueSize = TriggerDef.getDefaultQueueSize();
1378: String sTrigName = c.getName();
1379: String sWhen = c.getString();
1380: String sOper = c.getString();
1381:
1382: c.getThis("ON");
1383:
1384: String sTableName = c.getString();
1385:
1386: t = getTable(sTableName, session);
1387:
1388: if (t.isView()) {
1389: throw Trace.error(Trace.NOT_A_TABLE);
1390: }
1391:
1392: // fredt@users 20020221 - patch 513005 by sqlbob@users (RMP)
1393: session.setScripting(!t.isTemp());
1394:
1395: // "FOR EACH ROW" or "CALL"
1396: String tok = c.getString();
1397:
1398: if (tok.equals("FOR")) {
1399: tok = c.getString();
1400:
1401: if (tok.equals("EACH")) {
1402: tok = c.getString();
1403:
1404: if (tok.equals("ROW")) {
1405: bForEach = true;
1406: tok = c.getString(); // should be 'NOWAIT' or 'QUEUE' or 'CALL'
1407: } else {
1408: throw Trace.error(Trace.UNEXPECTED_END_OF_COMMAND,
1409: tok);
1410: }
1411: } else {
1412: throw Trace.error(Trace.UNEXPECTED_END_OF_COMMAND, tok);
1413: }
1414: }
1415:
1416: if (tok.equals("NOWAIT")) {
1417: bNowait = true;
1418: tok = c.getString(); // should be 'CALL' or 'QUEUE'
1419: }
1420:
1421: if (tok.equals("QUEUE")) {
1422: nQueueSize = Integer.parseInt(c.getString());
1423: tok = c.getString(); // should be 'CALL'
1424: }
1425:
1426: if (!tok.equals("CALL")) {
1427: throw Trace.error(Trace.UNEXPECTED_END_OF_COMMAND, tok);
1428: }
1429:
1430: String sClassName = c.getString(); // double quotes have been stripped
1431: TriggerDef td;
1432: Trigger o;
1433:
1434: try {
1435: Class cl = Class.forName(sClassName); // dynamically load class
1436:
1437: o = (Trigger) cl.newInstance(); // dynamically instantiate it
1438: td = new TriggerDef(sTrigName, sWhen, sOper, bForEach, t,
1439: o, "\"" + sClassName + "\"", bNowait, nQueueSize);
1440:
1441: if (td.isValid()) {
1442: t.addTrigger(td);
1443: td.start(); // start the trigger thread
1444: } else {
1445: String msg = "Error in parsing trigger command ";
1446:
1447: throw Trace.error(Trace.UNEXPECTED_TOKEN, msg);
1448: }
1449: } catch (Exception e) {
1450: String msg = "Exception in loading trigger class "
1451: + e.getMessage();
1452:
1453: throw Trace.error(Trace.UNKNOWN_FUNCTION, msg);
1454: }
1455: }
1456:
1457: /**
1458: * Responsible for handling the creation of table columns during the
1459: * process of executing CREATE TABLE statements.
1460: *
1461: * @param c the tokenized representation of the statement being processed
1462: * @param t target table
1463: * @return
1464: * @throws SQLException
1465: */
1466: private Column processCreateColumn(Tokenizer c, Table t)
1467: throws SQLException {
1468:
1469: boolean identity = false;
1470: boolean primarykey = false;
1471: String sToken = c.getString();
1472: String sColumn = sToken;
1473: boolean isnamequoted = c.wasQuotedIdentifier();
1474: String typestring = c.getString();
1475: int iType = Column.getTypeNr(typestring);
1476:
1477: Trace.check(!sColumn.equals(Table.DEFAULT_PK),
1478: Trace.COLUMN_ALREADY_EXISTS, sColumn);
1479:
1480: if (typestring.equals("IDENTITY")) {
1481: identity = true;
1482: primarykey = true;
1483: }
1484:
1485: if (iType == Types.VARCHAR && bIgnoreCase) {
1486: iType = Column.VARCHAR_IGNORECASE;
1487: }
1488:
1489: sToken = c.getString();
1490:
1491: if (iType == Types.DOUBLE && sToken.equals("PRECISION")) {
1492: sToken = c.getString();
1493: }
1494:
1495: // fredt@users 20020130 - patch 491987 by jimbag@users
1496: String sLen = "";
1497:
1498: if (sToken.equals("(")) {
1499:
1500: // read length
1501: do {
1502: sToken = c.getString();
1503:
1504: if (!sToken.equals(")")) {
1505: sLen += sToken;
1506: }
1507: } while (!sToken.equals(")"));
1508:
1509: sToken = c.getString();
1510: }
1511:
1512: int iLen = 0;
1513: int iScale = 0;
1514:
1515: // see if we have a scale specified
1516: int index;
1517:
1518: if ((index = sLen.indexOf(",")) != -1) {
1519: String sScale = sLen.substring(index + 1, sLen.length());
1520:
1521: sLen = sLen.substring(0, index);
1522:
1523: try {
1524: iScale = Integer.parseInt(sScale.trim());
1525: } catch (NumberFormatException ne) {
1526: throw Trace.error(Trace.UNEXPECTED_TOKEN, sLen);
1527: }
1528: }
1529:
1530: // convert the length
1531: if (sLen.trim().length() > 0) {
1532: try {
1533: iLen = Integer.parseInt(sLen.trim());
1534: } catch (NumberFormatException ne) {
1535: throw Trace.error(Trace.UNEXPECTED_TOKEN, sLen);
1536: }
1537: }
1538:
1539: String defaultvalue = null;
1540:
1541: if (sToken.equals("DEFAULT")) {
1542: String s = c.getString();
1543:
1544: if (c.wasValue() && iType != Types.BINARY
1545: && iType != Types.OTHER) {
1546: Object sv = c.getAsValue();
1547:
1548: if (sv != null) {
1549: defaultvalue = String.valueOf(sv);
1550:
1551: try {
1552: Column.convertObject(defaultvalue, iType);
1553: } catch (Exception e) {
1554: throw Trace.error(Trace.WRONG_DEFAULT_CLAUSE,
1555: defaultvalue);
1556: }
1557:
1558: String testdefault = (String) Parser.enforceSize(
1559: defaultvalue, iType, iLen, false);
1560:
1561: if (defaultvalue.equals(testdefault) == false) {
1562: throw Trace.error(Trace.WRONG_DEFAULT_CLAUSE,
1563: defaultvalue);
1564: }
1565: }
1566: } else {
1567: throw Trace.error(Trace.WRONG_DEFAULT_CLAUSE, s);
1568: }
1569:
1570: sToken = c.getString();
1571: }
1572:
1573: boolean nullable = true;
1574:
1575: if (sToken.equals("NULL")) {
1576: sToken = c.getString();
1577: } else if (sToken.equals("NOT")) {
1578: c.getThis("NULL");
1579:
1580: nullable = false;
1581: sToken = c.getString();
1582: }
1583:
1584: if (sToken.equals("IDENTITY")) {
1585: identity = true;
1586: sToken = c.getString();
1587: primarykey = true;
1588: }
1589:
1590: if (sToken.equals("PRIMARY")) {
1591: c.getThis("KEY");
1592:
1593: primarykey = true;
1594: } else {
1595: c.back();
1596: }
1597:
1598: return new Column(new HsqlName(sColumn, isnamequoted),
1599: nullable, iType, iLen, iScale, identity, primarykey,
1600: defaultvalue);
1601: }
1602:
1603: // fredt@users 20020225 - patch 509002 by fredt
1604: // temporary attributes for constraints used in processCreateTable()
1605:
1606: /**
1607: * temporary attributes for constraints used in processCreateTable()
1608: */
1609: private class TempConstraint {
1610:
1611: HsqlName name;
1612: int[] localCol;
1613: Table expTable;
1614: int[] expCol;
1615: int type;
1616: boolean cascade;
1617:
1618: TempConstraint(HsqlName name, int[] localCol, Table expTable,
1619: int[] expCol, int type, boolean cascade) {
1620:
1621: this .name = name;
1622: this .type = type;
1623: this .localCol = localCol;
1624: this .expTable = expTable;
1625: this .expCol = expCol;
1626: this .cascade = cascade;
1627: }
1628: }
1629:
1630: // fredt@users 20020225 - patch 509002 by fredt
1631: // process constraints after parsing to include primary keys defined as
1632: // constraints
1633: // fredt@users 20020225 - patch 489777 by fredt
1634: // better error trapping
1635: // fredt@users 20020221 - patch 513005 by sqlbob@users (RMP)
1636:
1637: /**
1638: * Responsible for handling the execution CREATE TABLE SQL statements.
1639: *
1640: * @param c
1641: * @param session
1642: * @param type Description of the Parameter
1643: * @throws SQLException
1644: */
1645: private void processCreateTable(Tokenizer c, Session session,
1646: int type) throws SQLException {
1647:
1648: Table t;
1649: String sToken = c.getName();
1650: boolean isnamequoted = c.wasQuotedIdentifier();
1651:
1652: if (DatabaseInformation.isSystemTable(sToken)
1653: || findUserTable(sToken, session) != null) {
1654: throw Trace.error(Trace.TABLE_ALREADY_EXISTS, sToken);
1655: }
1656:
1657: if (type == Table.TEMP_TEXT_TABLE || type == Table.TEXT_TABLE) {
1658: t = new TextTable(this , new HsqlName(sToken, isnamequoted),
1659: type, session);
1660: } else {
1661: t = new Table(this , new HsqlName(sToken, isnamequoted),
1662: type, session);
1663: }
1664:
1665: c.getThis("(");
1666:
1667: int[] primarykeycolumn = null;
1668: int column = 0;
1669: boolean constraint = false;
1670:
1671: while (true) {
1672: sToken = c.getString();
1673: isnamequoted = c.wasQuotedIdentifier();
1674:
1675: // fredt@users 20020225 - comment
1676: // we can check here for reserved words used with quotes as column names
1677: if (sToken.equals("CONSTRAINT") || sToken.equals("PRIMARY")
1678: || sToken.equals("FOREIGN")
1679: || sToken.equals("UNIQUE")) {
1680: c.back();
1681:
1682: constraint = true;
1683:
1684: break;
1685: }
1686:
1687: c.back();
1688:
1689: Column newcolumn = processCreateColumn(c, t);
1690:
1691: t.addColumn(newcolumn);
1692:
1693: if (newcolumn.isPrimaryKey()) {
1694: Trace.check(primarykeycolumn == null,
1695: Trace.SECOND_PRIMARY_KEY, "column " + column);
1696:
1697: primarykeycolumn = new int[] { column };
1698: }
1699:
1700: sToken = c.getString();
1701:
1702: if (sToken.equals(")")) {
1703: break;
1704: }
1705:
1706: if (!sToken.equals(",")) {
1707: throw Trace.error(Trace.UNEXPECTED_TOKEN, sToken);
1708: }
1709:
1710: column++;
1711: }
1712:
1713: try {
1714:
1715: // fredt@users 20020225 - comment
1716: // HSQLDB relies on primary index to be the first one defined
1717: // and needs original or system added primary key before any non-unique index
1718: // is created
1719: HsqlArrayList tempConstraints = new HsqlArrayList();
1720: TempConstraint tempConst = new TempConstraint(null,
1721: primarykeycolumn, null, null, Constraint.MAIN,
1722: false);
1723:
1724: // tony_lai@users 20020820 - patch 595099
1725: HsqlName pkName = null;
1726:
1727: tempConstraints.add(tempConst);
1728:
1729: if (constraint) {
1730: int i = 0;
1731:
1732: while (true) {
1733: sToken = c.getString();
1734:
1735: HsqlName cname = null;
1736:
1737: i++;
1738:
1739: if (sToken.equals("CONSTRAINT")) {
1740: cname = new HsqlName(c.getName(), c
1741: .wasQuotedIdentifier());
1742: sToken = c.getString();
1743: }
1744:
1745: if (sToken.equals("PRIMARY")) {
1746: c.getThis("KEY");
1747:
1748: // tony_lai@users 20020820 - patch 595099
1749: pkName = cname;
1750:
1751: int col[] = processColumnList(c, t);
1752: TempConstraint mainConst = (TempConstraint) tempConstraints
1753: .get(0);
1754:
1755: Trace.check(mainConst.localCol == null,
1756: Trace.SECOND_PRIMARY_KEY);
1757:
1758: mainConst.localCol = col;
1759: } else if (sToken.equals("UNIQUE")) {
1760: int col[] = processColumnList(c, t);
1761:
1762: if (cname == null) {
1763: cname = HsqlName.makeAutoName("CT");
1764: }
1765:
1766: tempConst = new TempConstraint(cname, col,
1767: null, null, Constraint.UNIQUE, false);
1768:
1769: tempConstraints.add(tempConst);
1770: } else if (sToken.equals("FOREIGN")) {
1771: c.getThis("KEY");
1772:
1773: tempConst = processCreateFK(c, session, t,
1774: cname);
1775:
1776: if (tempConst.expCol == null) {
1777: TempConstraint mainConst = (TempConstraint) tempConstraints
1778: .get(0);
1779:
1780: tempConst.expCol = mainConst.localCol;
1781:
1782: if (tempConst.expCol == null) {
1783: throw Trace.error(
1784: Trace.INDEX_NOT_FOUND,
1785: "table has no primary key");
1786: }
1787: }
1788:
1789: t.checkColumnsMatch(tempConst.localCol,
1790: tempConst.expTable, tempConst.expCol);
1791: tempConstraints.add(tempConst);
1792: }
1793:
1794: sToken = c.getString();
1795:
1796: if (sToken.equals(")")) {
1797: break;
1798: }
1799:
1800: if (!sToken.equals(",")) {
1801: throw Trace.error(Trace.UNEXPECTED_TOKEN,
1802: sToken);
1803: }
1804: }
1805: }
1806:
1807: session.commit();
1808:
1809: // fredt@users 20020225 - patch 509002 by fredt
1810: // it is essential to stay compatible with existing cached tables
1811: // so we create all constraints and indexes (even duplicates) for cached
1812: // tables
1813: // CONSTRAINT PRIMARY KEY can appear in user scripts and new tables only so
1814: // we can safely apply it correctly
1815: // first apply any primary key constraint
1816: // then set all the constriants
1817: // also, duplicate indexes can be avoided if we choose to in the future but
1818: // currently we have to accept them to stay compatible with existing cached
1819: // tables that include them
1820: tempConst = (TempConstraint) tempConstraints.get(0);
1821:
1822: // tony_lai@users 20020820 - patch 595099
1823: t.createPrimaryKey(pkName, tempConst.localCol);
1824:
1825: boolean logDDL = false;
1826:
1827: for (int i = 1; i < tempConstraints.size(); i++) {
1828: tempConst = (TempConstraint) tempConstraints.get(i);
1829:
1830: if (tempConst.type == Constraint.UNIQUE) {
1831: TableWorks tw = new TableWorks(t);
1832:
1833: tw.createUniqueConstraint(tempConst.localCol,
1834: tempConst.name);
1835:
1836: t = tw.getTable();
1837: }
1838:
1839: if (tempConst.type == Constraint.FOREIGN_KEY) {
1840: TableWorks tw = new TableWorks(t);
1841:
1842: tw.createForeignKey(tempConst.localCol,
1843: tempConst.expCol, tempConst.name,
1844: tempConst.expTable, tempConst.cascade);
1845:
1846: t = tw.getTable();
1847: }
1848: }
1849:
1850: linkTable(t);
1851: } catch (SQLException e) {
1852:
1853: // fredt@users 20020225 - comment
1854: // if a SQLException is thrown while creating table, any foreign key that has
1855: // been created leaves it modification to the expTable in place
1856: // need to undo those modifications. This should not happen in practice.
1857: removeExportedKeys(t);
1858:
1859: throw e;
1860: }
1861: }
1862:
1863: TempConstraint processCreateFK(Tokenizer c, Session session,
1864: Table t, HsqlName cname) throws SQLException {
1865:
1866: int localcol[] = processColumnList(c, t);
1867:
1868: c.getThis("REFERENCES");
1869:
1870: String expTableName = c.getString();
1871: Table expTable;
1872:
1873: // fredt@users 20020221 - patch 520213 by boucherb@users - self reference FK
1874: // allows foreign keys that reference a column in the same table
1875: if (t.equals(expTableName)) {
1876: expTable = t;
1877: } else {
1878: expTable = getTable(expTableName, session);
1879: }
1880:
1881: int expcol[] = null;
1882: String sToken = c.getString();
1883:
1884: c.back();
1885:
1886: // fredt@users 20020503 - patch 1.7.0 by fredt - FOREIGN KEY on table
1887: if (sToken.equals("(")) {
1888: expcol = processColumnList(c, expTable);
1889: } else {
1890:
1891: // the exp table must have a user defined primary key
1892: Index expIndex = expTable.getPrimaryIndex();
1893:
1894: if (expIndex != null) {
1895: expcol = expIndex.getColumns();
1896:
1897: if (expcol[0] == expTable.getColumnCount()) {
1898: throw Trace.error(Trace.INDEX_NOT_FOUND,
1899: expTableName + " has no primary key");
1900: }
1901: }
1902:
1903: // with CREATE TABLE, (expIndex == null) when self referencing FK
1904: // is declared in CREATE TABLE
1905: // null will be returned for expCol and will be checked
1906: // in caller method
1907: // with ALTER TABLE, (expIndex == null) when table has no PK
1908: }
1909:
1910: sToken = c.getString();
1911:
1912: // fredt@users 20020305 - patch 1.7.0 - cascading deletes
1913: boolean cascade = false;
1914:
1915: if (sToken.equals("ON")) {
1916: c.getThis("DELETE");
1917: c.getThis("CASCADE");
1918:
1919: cascade = true;
1920: } else {
1921: c.back();
1922: }
1923:
1924: if (cname == null) {
1925: cname = HsqlName.makeAutoName("FK");
1926: }
1927:
1928: return new TempConstraint(cname, localcol, expTable, expcol,
1929: Constraint.FOREIGN_KEY, cascade);
1930: }
1931:
1932: // fredt@users 20020420 - patch523880 by leptipre@users - VIEW support
1933:
1934: /**
1935: * Responsible for handling the execution CREATE VIEW SQL statements.
1936: *
1937: * @param session
1938: * @param c
1939: * @throws SQLException
1940: */
1941: private void processCreateView(Tokenizer c, Session session)
1942: throws SQLException {
1943:
1944: View v;
1945: String sToken = c.getName();
1946: int logposition = c.getPartMarker();
1947:
1948: if (this .findUserTable(sToken, session) != null) {
1949: throw Trace.error(Trace.VIEW_ALREADY_EXISTS, sToken);
1950: }
1951:
1952: v = new View(this ,
1953: new HsqlName(sToken, c.wasQuotedIdentifier()));
1954:
1955: c.getThis("AS");
1956: c.setPartMarker();
1957: c.getThis("SELECT");
1958:
1959: Result rResult;
1960: Parser p = new Parser(this , c, session);
1961: int maxRows = session.getMaxRows();
1962:
1963: try {
1964: Select select = p.parseSelect();
1965:
1966: if (select.sIntoTable != null) {
1967: throw (Trace.error(Trace.TABLE_NOT_FOUND));
1968: }
1969:
1970: select.setPreProcess();
1971:
1972: rResult = select.getResult(1);
1973: } catch (SQLException e) {
1974: throw e;
1975: }
1976:
1977: v.setStatement(c.getLastPart());
1978: v.addColumns(rResult);
1979: session.commit();
1980: tTable.add(v);
1981: c.setPartMarker(logposition);
1982: }
1983:
1984: private void processRenameTable(Tokenizer c, Session session,
1985: String tablename) throws SQLException {
1986:
1987: String newname = c.getName();
1988: boolean isquoted = c.wasQuotedIdentifier();
1989: Table t = findUserTable(tablename);
1990:
1991: // this ensures temp table belongs to this session
1992: if (t == null || !t.equals(tablename, session)) {
1993: Trace.error(Trace.TABLE_NOT_FOUND, tablename);
1994: }
1995:
1996: Table ttemp = findUserTable(newname);
1997:
1998: if (ttemp != null
1999: && ttemp.equals(ttemp.getName().name, session)) {
2000: throw Trace.error(Trace.TABLE_ALREADY_EXISTS, tablename);
2001: }
2002:
2003: session.commit();
2004: session.setScripting(!t.isTemp());
2005: t.setName(newname, isquoted);
2006: }
2007:
2008: /**
2009: * 'RENAME' declaration.
2010: * ALTER TABLE <name> RENAME TO <newname>
2011: * ALTER INDEX <name> RENAME TO <newname>
2012: *
2013: * ALTER TABLE <name> ADD CONSTRAINT <constname> FOREIGN KEY (<col>, ...)
2014: * REFERENCE <other table> (<col>, ...) [ON DELETE CASCADE]
2015: *
2016: * ALTER TABLE <name> ADD CONSTRAINT <constname> UNIQUE (<col>, ...)
2017: *
2018: * @param c
2019: * @param session
2020: * @return Result
2021: * @throws SQLException
2022: */
2023: private Result processAlter(Tokenizer c, Session session)
2024: throws SQLException {
2025:
2026: session.checkReadWrite();
2027: session.checkAdmin();
2028: session.setScripting(true);
2029:
2030: String sToken = c.getString();
2031:
2032: if (sToken.equals("TABLE")) {
2033: processAlterTable(c, session);
2034: } else if (sToken.equals("INDEX")) {
2035: processAlterIndex(c, session);
2036: } else {
2037: throw Trace.error(Trace.UNEXPECTED_TOKEN, sToken);
2038: }
2039:
2040: return new Result();
2041: }
2042:
2043: private void processAlterTable(Tokenizer c, Session session)
2044: throws SQLException {
2045:
2046: String tablename = c.getString();
2047: Table t = getUserTable(tablename, session);
2048: TableWorks tw = new TableWorks(t);
2049: String sToken = c.getString();
2050:
2051: if (sToken.equals("RENAME")) {
2052: c.getThis("TO");
2053: processRenameTable(c, session, tablename);
2054:
2055: return;
2056: } else if (sToken.equals("ADD")) {
2057: sToken = c.getString();
2058:
2059: if (sToken.equals("CONSTRAINT")) {
2060: HsqlName cname = new HsqlName(c.getName(), c
2061: .wasQuotedIdentifier());
2062:
2063: sToken = c.getString();
2064:
2065: if (sToken.equals("FOREIGN")) {
2066: c.getThis("KEY");
2067:
2068: TempConstraint tc = processCreateFK(c, session, t,
2069: cname);
2070:
2071: t.checkColumnsMatch(tc.localCol, tc.expTable,
2072: tc.expCol);
2073: session.commit();
2074: tw.createForeignKey(tc.localCol, tc.expCol,
2075: tc.name, tc.expTable, tc.cascade);
2076:
2077: return;
2078: } else if (sToken.equals("UNIQUE")) {
2079: int col[] = processColumnList(c, t);
2080:
2081: session.commit();
2082: tw.createUniqueConstraint(col, cname);
2083:
2084: return;
2085: } else {
2086: throw Trace.error(Trace.UNEXPECTED_TOKEN, sToken);
2087: }
2088: } else if (sToken.equals("COLUMN")) {
2089: int colindex = t.getColumnCount();
2090: Column column = processCreateColumn(c, t);
2091:
2092: sToken = c.getString();
2093:
2094: if (sToken.equals("BEFORE")) {
2095: sToken = c.getName();
2096: colindex = t.getColumnNr(sToken);
2097: } else {
2098: c.back();
2099: }
2100:
2101: if (column.isIdentity()
2102: || column.isPrimaryKey()
2103: || (!t.isEmpty()
2104: && column.isNullable() == false && column
2105: .getDefaultString() == null)) {
2106: throw Trace.error(Trace.BAD_ADD_COLUMN_DEFINITION);
2107: }
2108:
2109: session.commit();
2110: tw.addOrDropColumn(column, colindex, 1);
2111:
2112: return;
2113: }
2114: } else if (sToken.equals("DROP")) {
2115: sToken = c.getString();
2116:
2117: if (sToken.equals("CONSTRAINT")) {
2118: String cname = c.getName();
2119:
2120: session.commit();
2121: tw.dropConstraint(cname);
2122:
2123: return;
2124: } else if (sToken.equals("COLUMN")) {
2125: sToken = c.getName();
2126:
2127: int colindex = t.getColumnNr(sToken);
2128:
2129: session.commit();
2130: tw.addOrDropColumn(null, colindex, -1);
2131:
2132: return;
2133: } else {
2134: throw Trace.error(Trace.UNEXPECTED_TOKEN, sToken);
2135: }
2136: } else {
2137: throw Trace.error(Trace.UNEXPECTED_TOKEN, sToken);
2138: }
2139: }
2140:
2141: private void processAlterIndex(Tokenizer c, Session session)
2142: throws SQLException {
2143:
2144: String indexname = c.getName();
2145:
2146: c.getThis("RENAME");
2147: c.getThis("TO");
2148:
2149: String newname = c.getName();
2150: boolean isQuoted = c.wasQuotedIdentifier();
2151: Table t = findTableForIndex(indexname);
2152:
2153: if (t == null || !t.equals(t.getName().name, session)) {
2154: throw Trace.error(Trace.INDEX_NOT_FOUND, indexname);
2155: }
2156:
2157: Table ttemp = findTableForIndex(newname);
2158:
2159: if (ttemp != null
2160: && ttemp.equals(ttemp.getName().name, session)) {
2161: throw Trace.error(Trace.INDEX_ALREADY_EXISTS, indexname);
2162: }
2163:
2164: if (HsqlName.isReservedName(indexname)) {
2165: throw Trace.error(Trace.SYSTEM_INDEX, indexname);
2166: }
2167:
2168: if (HsqlName.isReservedName(newname)) {
2169: throw Trace.error(Trace.BAD_INDEX_CONSTRAINT_NAME,
2170: indexname);
2171: }
2172:
2173: session.setScripting(!t.isTemp());
2174: session.commit();
2175: t.getIndex(indexname).setName(newname, isQuoted);
2176: }
2177:
2178: // fredt@users 20020221 - patch 1.7.0 chnaged IF EXISTS syntax
2179: // new syntax DROP TABLE tablename IF EXISTS
2180: // fredt@users 20020221 - patch 513005 by sqlbob@users (RMP)
2181:
2182: /**
2183: * Method declaration
2184: *
2185: * @param c
2186: * @param session
2187: * @return
2188: * @throws SQLException
2189: */
2190: private Result processDrop(Tokenizer c, Session session)
2191: throws SQLException {
2192:
2193: session.checkReadWrite();
2194: session.checkAdmin();
2195: session.setScripting(true);
2196:
2197: String sToken = c.getString();
2198:
2199: if (sToken.equals("TABLE") || sToken.equals("VIEW")) {
2200: boolean isview = sToken.equals("VIEW");
2201: String tablename = c.getString();
2202: boolean dropmode = false;
2203:
2204: sToken = c.getString();
2205:
2206: if (sToken.equals("IF")) {
2207: c.getThis("EXISTS");
2208:
2209: dropmode = true;
2210: } else {
2211: c.back();
2212:
2213: Table t = getTable(tablename, session);
2214:
2215: session.setScripting(!t.isTemp());
2216: }
2217:
2218: dropTable(tablename, dropmode, isview, session);
2219: session.commit();
2220: } else if (sToken.equals("USER")) {
2221: aAccess.dropUser(c.getStringToken());
2222: } else if (sToken.equals("TRIGGER")) {
2223: dropTrigger(c.getString(), session);
2224: } else if (sToken.equals("INDEX")) {
2225: String indexname = c.getName();
2226: Table t = findTableForIndex(indexname);
2227:
2228: if (t == null || !t.equals(t.getName().name, session)) {
2229: throw Trace.error(Trace.INDEX_NOT_FOUND, indexname);
2230: }
2231:
2232: t.checkDropIndex(indexname, null);
2233:
2234: // fredt@users 20020405 - patch 1.7.0 by fredt - drop index bug
2235: // see Table.moveDefinition();
2236: session.commit();
2237: session.setScripting(false);
2238:
2239: TableWorks tw = new TableWorks(t);
2240:
2241: tw.dropIndex(indexname);
2242: } else {
2243: throw Trace.error(Trace.UNEXPECTED_TOKEN, sToken);
2244: }
2245:
2246: return new Result();
2247: }
2248:
2249: // fredt@users 20020221 - patch 513005 by sqlbob@users (RMP)
2250:
2251: /**
2252: * Responsible for handling the execution of GRANT and REVOKE SQL
2253: * statements.
2254: *
2255: * @param c
2256: * @param session
2257: * @param grant
2258: * @return Description of the Return Value
2259: * @throws SQLException
2260: */
2261: private Result processGrantOrRevoke(Tokenizer c, Session session,
2262: boolean grant) throws SQLException {
2263:
2264: session.checkReadWrite();
2265: session.checkAdmin();
2266:
2267: // fredt@users 20020221 - patch 513005 by sqlbob@users (RMP)
2268: session.setScripting(true);
2269:
2270: int right = 0;
2271: String sToken;
2272:
2273: do {
2274: String sRight = c.getString();
2275:
2276: right |= UserManager.getRight(sRight);
2277: sToken = c.getString();
2278: } while (sToken.equals(","));
2279:
2280: if (!sToken.equals("ON")) {
2281: throw Trace.error(Trace.UNEXPECTED_TOKEN, sToken);
2282: }
2283:
2284: String table = c.getString();
2285:
2286: if (table.equals("CLASS")) {
2287:
2288: // object is saved as 'CLASS "java.lang.Math"'
2289: // tables like 'CLASS "xy"' should not be created
2290: table += " \"" + c.getString() + "\"";
2291: } else {
2292:
2293: // fredt@users 20020221 - patch 513005 by sqlbob@users (RMP)
2294: // to make sure the table exists
2295: Table t = getTable(table, session);
2296:
2297: session.setScripting(!t.isTemp());
2298: }
2299:
2300: c.getThis("TO");
2301:
2302: String user = c.getStringToken();
2303: String command;
2304:
2305: if (grant) {
2306: aAccess.grant(user, table, right);
2307:
2308: command = "GRANT";
2309: } else {
2310: aAccess.revoke(user, table, right);
2311:
2312: command = "REVOKE";
2313: }
2314:
2315: return new Result();
2316: }
2317:
2318: /**
2319: * Responsible for handling the execution CONNECT SQL statements
2320: *
2321: * @param c
2322: * @param session
2323: * @return
2324: * @throws SQLException
2325: */
2326: private Result processConnect(Tokenizer c, Session session)
2327: throws SQLException {
2328:
2329: c.getThis("USER");
2330:
2331: String username = c.getStringToken();
2332:
2333: c.getThis("PASSWORD");
2334:
2335: String password = c.getStringToken();
2336: User user = aAccess.getUser(username, password);
2337:
2338: session.commit();
2339: session.setUser(user);
2340:
2341: return new Result();
2342: }
2343:
2344: /**
2345: * Responsible for handling the execution DISCONNECT SQL statements
2346: *
2347: * @param session
2348: * @return
2349: * @throws SQLException
2350: */
2351: private Result processDisconnect(Session session)
2352: throws SQLException {
2353:
2354: if (!session.isClosed()) {
2355: session.disconnect();
2356: cSession.set(session.getId(), null);
2357: }
2358:
2359: dropTempTables(session);
2360:
2361: return new Result();
2362: }
2363:
2364: // fredt@users 20020221 - patch 513005 by sqlbob@users (RMP)
2365:
2366: /**
2367: * Responsible for handling the execution SET SQL statements
2368: *
2369: * @param c
2370: * @param session
2371: * @return
2372: * @throws SQLException
2373: */
2374: private Result processSet(Tokenizer c, Session session)
2375: throws SQLException {
2376:
2377: // fredt@users 20020221 - patch 513005 by sqlbob@users (RMP)
2378: session.setScripting(true);
2379:
2380: String sToken = c.getString();
2381:
2382: if (sToken.equals("PASSWORD")) {
2383: session.checkReadWrite();
2384: session.setPassword(c.getStringToken());
2385: } else if (sToken.equals("READONLY")) {
2386: session.commit();
2387: session.setReadOnly(processTrueOrFalse(c));
2388: } else if (sToken.equals("LOGSIZE")) {
2389: session.checkAdmin();
2390:
2391: int i = Integer.parseInt(c.getString());
2392:
2393: logger.setLogSize(i);
2394: } else if (sToken.equals("IGNORECASE")) {
2395: session.checkAdmin();
2396:
2397: bIgnoreCase = processTrueOrFalse(c);
2398: } else if (sToken.equals("MAXROWS")) {
2399: int i = Integer.parseInt(c.getString());
2400:
2401: session.setMaxRows(i);
2402: } else if (sToken.equals("AUTOCOMMIT")) {
2403: session.setAutoCommit(processTrueOrFalse(c));
2404: } else if (sToken.equals("TABLE")) {
2405:
2406: // fredt@users 20020221 - patch 513005 by sqlbob@users (RMP)
2407: // support for SET TABLE <table> READONLY [TRUE|FALSE]
2408: // sqlbob@users 20020427 support for SET TABLE <table> SOURCE "file" [DESC]
2409: session.checkReadWrite();
2410:
2411: Table t = getTable(c.getString(), session);
2412:
2413: sToken = c.getString();
2414:
2415: session.setScripting(!t.isTemp());
2416:
2417: if (sToken.equals("SOURCE")) {
2418: if (!t.isTemp()) {
2419: session.checkAdmin();
2420: }
2421:
2422: sToken = c.getString();
2423:
2424: if (!c.wasQuotedIdentifier()) {
2425:
2426: //fredt - can replace with a better message
2427: throw Trace.error(Trace.INVALID_ESCAPE);
2428: }
2429:
2430: boolean isDesc = false;
2431:
2432: if (c.getString().equals("DESC")) {
2433: isDesc = true;
2434: } else {
2435: c.back();
2436: }
2437:
2438: t.setDataSource(sToken, isDesc, session);
2439: } else if (sToken.equals("READONLY")) {
2440: session.checkAdmin();
2441: t.setDataReadOnly(processTrueOrFalse(c));
2442: } else if (sToken.equals("INDEX")) {
2443: session.checkAdmin();
2444: c.getString();
2445: t.setIndexRoots((String) c.getAsValue());
2446: } else {
2447: throw Trace.error(Trace.UNEXPECTED_TOKEN, sToken);
2448: }
2449: } else if (sToken.equals("REFERENTIAL_INTEGRITY")) {
2450:
2451: // fredt - no longer checking for misspelt form
2452: session.checkAdmin();
2453:
2454: bReferentialIntegrity = processTrueOrFalse(c);
2455: } else if (sToken.equals("WRITE_DELAY")) {
2456: session.checkAdmin();
2457:
2458: boolean delay = processTrueOrFalse(c);
2459:
2460: logger.setWriteDelay(delay);
2461: } else {
2462: throw Trace.error(Trace.UNEXPECTED_TOKEN, sToken);
2463: }
2464:
2465: return new Result();
2466: }
2467:
2468: /**
2469: * Method declaration
2470: *
2471: * @param c
2472: * @return
2473: * @throws SQLException
2474: */
2475: private boolean processTrueOrFalse(Tokenizer c) throws SQLException {
2476:
2477: String sToken = c.getString();
2478:
2479: if (sToken.equals("TRUE")) {
2480: return true;
2481: } else if (sToken.equals("FALSE")) {
2482: return false;
2483: } else {
2484: throw Trace.error(Trace.UNEXPECTED_TOKEN, sToken);
2485: }
2486: }
2487:
2488: /**
2489: * Responsible for handling the execution COMMIT SQL statements
2490: *
2491: * @param c
2492: * @param session
2493: * @return
2494: * @throws SQLException
2495: */
2496: private Result processCommit(Tokenizer c, Session session)
2497: throws SQLException {
2498:
2499: String sToken = c.getString();
2500:
2501: if (!sToken.equals("WORK")) {
2502: c.back();
2503: }
2504:
2505: session.commit();
2506:
2507: return new Result();
2508: }
2509:
2510: /**
2511: * Responsible for handling the execution ROLLBACK SQL statementsn
2512: *
2513: * @param c
2514: * @param session
2515: * @return
2516: * @throws SQLException
2517: */
2518: private Result processRollback(Tokenizer c, Session session)
2519: throws SQLException {
2520:
2521: String sToken = c.getString();
2522:
2523: if (sToken.equals("TO")) {
2524: String sToken1 = c.getString();
2525:
2526: if (!sToken1.equals("SAVEPOINT")) {
2527: throw Trace.error(Trace.UNEXPECTED_TOKEN, sToken1);
2528: }
2529:
2530: sToken1 = c.getString();
2531:
2532: if (sToken1.length() == 0) {
2533: throw Trace.error(Trace.UNEXPECTED_TOKEN, sToken1);
2534: }
2535:
2536: session.rollbackToSavepoint(sToken1);
2537:
2538: return new Result();
2539: }
2540:
2541: if (!sToken.equals("WORK")) {
2542: c.back();
2543: }
2544:
2545: session.rollback();
2546:
2547: return new Result();
2548: }
2549:
2550: /**
2551: * Responsible for handling the execution of SAVEPOINT SQL statements.
2552: *
2553: * @param c Description of the Parameter
2554: * @param session Description of the Parameter
2555: * @return Description of the Return Value
2556: * @throws SQLException
2557: */
2558: private Result processSavepoint(Tokenizer c, Session session)
2559: throws SQLException {
2560:
2561: String sToken = c.getString();
2562:
2563: if (sToken.length() == 0) {
2564: throw Trace.error(Trace.UNEXPECTED_TOKEN, sToken);
2565: }
2566:
2567: session.savepoint(sToken);
2568:
2569: return new Result();
2570: }
2571:
2572: /**
2573: * Called by the garbage collector on this Databases object when garbage
2574: * collection determines that there are no more references to it.
2575: */
2576: public void finalize() {
2577:
2578: try {
2579: close(-1);
2580: } catch (SQLException e) { // it's too late now
2581: }
2582: }
2583:
2584: /**
2585: * Method declaration
2586: *
2587: * @param closemode Description of the Parameter
2588: * @throws SQLException
2589: */
2590: private void close(int closemode) throws SQLException {
2591:
2592: logger.closeLog(closemode);
2593: if (channel != null) {
2594: channel.close();
2595: channel = null;
2596: }
2597: if (adapter != null) {
2598: adapter.stop();
2599: adapter = null;
2600: }
2601:
2602: // tony_lai@users 20020820
2603: // The database re-open and close has been moved from
2604: // Log#close(int closemode) for saving memory usage.
2605: // Doing so the instances of Log and other objects are no longer
2606: // referenced, and therefore can be garbage collected if necessary.
2607: if (closemode == 1) {
2608: open();
2609: logger.closeLog(0);
2610: }
2611:
2612: bShutdown = true;
2613:
2614: jdbcConnection.removeDatabase(this );
2615: }
2616:
2617: /**
2618: * Responsible for handling the execution SHUTDOWN SQL statements
2619: *
2620: * @param c
2621: * @param session
2622: * @return
2623: * @throws SQLException
2624: */
2625: private Result processShutdown(Tokenizer c, Session session)
2626: throws SQLException {
2627:
2628: if (!session.isClosed()) {
2629: session.checkAdmin();
2630: }
2631:
2632: int closemode = 0;
2633: String token = c.getString();
2634:
2635: if (token.equals("IMMEDIATELY")) {
2636: closemode = -1;
2637: } else if (token.equals("COMPACT")) {
2638: closemode = 1;
2639: } else {
2640: c.back();
2641: }
2642:
2643: // don't disconnect system user; need it to save database
2644: for (int i = 1, tsize = cSession.size(); i < tsize; i++) {
2645: Session d = (Session) cSession.get(i);
2646:
2647: if (d != null) {
2648: d.disconnect();
2649: }
2650: }
2651:
2652: cSession.clear();
2653: close(closemode);
2654: processDisconnect(session);
2655:
2656: return new Result();
2657: }
2658:
2659: /**
2660: * Responsible for handling the parse and execution of CHECKPOINT SQL
2661: * statements.
2662: *
2663: * @param session
2664: * @return
2665: * @throws SQLException
2666: */
2667: private Result processCheckpoint(Session session)
2668: throws SQLException {
2669:
2670: session.checkAdmin();
2671: session.checkReadWrite();
2672: logger.checkpoint();
2673:
2674: return new Result();
2675: }
2676:
2677: /**
2678: * @param ownerSession
2679: */
2680: private void dropTempTables(Session ownerSession) {
2681:
2682: for (int i = 0; i < tTable.size(); i++) {
2683: Table toDrop = (Table) tTable.get(i);
2684:
2685: if (toDrop.isTemp()
2686: && toDrop.getOwnerSession() == ownerSession) {
2687: tTable.remove(i);
2688: }
2689: }
2690: }
2691:
2692: // fredt@users 20020221 - patch 521078 by boucherb@users - DROP TABLE checks
2693: // avoid dropping tables referenced by foreign keys - also bug 451245
2694: // additions by fredt@users
2695: // remove redundant constrains on tables referenced by the dropped table
2696: // avoid dropping even with referential integrity off
2697:
2698: /**
2699: * Drops the specified user-defined view or table from this Database
2700: * object. <p>
2701: *
2702: * The process of dropping a table or view includes:
2703: * <OL>
2704: * <LI> checking that the specified Session's currently connected User
2705: * has the right to perform this operation and refusing to proceed if
2706: * not by throwing.
2707: * <LI> checking for referential constraints that conflict with this
2708: * operation and refusing to proceed if they exist by throwing.</LI>
2709: *
2710: * <LI> removing the specified Table from this Database object.
2711: * <LI> removing any exported foreign keys Constraint objects held by
2712: * any tables referenced by the table to be dropped. This is especially
2713: * important so that the dropped Table ceases to be referenced,
2714: * eventually allowing its full garbage collection.
2715: * <LI>
2716: * </OL>
2717: * <p>
2718: *
2719: *
2720: *
2721: * @param name of the table or view to drop
2722: * @param ifExists if true and if the Table to drop does not exist, fail
2723: * silently, else throw
2724: * @param isView true if the name argument refers to a View
2725: * @param session the connected context in which to perform this
2726: * operation
2727: * @throws SQLException if any of the checks listed above fail
2728: */
2729: void dropTable(String name, boolean ifExists, boolean isView,
2730: Session session) throws SQLException {
2731:
2732: Table toDrop = null;
2733: int dropIndex = -1;
2734: int refererIndex = -1;
2735: Enumeration constraints = null;
2736: Constraint currentConstraint = null;
2737: Table refTable = null;
2738: boolean isRef = false;
2739: boolean isSelfRef = false;
2740:
2741: for (int i = 0; i < tTable.size(); i++) {
2742: toDrop = (Table) tTable.get(i);
2743:
2744: if (toDrop.equals(name, session)
2745: && (isView == toDrop.isView())) {
2746: dropIndex = i;
2747:
2748: break;
2749: } else {
2750: toDrop = null;
2751: }
2752: }
2753:
2754: if (dropIndex == -1) {
2755: if (ifExists) {
2756: return;
2757: } else {
2758: throw Trace.error(isView ? Trace.VIEW_NOT_FOUND
2759: : Trace.TABLE_NOT_FOUND, name);
2760: }
2761: }
2762:
2763: constraints = toDrop.getConstraints().elements();
2764:
2765: while (constraints.hasMoreElements()) {
2766: currentConstraint = (Constraint) constraints.nextElement();
2767:
2768: if (currentConstraint.getType() != Constraint.MAIN) {
2769: continue;
2770: }
2771:
2772: refTable = currentConstraint.getRef();
2773: isRef = (refTable != null);
2774: isSelfRef = (isRef && toDrop.equals(refTable));
2775:
2776: if (isRef && !isSelfRef) {
2777:
2778: // cover the case where the referencing table
2779: // may have already been dropped
2780: for (int k = 0; k < tTable.size(); k++) {
2781: if (refTable.equals(tTable.get(k))) {
2782: refererIndex = k;
2783:
2784: break;
2785: }
2786: }
2787:
2788: if (refererIndex != -1) {
2789:
2790: // tony_lai@users 20020820 - patch 595156
2791: throw Trace.error(
2792: Trace.INTEGRITY_CONSTRAINT_VIOLATION,
2793: currentConstraint.getName().name
2794: + " table: "
2795: + refTable.getName().name);
2796: }
2797: }
2798: }
2799:
2800: if (toDrop.isText()) {
2801: toDrop.setDataSource("", false, session);
2802: }
2803:
2804: tTable.remove(dropIndex);
2805: removeExportedKeys(toDrop);
2806: }
2807:
2808: /**
2809: * Removes any foreign key Constraint objects (exported keys) held by any
2810: * tables referenced by the specified table. <p>
2811: *
2812: * This method is called as the last step of a successful call to in
2813: * order to ensure that the dropped Table ceases to be referenced when
2814: * enforcing referential integrity.
2815: *
2816: * @param toDrop The table to which other tables may be holding keys.
2817: * This is typically a table that is in the process of being dropped.
2818: */
2819: void removeExportedKeys(Table toDrop) {
2820:
2821: for (int i = 0; i < tTable.size(); i++) {
2822: HsqlArrayList constraintvector = ((Table) tTable.get(i))
2823: .getConstraints();
2824:
2825: for (int j = constraintvector.size() - 1; j >= 0; j--) {
2826: Constraint currentConstraint = (Constraint) constraintvector
2827: .get(j);
2828: Table refTable = currentConstraint.getRef();
2829:
2830: if (toDrop == refTable) {
2831: constraintvector.remove(j);
2832: }
2833: }
2834: }
2835: }
2836:
2837: // fredt@users 20020221 - patch 513005 by sqlbob@users (RMP)
2838:
2839: /**
2840: * Method declaration
2841: *
2842: * @param name
2843: * @param session
2844: * @throws SQLException
2845: */
2846: private void dropTrigger(String name, Session session)
2847: throws SQLException {
2848:
2849: // look in each trigger list of each type of trigger for each table
2850: for (int i = 0, tsize = tTable.size(); i < tsize; i++) {
2851: Table t = (Table) tTable.get(i);
2852: int numTrigs = TriggerDef.numTrigs();
2853:
2854: for (int tv = 0; tv < numTrigs; tv++) {
2855: HsqlArrayList v = t.vTrigs[tv];
2856:
2857: for (int tr = 0; tr < v.size(); tr++) {
2858: TriggerDef td = (TriggerDef) v.get(tr);
2859:
2860: if (td.name.equals(name)) {
2861:
2862: // fredt@users 20020221 - patch 513005 by sqlbob@users (RMP)
2863: session.setScripting(!td.table.isTemp());
2864: v.remove(tr);
2865:
2866: if (Trace.TRACE) {
2867: Trace.trace("Trigger dropped " + name);
2868: }
2869:
2870: return;
2871: }
2872: }
2873: }
2874: }
2875:
2876: throw Trace.error(Trace.TRIGGER_NOT_FOUND, name);
2877: }
2878:
2879: /**
2880: * Move the existing X.script and X.data files to X.script.backup.<date> and X.data.backup.<date>
2881: */
2882: private void renameExistingLogs() {
2883: File script_file = new File(sName + ".script");
2884: File data_file = new File(sName + ".data");
2885: Calendar c = Calendar.getInstance();
2886: String curr_date;
2887:
2888: curr_date = c.get(Calendar.YEAR) + "_" + c.get(Calendar.MONTH)
2889: + "_" + c.get(Calendar.DAY_OF_MONTH) + "-"
2890: + c.get(Calendar.HOUR) + ":" + c.get(Calendar.MINUTE)
2891: + ":" + c.get(Calendar.SECOND) + "."
2892: + c.get(Calendar.MILLISECOND);
2893:
2894: if (script_file.exists()) {
2895: System.out
2896: .println("Database.renameExistingLogs(): renaming "
2897: + sName + ".script" + " to " + sName
2898: + ".script.backup-" + curr_date);
2899: script_file.renameTo(new File(sName + ".script.backup-"
2900: + curr_date));
2901: }
2902:
2903: if (data_file.exists()) {
2904: System.out
2905: .println("Database.renameExistingLogs(): renaming "
2906: + sName + ".data" + " to " + sName
2907: + ".data.backup-" + curr_date);
2908: data_file.renameTo(new File(sName + ".data.backup-"
2909: + curr_date));
2910: }
2911: }
2912:
2913: /**
2914: * Transitional interface for log and cache management. In future, this
2915: * will form the basis for the public interface of logging and cache
2916: * classes.<p>
2917: *
2918: * Implements a storage manager wrapper that provides a consitent, always
2919: * available interface to storage management for the Database class,
2920: * despite the fact not all Database objects actually use file storage.
2921: * <p>
2922: *
2923: * The Logger class makes it possible avoid the necessity to test for a
2924: * null Log Database attribute again and again, in many different places,
2925: * and generally avoids tight coupling between Database and Log, opening
2926: * the doors for multiple logs/caches in the future. In this way, the
2927: * Database class does not need to know the details of the Logging/Cache
2928: * implementation, lowering its breakability factor and promoting
2929: * long-term code flexibility.
2930: */
2931: class Logger {
2932:
2933: /**
2934: * The Log object this Logger object wraps
2935: */
2936: private Log lLog;
2937:
2938: /**
2939: * Opens the specified Database object's database files and starts up
2940: * the logging process. <p>
2941: *
2942: * If the specified Database object is a new database, its database
2943: * files are first created.
2944: *
2945: * @param db the Database
2946: * @param sys the anonymous system Session context in which the
2947: * specified Database object's logging process will operate
2948: * @param name the path and common name of the database files
2949: * @return true if the specified database files had to be created
2950: * before being opened (i.e. a new database is created)
2951: * @throws SQLException if there is a problem, such as the case when
2952: * the specified files are in use by another process
2953: */
2954: boolean openLog(Database db, Session sys, String name)
2955: throws SQLException {
2956:
2957: lLog = new Log(db, sys, name);
2958:
2959: boolean result = lLog.open();
2960:
2961: return result;
2962: }
2963:
2964: // fredt@users 20020130 - patch 495484 by boucherb@users
2965:
2966: /**
2967: * Shuts down the logging process using the specified mode. <p>
2968: *
2969: *
2970: *
2971: * @param closemode The mode in which to shut down the logging
2972: * process
2973: * <OL>
2974: * <LI> closemode -1 performs SHUTDOWN IMMEDIATELY, equivalent
2975: * to a poweroff or crash.
2976: * <LI> closemode 0 performs a normal SHUTDOWN that
2977: * checkpoints the database normally.
2978: * <LI> closemode 1 performs a shutdown compact that scripts
2979: * out the contents of any CACHED tables to the log then
2980: * deletes the existing *.data file that contains the data
2981: * for all CACHED table before the normal checkpoint process
2982: * which in turn creates a new, compact *.data file.
2983: * </OL>
2984: *
2985: * @throws SQLException if there is a problem closing the Log and
2986: * its dependent files.
2987: */
2988: void closeLog(int closemode) throws SQLException {
2989:
2990: if (lLog == null) {
2991: return;
2992: }
2993:
2994: lLog.stop();
2995:
2996: switch (closemode) {
2997:
2998: case -1:
2999: lLog.shutdown();
3000: break;
3001:
3002: case 0:
3003: lLog.close(false);
3004: break;
3005:
3006: case 1:
3007: lLog.close(true);
3008: break;
3009: }
3010:
3011: lLog = null;
3012: }
3013:
3014: /**
3015: * Determines if the logging process actually does anything. <p>
3016: *
3017: * In-memory Database objects do not need to log anything. This
3018: * method is essentially equivalent to testing whether this logger's
3019: * database is an in-memory mode database.
3020: *
3021: * @return true if this object encapsulates a non-null Log instance,
3022: * else false
3023: */
3024: boolean hasLog() {
3025: return lLog != null;
3026: }
3027:
3028: /**
3029: * Returns the Cache object or null if one doesn't exist.
3030: */
3031: Cache getCache() throws SQLException {
3032:
3033: if (lLog != null) {
3034: return lLog.getCache();
3035: } else {
3036: return null;
3037: }
3038: }
3039:
3040: /**
3041: * Releases any cached data rows above the maximum set for any Cache
3042: * objects existing within the context of this logger.
3043: *
3044: * @throws SQLException if there is a problem releasing cahced data
3045: * rows during the cleanup process
3046: */
3047: void cleanUp() throws SQLException {
3048:
3049: if (lLog != null && lLog.getCache() != null) {
3050: lLog.getCache().cleanUp();
3051: }
3052: }
3053:
3054: /**
3055: * Records a Log entry representing a new connection action on the
3056: * specified Session object.
3057: *
3058: * @param session the Session object for which to record the log
3059: * entry
3060: * @param username the name of the User, as known to the database
3061: * @param password the password of the user, as know to the database
3062: * @throws SQLException if there is a problem recording the Log
3063: * entry
3064: */
3065: void logConnectUser(Session session, String username,
3066: String password) throws SQLException {
3067:
3068: if (lLog != null) {
3069: lLog.write(session, "CONNECT USER " + username
3070: + " PASSWORD \"" + password + "\"");
3071: }
3072: }
3073:
3074: /**
3075: * Records a Log entry for the specified SQL statement, on behalf of
3076: * the specified Session object.
3077: *
3078: * @param session the Session object for which to record the Log
3079: * entry
3080: * @param statement the SQL statement to Log
3081: * @throws SQLException if there is a problem recording the entry
3082: */
3083: void writeToLog(Session session, String statement)
3084: throws SQLException {
3085:
3086: if (lLog != null) {
3087: lLog.write(session, statement);
3088: }
3089: }
3090:
3091: /**
3092: * Checkpoints the database. <p>
3093: *
3094: * The most important effect of calling this method is to cause the
3095: * log file to be rewritten in the most efficient form to
3096: * reflect the current state of the database, i.e. only the DDL and
3097: * insert DML required to recreate the database in its present state.
3098: * Other house-keeping duties are performed w.r.t. other database
3099: * files, in order to ensure as much as possible the ACID properites
3100: * of the database.
3101: *
3102: * @throws SQLException if there is a problem checkpointing the
3103: * database
3104: */
3105: private void checkpoint() throws SQLException {
3106:
3107: if (lLog != null) {
3108: lLog.checkpoint();
3109: }
3110: }
3111:
3112: /**
3113: * Sets the maximum size to which the log file can grow
3114: * before being automatically checkpointed.
3115: *
3116: * @param i The size, in MB
3117: */
3118: void setLogSize(int i) {
3119:
3120: if (lLog != null) {
3121: lLog.setLogSize(i);
3122: }
3123: }
3124:
3125: /**
3126: * Sets the log write delay mode on or off. When write delay mode is
3127: * switched on, the strategy is that executed commands are written to
3128: * the log at most 1 second after they are executed. This may
3129: * improve performance for applications that execute a large number
3130: * of short running statements in a short period of time, but risks
3131: * failing to log some possibly large number of statements in the
3132: * event of a crash. When switched off, the strategy is that all SQL
3133: * commands are written to the log immediately after they
3134: * are executed, resulting in possibly slower execution but with the
3135: * maximum risk being the loss of at most one statement in the event
3136: * of a crash.
3137: *
3138: * @param delay if true, used a delayed write strategy, else use an
3139: * immediate write strategy
3140: */
3141: void setWriteDelay(boolean delay) {
3142:
3143: if (lLog != null) {
3144: lLog.setWriteDelay(delay);
3145: }
3146: }
3147:
3148: /**
3149: * Opens the TextCache object if a Log object exists.
3150: */
3151: Cache openTextCache(String table, String source,
3152: boolean readOnlyData, boolean reversed)
3153: throws SQLException {
3154: return lLog.openTextCache(table, source, readOnlyData,
3155: reversed);
3156: }
3157:
3158: /**
3159: * Closes the TextCache object if a Log object exists.
3160: */
3161: void closeTextCache(String name) throws SQLException {
3162: lLog.closeTextCache(name);
3163: }
3164:
3165: void initializeDatabaseFromBuffer(byte[] buf)
3166: throws SQLException {
3167: lLog.initializeDatabaseFromBuffer(buf);
3168: }
3169:
3170: }
3171: }
|