KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > quadcap > sql > Connection


1 package com.quadcap.sql;
2
3 /* Copyright 1999 - 2003 Quadcap Software. All rights reserved.
4  *
5  * This software is distributed under the Quadcap Free Software License.
6  * This software may be used or modified for any purpose, personal or
7  * commercial. Open Source redistributions are permitted. Commercial
8  * redistribution of larger works derived from, or works which bundle
9  * this software requires a "Commercial Redistribution License"; see
10  * http://www.quadcap.com/purchase.
11  *
12  * Redistributions qualify as "Open Source" under one of the following terms:
13  *
14  * Redistributions are made at no charge beyond the reasonable cost of
15  * materials and delivery.
16  *
17  * Redistributions are accompanied by a copy of the Source Code or by an
18  * irrevocable offer to provide a copy of the Source Code for up to three
19  * years at the cost of materials and delivery. Such redistributions
20  * must allow further use, modification, and redistribution of the Source
21  * Code under substantially the same terms as this license.
22  *
23  * Redistributions of source code must retain the copyright notices as they
24  * appear in each source code file, these license terms, and the
25  * disclaimer/limitation of liability set forth as paragraph 6 below.
26  *
27  * Redistributions in binary form must reproduce this Copyright Notice,
28  * these license terms, and the disclaimer/limitation of liability set
29  * forth as paragraph 6 below, in the documentation and/or other materials
30  * provided with the distribution.
31  *
32  * The Software is provided on an "AS IS" basis. No warranty is
33  * provided that the Software is free of defects, or fit for a
34  * particular purpose.
35  *
36  * Limitation of Liability. Quadcap Software shall not be liable
37  * for any damages suffered by the Licensee or any third party resulting
38  * from use of the Software.
39  */

40
41 import java.io.IOException JavaDoc;
42
43 import java.util.ArrayList JavaDoc;
44 import java.util.Enumeration JavaDoc;
45 import java.util.Hashtable JavaDoc;
46 import java.util.List JavaDoc;
47 import java.util.Random JavaDoc;
48
49 import java.sql.SQLException JavaDoc;
50
51 import com.quadcap.sql.index.Btree;
52 import com.quadcap.sql.index.Comparator;
53
54 import com.quadcap.sql.file.BlockFile;
55 import com.quadcap.sql.file.Log;
56
57 import com.quadcap.sql.lock.Lock;
58 import com.quadcap.sql.lock.LockMode;
59 import com.quadcap.sql.lock.Transaction;
60 import com.quadcap.sql.lock.TransactionObserver;
61
62 import com.quadcap.util.Debug;
63 import com.quadcap.util.Util;
64
65 /**
66  * Analagous (and mapped onto) a JDBC <code>Connection</code>, this class
67  * maintains state and locks on behalf of a single session.
68  *
69  * @author Stan Bailes
70  */

71 public class Connection implements TransactionObserver {
72     Object JavaDoc connLock;
73     Object JavaDoc fileLock;
74
75     Transaction trans = null;
76     boolean transAborted = false;
77     long transId = -1;
78     
79     String JavaDoc auth;
80     boolean writeLog = true;
81     boolean autoCommit = true;
82     Database db;
83     BlockFile file;
84     Hashtable JavaDoc rlocks = new Hashtable JavaDoc();
85     Hashtable JavaDoc wlocks = new Hashtable JavaDoc();
86
87     Lock db_IS = null;
88     Lock db_IX = null;
89
90     Hashtable JavaDoc transContext = null;
91     Random JavaDoc random = null;
92     boolean readOnly = false;
93
94     List JavaDoc sessions = new ArrayList JavaDoc();
95     int nextSession = -1;
96     boolean isClosed = false;
97
98     long lastInsertId = -1;
99    
100     int id;
101     static int lastId = 0;
102
103     /**
104      * Construct a new connection for the specified database with the specified
105      * authorization
106      *
107      * @param db the database
108      * @param user the authorization id
109      * @param passwd the password
110      *
111      * @exception SQLException thrown if bad authorization
112      */

113     public Connection(Database db, String JavaDoc auth, String JavaDoc passwd)
114         throws SQLException JavaDoc
115     {
116         this.id = lastId++;
117     this.db = db;
118     this.file = db.getFile();
119         this.connLock = new Object JavaDoc();
120         this.fileLock = file.getLock();
121     this.readOnly = db.isReadOnly();
122     setAuth(auth, passwd);
123         //#ifdef TRACE
124
if (Trace.bit(8)) {
125             Debug.println("Connection() : " + this);
126         }
127         //#endif
128
}
129
130     /**
131      * Create a new session and store it in the session table.
132      */

133     public final Session createSession() throws IOException JavaDoc, SQLException JavaDoc {
134         int sessionPos = nextSession;
135         if (nextSession == -1) {
136             sessionPos = sessions.size();
137             sessions.add(null);
138         } else {
139             nextSession = ((Number JavaDoc)sessions.get(nextSession)).intValue();
140         }
141         Session session = new Session(this, sessionPos);
142         sessions.set(sessionPos, session);
143         //Debug.println("createSession(" + sessionPos + "): " + Util.stackTrace());
144
return session;
145     }
146
147     public final void removeSession(Session session) {
148         if (sessions != null) {
149             int pos = session.sessionIndex;
150             Session sess2 = (Session)sessions.get(pos);
151             if (sess2 != session) {
152                 throw new RuntimeException JavaDoc("Bad session table");
153             }
154             sessions.set(pos, new Integer JavaDoc(nextSession));
155             nextSession = pos;
156         }
157     }
158
159         final Session getSession(int i) {
160 //- //#ifdef USE_WEAK_REFS2
161
//- WeakReference ref = (WeakReference)sessions.elementAt(i);
162
//- if (ref != null) {
163
//- Object obj = ref.get();
164
//- if (obj != null && obj instanceof Session) {
165
//- return (Session)obj;
166
//- }
167
//- }
168
//- return null;
169
//#else
170
Session s = null;
171         Object JavaDoc obj = sessions.get(i);
172         if (obj instanceof Session) s = (Session)obj;
173         return s;
174         //#endif
175
}
176
177     /**
178      * Close all the sessions in the table
179      */

180     final void closeSessions() {
181         for (int i = 0; i < sessions.size(); i++) {
182             Session session = getSession(i);
183             try {
184                 if (session != null) session.close();
185             } catch (Throwable JavaDoc t) {
186                 //#ifdef DEBUG
187
Debug.print(t);
188                 //#endif
189
}
190         }
191         sessions = null;
192         nextSession = -1;
193     }
194
195     /**
196      * Given a SQL ID, attempt to generate a fully qualified name by
197      * prepending the current schema name.
198      *
199      */

200     final String JavaDoc resolveName(String JavaDoc name) {
201     if (countDots(name) == 0 && auth != null && auth.length() > 0) {
202         return auth + "." + name;
203     } else {
204         return name;
205     }
206     }
207
208     /**
209      * Return the number of '.' delimiters in this identifier,
210      * skipping over any occurrences of '.' within delimted
211      * identifiers (i.e., literal strings surrounded by double
212      * quotes.)
213      */

214     static final int countDots(String JavaDoc name) {
215         int cnt = 0;
216         boolean quote = false;
217         for (int i = 0; i < name.length(); i++) {
218             char c = name.charAt(i);
219             if (c == '"') quote = !quote;
220             if (!quote && c == '.') cnt++;
221         }
222         return cnt;
223     }
224
225     /**
226      * Return a fully qualified column name.
227      *
228      * Column: <schema>.<table>.<column>
229      */

230     final String JavaDoc resolveColname(String JavaDoc name, Tuple cursorTuple) {
231         String JavaDoc ret = name;
232         switch (countDots(name)) {
233         case 0:
234             ret = cursorTuple.getName() + "." + name;
235             if (ret.startsWith(".")) {
236                 ret = ret.substring(1);
237             }
238             break;
239         case 1:
240             if (auth != null && auth.length() > 0) {
241                 ret = auth + "." + name;
242             }
243             break;
244         default:
245         }
246         return ret;
247     }
248
249     /**
250      * Accessor for my database
251      */

252     public final Database getDatabase() { return db; }
253
254     /**
255      * Accessor for my log
256      */

257     private final Log getLog() { return db.getLog(); }
258
259     /**
260      * Accessor for my file
261      */

262     public final BlockFile getFile() { return file; }
263
264     /**
265      * Accessor for my 'Random' object
266      */

267     Random JavaDoc getRandom() {
268     if (random == null) random = new Random JavaDoc();
269     return random;
270     }
271
272     /**
273      * The BLOB code (InsertBlob) needs access to the temp file.
274      */

275     final BlockFile getTempFile(boolean holdRef) throws IOException JavaDoc {
276     return db.getTempFile(holdRef);
277     }
278
279     /**
280      * At statement end, if autoCommit is TRUE, we commit (or rollback)
281      * the transaction
282      */

283     void endStatement(Session session, boolean abort)
284         throws IOException JavaDoc, SQLException JavaDoc
285     {
286         if (autoCommit && trans != null) {
287             if (abort) {
288                 rollbackTransaction();
289             } else {
290                 endTransaction();
291             }
292         }
293     }
294             
295     /**
296      * Commit the current transaction
297      *
298      * PRE: cursors closed, statements finished.
299      * POST: transaction commit/rollback, locks released
300      */

301     public final void endTransaction()
302         throws IOException JavaDoc, SQLException JavaDoc
303     {
304         //#ifdef TRACE
305
if (Trace.bit(9)) {
306             Debug.println(toString() + ".endTransaction()");
307         }
308         //#endif
309
SQLException JavaDoc se = null;
310     IOException JavaDoc io = null;
311         synchronized (fileLock) {
312         synchronized (connLock) {
313             try {
314                 if (transContext != null) {
315                     try {
316                         finishContexts();
317                     } finally {
318                         transContext = null;
319                     }
320                 }
321                 if (!readOnly && trans != null) {
322                     //#ifdef TRACE
323
if (Trace.bit(9)) {
324                         Debug.println(toString() + ": db.commitTransaction()");
325                     }
326                     //#endif
327
try {
328                         db.commitTransaction(trans);
329                     } catch (Exception JavaDoc e) {
330                         //#ifdef DEBUG
331
Debug.print(e);
332                         //#endif
333
throw new RuntimeException JavaDoc(e.toString());
334                     }
335                 }
336             } catch (SQLException JavaDoc ex) {
337                 se = ex;
338                 checkAborted();
339                 db.rollbackTransaction(trans);
340             } catch (IOException JavaDoc ex) {
341                 io = ex;
342                 checkAborted();
343                 db.rollbackTransaction(trans);
344             } finally {
345                 releaseLocks();
346             }
347             if (se != null) throw se;
348             if (io != null) throw io;
349         }
350         }
351     }
352
353     /**
354      * Roll back this transaction. Toss the pending lists (and statement
355      * contexts, right? XXX), close any cursors and get the database to
356      * undo anything we've actually done to the database.
357      */

358     public final void rollbackTransaction()
359         throws IOException JavaDoc, SQLException JavaDoc
360     {
361         //#ifdef TRACE
362
if (Trace.bit(9)) {
363             Debug.println(toString() + ".rollbackTransaction()");
364         }
365         //#endif
366
synchronized (fileLock) {
367         synchronized (connLock) {
368             try {
369                 transContext = null;
370                 if (trans != null) {
371                     db.rollbackTransaction(trans);
372                 }
373             } finally {
374                 releaseLocks();
375             }
376         }
377         }
378     }
379
380     /**
381      * TransactionObserver implementation: abort the current transaction
382      */

383     public void abort(Transaction t) throws IOException JavaDoc {
384         transContext = null;
385         try {
386             releaseLocks();
387             db.releaseLocks(t);
388         } finally {
389             transAborted = true;
390         }
391     }
392
393     /**
394      * Statement rollback
395      */

396     final void rollbackStatement(Session s) throws IOException JavaDoc, SQLException JavaDoc {
397         synchronized (fileLock) {
398         synchronized (connLock) {
399             checkAborted();
400             if (trans != null) {
401                 db.rollbackStatement(trans, s.getStmtId());
402             }
403         }
404         }
405     }
406
407     final void checkAborted() throws SQLException JavaDoc {
408         synchronized (connLock) {
409             if (transAborted) {
410                 transAborted = false;
411                 throw new SQLException JavaDoc("Transaction aborted");
412             }
413         }
414     }
415
416     /**
417      * Return the current transaction
418      */

419     public final Transaction getTransaction() { return trans; }
420
421     /**
422      * Return the current transaction id as a long.
423      */

424     public final long getTransactionId() { return transId; }
425
426     /**
427      * Return (lazy create) the current Transaction
428      */

429     final Transaction makeTransaction() throws SQLException JavaDoc
430     {
431         if (trans == null) {
432             synchronized (fileLock) {
433             synchronized (connLock) {
434                 checkAborted();
435                 if (trans == null) {
436                     try {
437                         trans = db.makeTransaction(writeLog);
438                     } catch (IOException JavaDoc ex) {
439                         throw DbException.wrapThrowable(ex);
440                     }
441                     trans.setObserver(this);
442                     transId = trans.getTransactionId();
443                 }
444             }
445             }
446         }
447     return trans;
448     }
449
450     /**
451      * Get the database root lock in the specified mode.
452      */

453     final Lock getDbLock(int mode) throws IOException JavaDoc, SQLException JavaDoc {
454         makeTransaction();
455         switch (mode) {
456         case LockMode.IS:
457             if (db_IS == null) {
458                 synchronized (connLock) {
459                     if (db_IS == null) {
460                         db_IS = db.getLockManager().getLock(trans, null, "db", LockMode.IS);
461                     }
462                 }
463             }
464             return db_IS;
465         case LockMode.IX:
466             if (db_IX == null) {
467                 synchronized (connLock) {
468                     if (db_IX == null) {
469                         db_IX = db.getLockManager().getLock(trans, null, "db", LockMode.IX);
470                     }
471                 }
472             }
473             return db_IX;
474         default:
475             synchronized (connLock) {
476                 return db.getLockManager().getLock(trans, null, "db", mode);
477             }
478         }
479     }
480
481     /**
482      * Helper routine to obtain a write lock on the specified table
483      */

484     final void getTableWriteLock(String JavaDoc tableName)
485     throws SQLException JavaDoc, IOException JavaDoc
486     {
487     if (!readOnly && !inRecovery()) {
488             synchronized (connLock) {
489                 if (wlocks.get(tableName) == null) {
490                     Lock dbLock = getDbLock(LockMode.IX);
491                     db.getLockManager().getLock(trans, dbLock, tableName, LockMode.X);
492                     wlocks.put(tableName, tableName);
493                 }
494             }
495     }
496     }
497
498     /**
499      * Helper routine to obtain a read lock on the specified table
500      */

501     final void getTableReadLock(String JavaDoc tableName)
502     throws SQLException JavaDoc, IOException JavaDoc
503     {
504     if (!readOnly && !inRecovery()) {
505             synchronized (connLock) {
506                 if (rlocks.get(tableName) == null) {
507                     Lock dbLock = getDbLock(LockMode.IS);
508                     db.getLockManager().getLock(trans, dbLock, tableName, LockMode.S);
509                     rlocks.put(tableName, tableName);
510                 }
511             }
512     }
513     }
514
515     /**
516      * At transaction end, release our cached locks.
517      * PRECONDITION: connLock monitor entered
518      */

519     final void releaseLocks() {
520     if (!readOnly) {
521             //#ifdef TRACE
522
if (Trace.bit(9)) {
523                 Debug.println(toString() + ".releaseLocks()");
524             }
525             //#endif
526
if (trans != null) {
527                 wlocks.clear();
528                 rlocks.clear();
529                 db_IS = null;
530                 db_IX = null;
531                 trans = null;
532                 transId = -1;
533             }
534     }
535     }
536
537     /**
538      * Public setter for the 'autoCommit' flag.
539      */

540     public final void setAutoCommit(boolean b) {
541     autoCommit = b;
542     }
543
544     /**
545      * Public accessor for the 'autoCommit' flag.
546      */

547     public final boolean getAutoCommit() {
548     return autoCommit;
549     }
550
551     /**
552      * Each context object represents some state retained on behalf
553      * of a constraint. The 'finish()' method of the context is used
554      * to perform any constraint-specific processing at the end of
555      * a statement/transaction.
556      */

557     final void finishContexts()
558     throws SQLException JavaDoc, IOException JavaDoc
559     {
560     SQLException JavaDoc se = null;
561     IOException JavaDoc io = null;
562
563         int maxp = 0;
564         for (int p = 0; p <= maxp; p++) {
565             Enumeration JavaDoc keys = transContext.keys();
566             while (keys.hasMoreElements()) {
567                 Object JavaDoc key = keys.nextElement();
568                 StatementContext sc = (StatementContext)transContext.get(key);
569                 int sp = sc.priority();
570                 maxp = Math.max(maxp, sp);
571                 if (sp == p) {
572                     try {
573                         //#ifdef TRACE
574
if (Trace.bit(10)) {
575                             Debug.println(toString() + ": Finish " +
576                                           Table.strip(sc.getClass().getName()));
577                         }
578                         //#endif
579
sc.finish(false);
580                     } catch (SQLException JavaDoc ex) {
581                         se = ex;
582                     } catch (IOException JavaDoc ex) {
583                         io = ex;
584                     }
585                 }
586             }
587         }
588     if (se != null) throw se;
589     if (io != null) throw io;
590     }
591
592     /**
593      * Set the current authorization code
594      */

595     public final void setAuth(String JavaDoc auth, String JavaDoc passwd) throws SQLException JavaDoc {
596         db.checkAuth(auth, passwd);
597     this.auth = auth;
598         this.writeLog = !auth.equals("__SYSTEM");
599     }
600
601     /**
602      * Access to 'recovery' flag
603      */

604     final boolean inRecovery() throws IOException JavaDoc { return db.inRecovery(); }
605
606     public final String JavaDoc getAuth() {
607     return auth;
608     }
609
610     /**
611      * Make a new (temporary) btree structure in the current database.
612      */

613     final Btree makeTempTree(Comparator compare) throws IOException JavaDoc {
614     BlockFile f = db.getTempFile();
615     long root = f.newPage();
616     Btree tree = new Btree(f, compare, root, true);
617     return tree;
618     }
619
620     final Btree makeTempTree() throws IOException JavaDoc {
621     BlockFile f = db.getTempFile();
622     long root = f.newPage();
623     Btree tree = new Btree(f, root, true);
624     return tree;
625     }
626
627     /**
628      * Return a saved context
629      */

630     final StatementContext getContext(Object JavaDoc obj) {
631         if (transContext == null) return null;
632     return (StatementContext)transContext.get(obj);
633     }
634
635     /**
636      * Add a new statement context
637      */

638     final void putContext(Object JavaDoc key, StatementContext val) {
639     if (transContext == null) {
640             transContext = new Hashtable JavaDoc();
641         }
642     transContext.put(key, val);
643     }
644
645     /**
646      * Close this connection. Close all statement sessions and end the
647      * transaction.
648      */

649     public final void close() throws SQLException JavaDoc, IOException JavaDoc {
650         //#ifdef TRACE
651
if (Trace.bit(8)) {
652             Debug.println(toString() + ".close()");
653         }
654         //#endif
655
try {
656             if (!readOnly) {
657                 try {
658                     closeSessions();
659                 } finally {
660                     if (trans != null) {
661                         if (autoCommit) {
662                             endTransaction();
663                         } else {
664                             rollbackTransaction();
665                         }
666                     }
667                 }
668             }
669         } finally {
670             isClosed = true;
671             db.removeConnection();
672         }
673     }
674
675     /**
676      * Return true if this compare is closed
677      */

678     public boolean isClosed() {
679         return isClosed || file == null;
680     }
681
682     /**
683      * Return true if this connection is read-only
684      */

685     public boolean isReadOnly() {
686         return readOnly;
687     }
688
689     //#ifdef DEBUG
690
public String JavaDoc toString() {
691         return "user=" + auth + ": (" + trans + ") " + db;
692     }
693     //#endif
694

695     public String JavaDoc getLabel() {
696         return "Connection " + id + ": (" + trans + ")";
697     }
698
699     long getLastInsertId() { return lastInsertId; }
700     void setLastInsertId(long id) { lastInsertId = id; }
701 }
702  
703
Popular Tags