Quadcap Embeddable Database

com/quadcap/sql/Session.java

Go to the documentation of this file.
00001 package com.quadcap.sql; 00002 00003 /* Copyright 1999 - 2003 Quadcap Software. All rights reserved. 00004 * 00005 * This software is distributed under the Quadcap Free Software License. 00006 * This software may be used or modified for any purpose, personal or 00007 * commercial. Open Source redistributions are permitted. Commercial 00008 * redistribution of larger works derived from, or works which bundle 00009 * this software requires a "Commercial Redistribution License"; see 00010 * http://www.quadcap.com/purchase. 00011 * 00012 * Redistributions qualify as "Open Source" under one of the following terms: 00013 * 00014 * Redistributions are made at no charge beyond the reasonable cost of 00015 * materials and delivery. 00016 * 00017 * Redistributions are accompanied by a copy of the Source Code or by an 00018 * irrevocable offer to provide a copy of the Source Code for up to three 00019 * years at the cost of materials and delivery. Such redistributions 00020 * must allow further use, modification, and redistribution of the Source 00021 * Code under substantially the same terms as this license. 00022 * 00023 * Redistributions of source code must retain the copyright notices as they 00024 * appear in each source code file, these license terms, and the 00025 * disclaimer/limitation of liability set forth as paragraph 6 below. 00026 * 00027 * Redistributions in binary form must reproduce this Copyright Notice, 00028 * these license terms, and the disclaimer/limitation of liability set 00029 * forth as paragraph 6 below, in the documentation and/or other materials 00030 * provided with the distribution. 00031 * 00032 * The Software is provided on an "AS IS" basis. No warranty is 00033 * provided that the Software is free of defects, or fit for a 00034 * particular purpose. 00035 * 00036 * Limitation of Liability. Quadcap Software shall not be liable 00037 * for any damages suffered by the Licensee or any third party resulting 00038 * from use of the Software. 00039 */ 00040 00041 import java.io.IOException; 00042 00043 import java.util.ArrayList; 00044 import java.util.Enumeration; 00045 import java.util.Hashtable; 00046 import java.util.List; 00047 import java.util.Random; 00048 00049 import java.sql.ResultSet; 00050 import java.sql.Statement; 00051 import java.sql.SQLException; 00052 00053 import com.quadcap.sql.index.Btree; 00054 import com.quadcap.sql.index.Comparator; 00055 00056 import com.quadcap.sql.file.BlockFile; 00057 import com.quadcap.sql.file.ByteUtil; 00058 import com.quadcap.sql.file.DatafileException; 00059 import com.quadcap.sql.file.Log; 00060 import com.quadcap.sql.file.LogEntry; 00061 00062 import com.quadcap.sql.lock.Transaction; 00063 00064 import com.quadcap.sql.io.ObjectOutputStream; 00065 00066 import com.quadcap.util.Debug; 00067 00068 /** 00069 * Analagous (and mapped onto) a JDBC <code>Statement</code>, this class 00070 * maintains state and locks on behalf of a single session. 00071 * 00072 * @author Stan Bailes 00073 */ 00074 public class Session { 00075 Connection qConn; 00076 Database db; 00077 ObjectOutputStream oos; 00078 00079 ResultSet rs = null; 00080 int updateCount = 0; 00081 long lastInsertId = 0; 00082 00083 Object plock = new Object(); 00084 List stmtPendingActions = null; 00085 00086 List cursors = null; 00087 Hashtable stmtContext = null; 00088 int stmtId = 0; 00089 00090 /** my position in qConn.sessions */ 00091 int sessionIndex = -1; 00092 static int lastId = 0; 00093 00094 boolean viewCheck = false; 00095 public boolean getViewCheck() { return viewCheck; } 00096 public void setViewCheck() { viewCheck = true; } 00097 public void clearViewCheck() { viewCheck = false; } 00098 00099 /** 00100 * Construct a new session for the specified database with the specified 00101 * authorization 00102 * 00103 * @param db the database 00104 * @param auth the authorization id 00105 */ 00106 Session(Connection conn, int sessionIndex) { 00107 this.qConn = conn; 00108 this.sessionIndex = sessionIndex; 00109 this.db = conn.getDatabase(); 00110 //this.id = lastId++; 00111 this.lastInsertId = conn.getLastInsertId(); 00112 this.oos = new ObjectOutputStream(null); 00113 } 00114 00115 /** 00116 * Accessor for my connection 00117 */ 00118 public final Connection getConnection() { return qConn; } 00119 00120 /** 00121 * Accessor for my transaction (lazy) 00122 */ 00123 public final Transaction getTransaction() { 00124 return qConn.getTransaction(); 00125 } 00126 00127 /** 00128 * Accessor for my transaction id (lazy) 00129 */ 00130 public final long getTransactionId() { 00131 return qConn.getTransactionId(); 00132 } 00133 00134 /** 00135 * Return the transaction id, starting a new transaction if 00136 * necessary 00137 */ 00138 public final long makeTransaction() throws SQLException { 00139 long trans = getTransactionId(); 00140 if (trans < 0) { 00141 qConn.makeTransaction(); 00142 trans = qConn.getTransactionId(); 00143 stmtId = 0; 00144 } else { 00145 stmtId++; 00146 } 00147 return trans; 00148 } 00149 00150 /** 00151 * Accessor for my logger 00152 */ 00153 final public Log getLog() { return db.getLog(); } 00154 00155 /** 00156 * Return my session id 00157 */ 00158 public int getSessionId() { return sessionIndex; } 00159 00160 /** 00161 * Return the current statement number 00162 */ 00163 public int getStmtId() { return stmtId; } 00164 00165 /** 00166 * Create a temporary btree with the specified comparator 00167 */ 00168 final Btree makeTempTree(Comparator compare) throws IOException { 00169 return qConn.makeTempTree(compare); 00170 } 00171 00172 /** 00173 * Create a temporary btree with the default comparator 00174 */ 00175 final Btree makeTempTree() throws IOException { 00176 return qConn.makeTempTree(); 00177 } 00178 00179 /** 00180 * Accessor for my database 00181 */ 00182 public final Database getDatabase() { return db; } 00183 00184 /** 00185 * Accessor for my file 00186 */ 00187 public final BlockFile getFile() { return qConn.getFile(); } 00188 00189 /** 00190 * Accessor for my 'Random' object 00191 */ 00192 final Random getRandom() { return qConn.getRandom(); } 00193 00194 /** 00195 * Helper routine to obtain a write lock on the specified table 00196 */ 00197 public final void getTableWriteLock(String tableName) 00198 throws SQLException, IOException 00199 { 00200 qConn.getTableWriteLock(tableName); 00201 } 00202 00203 /** 00204 * Helper routine to obtain a read lock on the specified table 00205 */ 00206 public final void getTableReadLock(String tableName) 00207 throws SQLException, IOException 00208 { 00209 qConn.getTableReadLock(tableName); 00210 } 00211 00212 /** 00213 * When a statement is initiated on a connection, any previous 00214 * result set open on that connection is implicitly closed. 00215 */ 00216 final void closeResultSet() { 00217 //#ifdef TRACE 00218 if (Trace.bit(9)) { 00219 Debug.println(toString() + ".closeResultSet()"); 00220 } 00221 //#endif 00222 if (rs != null) { 00223 try { 00224 rs.close(); 00225 } catch (Throwable t) { 00226 //#ifdef DEBUG 00227 //Debug.print(t); 00228 //#endif 00229 } finally { 00230 rs = null; 00231 } 00232 } 00233 } 00234 00235 /** 00236 * Execute a single SQL statement. 00237 */ 00238 public final void doStatement(Stmt s) throws IOException, SQLException { 00239 // ---- If the last statement had a result set, then commit 00240 // ---- it (if auto commit) and close the result set. This is 00241 // ---- mandated by JDBC. 00242 00243 //#ifdef TRACE 00244 if (Trace.bit(9)) { 00245 Debug.println(toString() + ".doStatement(" + s + ")"); 00246 } 00247 //#endif 00248 00249 if (rs != null) { 00250 closeResultSet(); 00251 } 00252 makeTransaction(); 00253 if (qConn.readOnly) { 00254 if (!(s instanceof SelectStmt)) { 00255 throw new SQLException( 00256 "Only SELECT statements are permitted in read-only mode"); 00257 } 00258 s.execute(this); 00259 } else { 00260 updateCount = 0; 00261 beginStatement(); 00262 00263 try { 00264 s.execute(this); 00265 } catch (IOException e) { 00266 //#ifdef DEBUG 00267 Debug.print(e); 00268 //#endif 00269 rollbackStatement(); 00270 throw e; 00271 } catch (SQLException e) { 00272 //#ifdef DEBUG 00273 if (Trace.bit(1)) { 00274 Debug.println("--- Verbose exception report: "); 00275 Debug.print(e); 00276 } 00277 //#endif 00278 rollbackStatement(); 00279 throw e; 00280 } catch (Throwable e) { 00281 //#ifdef DEBUG 00282 Debug.print(e); 00283 //#endif 00284 rollbackStatement(); 00285 throw DbException.wrapThrowable(e); 00286 } 00287 00288 // ---- If no result set, then just commit (statement or 00289 // ---- transaction level as required) now. 00290 if (rs == null) { 00291 endStatement(false); 00292 } 00293 } 00294 } 00295 00296 public final void beginStatement() throws IOException { 00297 getLog().addEntry(new LogEntry(getTransactionId(), stmtId, 00298 LogEntry.BEGIN_STATEMENT)); 00299 } 00300 00301 /** 00302 * Called to conclude the execution of this statement. 00303 * 00304 * @param abort if <b>true</b>, we perform statement-level 00305 * rollback, otherwise we conclude the statement (possibly 00306 * committing the results if autoCommit is <b>true</b>. 00307 */ 00308 public final void endStatement(boolean abort) 00309 throws IOException, SQLException 00310 { 00311 //#ifdef TRACE 00312 if (Trace.bit(9)) { 00313 Debug.println(toString() + ".endStatement(" + abort + ")"); 00314 } 00315 //#endif 00316 if (getTransactionId() >= 0) { 00317 try { 00318 closeCursors(abort); 00319 } catch (SQLException e) { 00320 rollbackStatement(); 00321 throw e; 00322 } 00323 qConn.endStatement(this, abort); 00324 } 00325 } 00326 00327 /** 00328 * Close any open cursors, aborting or commiting the current transaction 00329 */ 00330 final void closeCursors(boolean abort) 00331 throws SQLException, IOException 00332 { 00333 if (cursors != null) { 00334 for (int i = 0; i < cursors.size(); i++) { 00335 Cursor c = (Cursor)cursors.get(i); 00336 c.close(); 00337 } 00338 cursors = null; 00339 } 00340 if (abort) { 00341 stmtPendingActions = null; 00342 } else { 00343 doPendingActions(); 00344 } 00345 if (stmtContext != null) { 00346 try { 00347 finishContexts(stmtContext, abort); 00348 } finally { 00349 stmtContext = null; 00350 } 00351 } 00352 } 00353 00354 /** 00355 * Statement-level abort. The current statement has failed, but the 00356 * transaction as a whole is still ongoing, so only the effects of 00357 * the current statement so far should be undone. 00358 */ 00359 final void rollbackStatement() throws IOException, SQLException { 00360 //#ifdef TRACE 00361 if (Trace.bit(9)) { 00362 Debug.println(toString() + ".rollbackStatement()"); 00363 } 00364 //#endif 00365 synchronized (plock) { 00366 stmtPendingActions = null; 00367 } 00368 qConn.rollbackStatement(this); 00369 } 00370 00371 00372 /** 00373 * Log and execute the given unit of work for this session 00374 */ 00375 public final void doStep(LogStep s) throws SQLException, IOException { 00376 synchronized (db.getFileLock()) { 00377 s.prepare(this); 00378 //#ifdef DEBUG 00379 if (false && Trace.bit(15)) { 00380 Debug.println("[T:" + qConn.getTransactionId() + 00381 "].redo(" + s + ")"); 00382 } 00383 //#endif 00384 try { 00385 db.doStep(getTransaction(), s); 00386 } catch (DatafileException e) { 00387 //#ifdef DEBUG 00388 //Debug.print(e); 00389 //#endif 00390 throw (SQLException)e.getCause(); 00391 } 00392 } 00393 } 00394 00395 final void setResultSet(QedResultSet rs) { 00396 this.rs = rs; 00397 } 00398 00399 /** 00400 * Return the result set associated with this session 00401 */ 00402 final ResultSet getResultSet() { 00403 return rs; 00404 } 00405 00406 /** 00407 * Return the result set associated with this session, after 00408 * making sure that the the result set is bound to my Statement! 00409 */ 00410 public final ResultSet getResultSet(Statement stmt) { 00411 getResultSet(); 00412 if (rs != null) ((QedResultSet)rs).setStatement(stmt); 00413 return rs; 00414 } 00415 00416 /** 00417 * Return the session update count 00418 */ 00419 public final int getUpdateCount() { 00420 return updateCount; 00421 } 00422 00423 final void setUpdateCount(int c) { 00424 this.updateCount = c; 00425 } 00426 00427 final void incrUpdateCount() { 00428 this.updateCount++; 00429 } 00430 00431 final void decrUpdateCount() { 00432 this.updateCount++; 00433 } 00434 00435 /** 00436 * An action to be executed once the currently open cursors are 00437 * closed. 00438 */ 00439 final void addPendingAction(LogStep action) { 00440 synchronized (plock) { 00441 if (stmtPendingActions == null) stmtPendingActions = new ArrayList(); 00442 stmtPendingActions.add(action); 00443 } 00444 } 00445 00446 00447 /** 00448 * At transaction end, execute all of the pending actions 00449 */ 00450 final void doPendingActions() throws SQLException, IOException { 00451 synchronized (plock) { 00452 if (stmtPendingActions != null) { 00453 for (int i = 0; i < stmtPendingActions.size(); i++) { 00454 LogStep step = (LogStep)stmtPendingActions.get(i); 00455 doStep(step); 00456 } 00457 stmtPendingActions = null; 00458 } 00459 } 00460 } 00461 00462 /** 00463 * Another cursor created for this session, which we need to keep 00464 * track of. 00465 */ 00466 final void addCursor(Cursor c) { 00467 if (cursors == null) { 00468 cursors = new ArrayList(); 00469 } 00470 cursors.add(c); 00471 } 00472 00473 /** 00474 * Each context object represents some state retained on behalf 00475 * of a constraint. The 'finish()' method of the context is used 00476 * to perform any constraint-specific processing at the end of 00477 * a statement/transaction. 00478 */ 00479 final void finishContexts(Hashtable context, boolean abort) 00480 throws SQLException, IOException 00481 { 00482 SQLException se = null; 00483 IOException io = null; 00484 00485 int maxp = 0; 00486 for (int p = 0; p <= maxp; p++) { 00487 Enumeration keys = context.keys(); 00488 while (keys.hasMoreElements()) { 00489 Object key = keys.nextElement(); 00490 StatementContext sc = (StatementContext)context.get(key); 00491 int sp = sc.priority(); 00492 maxp = Math.max(maxp, sp); 00493 if (sp == p) { 00494 try { 00495 //#ifdef TRACE 00496 if (Trace.bit(10)) { 00497 Debug.println(toString() + " Finish " + 00498 Table.strip(sc.getClass().getName())); 00499 } 00500 //#endif 00501 sc.finish(abort); 00502 } catch (SQLException ex) { 00503 se = ex; 00504 } catch (IOException ex) { 00505 io = ex; 00506 } 00507 } 00508 } 00509 } 00510 if (se != null) throw se; 00511 if (io != null) throw io; 00512 } 00513 00514 /** 00515 * Return the requested statement context. 00516 * 00517 * @param obj is the key which uniquely identifies the context 00518 * @param deferrable is <b>true</b> if this context's execution can 00519 * be deferred until after the current statement. 00520 */ 00521 final StatementContext getContext(Object obj, 00522 boolean deferrable) { 00523 StatementContext ret = null; 00524 if (deferrable) { 00525 ret = qConn.getContext(obj); 00526 } else if (stmtContext != null) { 00527 ret = (StatementContext)stmtContext.get(obj); 00528 } 00529 return ret; 00530 } 00531 00532 /** 00533 * Add a new context... 00534 */ 00535 final void putContext(Object key, boolean deferrable, 00536 StatementContext val) { 00537 if (deferrable) { 00538 qConn.putContext(key, val); 00539 } else { 00540 if (stmtContext == null) { 00541 stmtContext = new Hashtable(); 00542 } 00543 stmtContext.put(key, val); 00544 } 00545 } 00546 00547 /** 00548 * Close this session 00549 */ 00550 public final void close() throws SQLException, IOException { 00551 endStatement(false); 00552 qConn.removeSession(this); 00553 } 00554 00555 /** 00556 * Set the session's "LAST INSERT ID" 00557 */ 00558 final void setLastInsertId(long insid) { 00559 lastInsertId = insid; 00560 qConn.setLastInsertId(insid); 00561 } 00562 00563 /** 00564 * Return the session's "LAST INSERT ID" 00565 */ 00566 public long getLastInsertId() { 00567 return lastInsertId; 00568 } 00569 00570 //#ifdef DEBUG 00571 /** 00572 * Return a string representation for debugging 00573 */ 00574 public String toString() { 00575 return "Session " + sessionIndex + ": " + qConn; 00576 } 00577 //#endif 00578 00579 /** 00580 * A buffer private to this session for formatting rowIds 00581 */ 00582 byte[] buf8 = new byte[8]; 00583 public final byte[] getBuf8(long l) { 00584 ByteUtil.putLong(buf8, 0, l); 00585 return buf8; 00586 } 00587 00588 public boolean inRecovery() throws IOException { 00589 return qConn.inRecovery(); 00590 } 00591 }