KickJava   Java API By Example, From Geeks To Geeks.

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


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.BufferedWriter JavaDoc;
42 import java.io.File JavaDoc;
43 import java.io.FileOutputStream JavaDoc;
44 import java.io.IOException JavaDoc;
45 import java.io.OutputStream JavaDoc;
46 import java.io.OutputStreamWriter JavaDoc;
47 import java.io.PrintWriter JavaDoc;
48
49 import java.util.ArrayList JavaDoc;
50 import java.util.Calendar JavaDoc;
51 import java.util.Collection JavaDoc;
52 import java.util.Enumeration JavaDoc;
53 import java.util.Iterator JavaDoc;
54 import java.util.HashMap JavaDoc;
55 import java.util.List JavaDoc;
56 import java.util.Map JavaDoc;
57 import java.util.Properties JavaDoc;
58
59 import java.util.zip.GZIPOutputStream JavaDoc;
60
61 import java.sql.SQLException JavaDoc;
62
63 import com.quadcap.sql.file.BlockFile;
64 import com.quadcap.sql.file.ByteUtil;
65 import com.quadcap.sql.file.Datafile;
66 import com.quadcap.sql.file.Log;
67 import com.quadcap.sql.file.SubPageManager;
68
69 import com.quadcap.sql.index.BCursor;
70 import com.quadcap.sql.index.Btree;
71
72 import com.quadcap.sql.lock.Transaction;
73
74 import com.quadcap.util.collections.LongIterator;
75 import com.quadcap.util.collections.LongMap;
76
77 import com.quadcap.util.ConfigString;
78 import com.quadcap.util.Debug;
79 import com.quadcap.util.Util;
80  
81 /**
82  * This class implements the QED SQL database outer API.
83  *
84  * @author Stan Bailes
85  */

86 public class Database extends Datafile {
87     QDriver driver;
88     Btree tableIndex;
89     // map index name to table name
90
Btree indexIndex;
91     Btree blobPermRefCounts;
92     Map JavaDoc tableMap = new HashMap JavaDoc();
93     MultiSet forwardDeps = null;
94     MultiSet reverseDeps = null;
95     long rootBlock = 0;
96     public DatabaseRoot nroot;
97
98     /** The Morgue; BLOB/CLOBs with zero (persistent) refcounts:
99         blobRef -> any */

100     LongMap blobMorgue = new LongMap(32);
101
102     /** Map blob -> Vector<transid> */
103     LongMap blobActivity = new LongMap(128);
104     
105     
106     int connectionCount = 0;
107     int closeCount = 0;
108     long nextTransId = 0;
109
110     public Object JavaDoc driverLock;
111
112     static final ConfigString backupClass
113     = ConfigString.find("qed.backupClass",
114             "com.quadcap.sql.tools.XmlDump");
115     /**
116      * Constructor
117      */

118     public Database() {}
119
120     public void init(QDriver driver, String JavaDoc url,
121                      String JavaDoc fileName, Properties JavaDoc props)
122     throws IOException JavaDoc, SQLException JavaDoc
123     {
124     this.driver = driver;
125         init(url, fileName, props);
126     }
127
128     /**
129      * Create a new root block
130      */

131     public void createRoot(String JavaDoc fileName, Properties JavaDoc props)
132         throws IOException JavaDoc
133     {
134         this.rootBlock = getFile().getUserBlock(0);
135         this.root = this.nroot = new DatabaseRoot(this, fileName, props);
136         if (!readOnly && !inMemory) {
137             new File JavaDoc(fileName, "logfile").delete();
138             flushRoot();
139         }
140      }
141
142     /**
143      * Read the root block from an existing database
144      */

145     public void readRoot() throws IOException JavaDoc {
146         this.rootBlock = getFile().getUserBlock(0);
147         root = nroot = (DatabaseRoot)file.getObject(rootBlock);
148         nroot.setDatabase(this);
149     }
150
151     /**
152      * Write the database root
153      */

154     public void flushRoot() throws IOException JavaDoc {
155     if (!inMemory /*?*/ && !readOnly) {
156             getFile().updateObject(rootBlock, root);
157         }
158     }
159
160     /**
161      * Get ready to rumble!!!
162      */

163     public void bootFromRoot(boolean restart) throws IOException JavaDoc {
164         nextTransId = root.getNextTransId();
165         nroot.setNextTransId((nextTransId | 0xff) + 1);
166
167         blobPermRefCounts = new Btree(file,
168                                       nroot.getBlobRefCountRoot(), !restart);
169         tableIndex = new Btree(file, nroot.getRelationIndexNode(), !restart);
170         indexIndex = new Btree(file, nroot.getIndexIndexNode(), !restart);
171         forwardDeps =
172             new MultiSet(new Btree(file, nroot.getForwardDepsNode(),
173                                    !restart));
174         reverseDeps =
175             new MultiSet(new Btree(file, nroot.getReverseDepsNode(),
176                                    !restart));
177     }
178
179     /**
180      * Return the next available transaction ID.
181      */

182     public long getNextTransId() throws IOException JavaDoc {
183         synchronized (fileLock) {
184             long t = nextTransId++;
185             if (!inMemory && (nextTransId & 0xff) == 0) {
186                 nroot.setNextTransId(nextTransId + 0x100);
187                 flushRoot();
188             }
189             return t;
190         }
191     }
192
193     /**
194      * Figure out if it's time to do a backup.
195      */

196     public void maybeBackup() throws IOException JavaDoc {
197         if (inMemory) return;
198     // are backups enabled?
199
int days = getBackupDays();
200     if (days == 0) return;
201
202     // is today the day?
203
Calendar JavaDoc now = Calendar.getInstance();
204     int nowDay = now.get(Calendar.DAY_OF_WEEK);
205     if (((days >> nowDay) & 1) == 0) return;
206     
207     // is now the time?
208
int time = getBackupTime();
209     int today = now.get(Calendar.DAY_OF_YEAR);
210     int nowTime =
211         now.get(Calendar.HOUR_OF_DAY) * 60 +
212         now.get(Calendar.MINUTE);
213     if (nowTime < time || today == getBackupLastDay()) return;
214
215     // cycle the old backup files
216
int count = getBackupCount();
217     for (int i = count-2; i >= 0; i--) {
218         makeBackupFile(i).renameTo(makeBackupFile(i+1));
219     }
220
221     // write new backup in correct format
222
OutputStream JavaDoc w = new FileOutputStream JavaDoc(makeBackupFile(0));
223     if (getBackupFormat().equals("xml.gz")) {
224         w = new GZIPOutputStream JavaDoc(w);
225     }
226     OutputStreamWriter JavaDoc ow = new OutputStreamWriter JavaDoc(w);
227     BufferedWriter JavaDoc bw = new BufferedWriter JavaDoc(ow);
228     
229     // create the backup helper and do it.
230
try {
231         java.sql.Connection JavaDoc conn =
232                 driver.makeConnection(this, "backup", null);
233         Class JavaDoc c = Class.forName(backupClass.toString());
234         Backup b = (Backup)c.newInstance();
235         b.backup(conn, bw);
236         bw.close();
237     } catch (IOException JavaDoc e) {
238         throw e;
239     } catch (Throwable JavaDoc t) {
240         throw new IOException JavaDoc(t.toString());
241     }
242     setBackupLastDay(today);
243     }
244
245     File JavaDoc makeBackupFile(int n) throws IOException JavaDoc {
246     File JavaDoc bdir = new File JavaDoc(getBackupDir());
247     if (!bdir.exists()) {
248         if (!bdir.mkdirs()) {
249         throw new IOException JavaDoc("can't create backup directory: " +
250                       bdir.getAbsolutePath());
251         }
252     }
253     String JavaDoc name = "backup-" + n + "." + getBackupFormat();
254     return new File JavaDoc(bdir, name);
255     }
256
257     /**
258      * TODO: XXX Unicode!!!
259      * Add a new named index for the specified table.
260      */

261     public void addIndex(String JavaDoc indexName, String JavaDoc tableName)
262     throws IOException JavaDoc
263     {
264         synchronized (fileLock) {
265             indexIndex.set(indexName.getBytes(), tableName.getBytes());
266         }
267     }
268
269
270     /**
271      * Return the table for the index of the specified name.
272      */

273     public String JavaDoc getTableForIndex(String JavaDoc indexName) throws IOException JavaDoc {
274         synchronized (fileLock) {
275             byte[] ret = indexIndex.get(indexName.getBytes());
276             if (ret == null) return null;
277             return new String JavaDoc(ret);
278         }
279     }
280
281     /**
282      * Remove the specified index
283      */

284     public void deleteIndex(String JavaDoc indexName) throws IOException JavaDoc {
285         synchronized (fileLock) {
286             indexIndex.delete(indexName.getBytes());
287         }
288     }
289
290     /**
291      * RENAME TABLE percolates into the index index...
292      */

293     void renameIndexedTable(String JavaDoc oldN, String JavaDoc newN) throws IOException JavaDoc {
294         synchronized (fileLock) {
295             BCursor bc = indexIndex.getCursor(false);
296             if (bc != null) {
297                 try {
298                     while (bc.next()) {
299                         String JavaDoc name = new String JavaDoc(bc.getVal());
300                         if (name.equals(oldN)) {
301                             bc.prev();
302                             bc.replace(newN.getBytes());
303                             bc.next();
304                         }
305                     }
306                 } finally {
307                     bc.release();
308                 }
309             }
310         }
311     }
312
313     /**
314      * Return the table identity for the table
315      *
316      * @exception SQLException if the table can't be located
317      */

318     public long getTableIdentity(String JavaDoc name)
319         throws IOException JavaDoc, SQLException JavaDoc
320     {
321         synchronized (fileLock) {
322             if (inMemory) {
323                 Table t = (Table)getRelation(name);
324                 return t.tableIdentity;
325             } else {
326                 BCursor tc = tableIndex.getCursor(true);
327                 try {
328                     if (tc.seek(name.getBytes()) &&
329                         tc.getValLen() >= 16) {
330                         return ByteUtil.getLong(tc.getValBuf(), 8);
331                     } else {
332                         throw new SQLException JavaDoc("getTableIdentity(" +
333                                                name + "): no table");
334                     }
335                 } finally {
336                     tc.release();
337                 }
338             }
339         }
340     }
341
342     /**
343      * The "table identity" is a long value stored along with the table
344      * stream pointer, so that it can easily be updated as long as the index
345      * is in memory, without touching any data pages.
346      */

347     public void updateTableIdentity(String JavaDoc name, long num)
348         throws IOException JavaDoc, SQLException JavaDoc
349     {
350         synchronized (fileLock) {
351             if (inMemory) {
352                 Table t = (Table)getRelation(name);
353                 t.tableIdentity = num;
354             } else {
355                 BCursor tc = tableIndex.getCursor(true);
356                 try {
357                     if (tc.seek(name.getBytes())) {
358                         int cnt = tc.getVal(temp);
359                         ByteUtil.putLong(temp, 8, num);
360                         tc.replace(temp, 0, cnt);
361                         return;
362                     } else {
363                         throw new SQLException JavaDoc("getTableIdentity(" +
364                                                name + "): no table");
365                     }
366                 } finally {
367                     tc.release();
368                 }
369             }
370         }
371     }
372
373     /**
374      * Helper to execute a single SQL statement (in its own connection)
375      * and return a result set.
376      */

377     public java.sql.ResultSet JavaDoc execute(String JavaDoc sql)
378         throws SQLException JavaDoc, IOException JavaDoc
379     {
380         Connection c = new Connection(this, "__NO_AUTH__", null);
381         Session session = c.createSession();
382         return execute(session, sql);
383     }
384
385     /**
386      * Helper to execute a single SQL statement for the given session.
387      */

388     public java.sql.ResultSet JavaDoc execute(Session session, String JavaDoc sql)
389         throws IOException JavaDoc, SQLException JavaDoc
390     {
391     SQLParser p = new SQLParser(session, sql, true);
392         Stmt s = null;
393         try {
394             s = p.statement();
395         } catch (Throwable JavaDoc t) {
396             Debug.print(t);
397         }
398         
399         if (s == null) {
400             throw new SQLException JavaDoc("Parse error", "42000");
401         }
402         session.doStatement(s);
403         return session.getResultSet();
404     }
405
406     /**
407      * Return the table/view with the specified name
408      *
409      * XXX Fix this "cache" implementation
410      */

411     public Relation getRelation(String JavaDoc name)
412         throws IOException JavaDoc
413     {
414         Relation t = null;
415         synchronized (fileLock) {
416             t = (Relation)tableMap.get(name);
417             if (!inMemory && t == null) {
418                 BCursor tc = tableIndex.getCursor(true);
419                 try {
420                     if (tc.seek(name.getBytes())) {
421                         byte[] d = tc.getValBuf();
422                         t = (Relation)getFile().getObject(ByteUtil.getLong(d, 0));
423                         if (t != null) {
424                             tableMap.put(name, t);
425                         }
426                     }
427                 } finally {
428                     tc.release();
429                 }
430             }
431         }
432         //#ifdef DEBUG
433
if (Trace.bit(0)) {
434             Debug.println("getRelation(" + name + ") = " +
435                           ((t == null) ? "NOT FOUND" : "FOUND"));
436         }
437         //#endif
438
return t;
439     }
440
441     /**
442      * Add a new table/view
443 ' */

444     public void addRelation(Relation table)
445     throws IOException JavaDoc, SQLException JavaDoc
446     {
447     synchronized (fileLock) {
448         String JavaDoc name = table.getName();
449         //#ifdef TRACE
450
if (Trace.bit(0)) {
451         Debug.println("addRelation(" + name + ")");
452         }
453         //#endif
454
if (tableMap.get(name) == null) {
455                 tableMap.put(name, table);
456                 if (!inMemory) {
457                     BCursor tc = tableIndex.getCursor(true);
458                     try {
459                         byte[] tkey = name.getBytes();
460                         if (!tc.seek(name.getBytes())) {
461                             long ref = getFile().putObject(table);
462                             ByteUtil.putLong(temp, 0, ref);
463                             ByteUtil.putLong(temp, 8, 1); // start counting at '1'
464
tc.insert(tkey, tkey.length, temp, 0, 16);
465                         }
466                     } finally {
467                         tc.release();
468                     }
469                 }
470             } else {
471                 throw new SQLException JavaDoc("Table/view already exists: " + name,
472                                        "42000");
473             }
474         }
475     }
476
477     /**
478      * Delete the specified table/view
479      */

480     public void removeRelation(String JavaDoc name)
481     throws IOException JavaDoc, SQLException JavaDoc
482     {
483     synchronized (fileLock) {
484         //#ifdef DEBUG
485
if (Trace.bit(0)) {
486                 Debug.println("removeRelation(" + name + ")");
487             }
488         //#endif
489
Relation t = getRelation(name);
490         if (t == null) {
491         throw new SQLException JavaDoc("No such table/view: " + name, "42000");
492         }
493         removeViewDependencies(name);
494         tableMap.remove(name);
495             if (!inMemory) {
496                 BCursor tc = tableIndex.getCursor(true);
497                 try {
498                     if (tc.seek(name.getBytes())) {
499                         getFile().removeObject(tc.getValAsLong());
500                         tc.delete();
501                     }
502                 } finally {
503                     tc.release();
504                 }
505             }
506     }
507         //#ifdef DEBUG
508
if (getRelation(name) != null) {
509             throw new SQLException JavaDoc("Relation not removed!!!");
510         }
511         //#endif
512
}
513
514     /**
515      * Rename a relation. This involves:
516      *
517      * 1. Changing the cached relation
518      * 2. Changing the btree index to the relation
519      * 3. Changing any indexIndex entries referring to the rel
520      * 4. Changing any forwardDeps or backwardDeps which
521      * refer to the relation
522      */

523     public void renameRelation(Relation r, String JavaDoc newN) throws IOException JavaDoc {
524     synchronized (fileLock) {
525             String JavaDoc oldN = r.getName();
526             r.setName(newN);
527             tableMap.remove(oldN);
528             tableMap.put(newN, r);
529             if (!inMemory) {
530                 BCursor tc = tableIndex.getCursor(true);
531                 try {
532                     if (tc.seek(oldN.getBytes())) {
533                         byte[] d = tc.getValBuf();
534                         long ref = ByteUtil.getLong(d, 0);
535                         tc.delete();
536                         byte[] nb = newN.getBytes();
537                         if (!tc.seek(nb)) {
538                             tc.insert(nb, nb.length, d, 0, 16);
539                             getFile().updateObject(ref, r);
540                         }
541                     }
542                 } finally {
543                     tc.release();
544                 }
545             }
546             if (true) {
547                 forwardDeps.rename(oldN, newN, reverseDeps);
548                 reverseDeps.rename(oldN, newN, forwardDeps);
549             }
550             if (true) {
551                 renameIndexedTable(oldN, newN);
552             }
553         }
554     }
555     
556     /**
557      * Store a modified version of the specified view/table to the
558      * store
559      */

560     public void updateRelation(Relation table) throws IOException JavaDoc {
561     synchronized (fileLock) {
562         //#ifdef TRACE
563
if (Trace.bit(0)) {
564         Debug.println("updateRelation(" + table.getName() + ")");
565         //Debug.println("updateRelation(" + table.getName() + ": " + table + ")"); // + Util.stackTrace());
566
}
567         //#endif
568
tableMap.put(table.getName(), table);
569             if (!inMemory) {
570                 BCursor tc = tableIndex.getCursor(true);
571                 try {
572                     if (tc.seek(table.getName().getBytes())) {
573                         getFile().updateObject(tc.getValAsLong(), table);
574                     } else {
575                         throw new IOException JavaDoc("Not found: " + table.getName());
576                     }
577                 } finally {
578                     tc.release();
579                 }
580             }
581         }
582     }
583
584     /**
585      * Establish the dependency link between a view and a base table
586      */

587     public void addViewDependency(String JavaDoc base, String JavaDoc view)
588     throws IOException JavaDoc
589     {
590     //#ifdef TRACE
591
if (Trace.bit(0)) {
592         Debug.println("addViewDependency(" + base + ", " + view + ")");
593     }
594     //#endif
595

596         synchronized (fileLock) {
597             forwardDeps.put(base, view);
598             reverseDeps.put(view, base);
599         }
600     }
601
602     /**
603      * Make sure that the specified view dependency is valid
604      */

605     public void checkViewDependency(String JavaDoc base, String JavaDoc view)
606     throws SQLException JavaDoc, IOException JavaDoc
607     {
608     Relation baseR = getRelation(base);
609     if (baseR == null) {
610         throw new SQLException JavaDoc("Base table not found: " + base, "42000");
611     }
612     if (baseR instanceof Table) {
613         Table baseT = (Table)baseR;
614         int modifiers = baseT.getModifiers();
615         if ((modifiers & Table.TEMPORARY) != 0) {
616         throw new SQLException JavaDoc("Can't create view of temporary " +
617                        "table " + base, "42000");
618         }
619     }
620     }
621
622     /**
623      * Return an enumeration of all views for the specified base table
624      */

625     public Enumeration JavaDoc getViews(String JavaDoc base) throws IOException JavaDoc {
626         synchronized (fileLock) {
627             return forwardDeps.get(base);
628         }
629     }
630
631     /**
632      * Return an enumeration containing all base tables for the specified
633      * view
634      */

635     public Enumeration JavaDoc getBases(String JavaDoc view) throws IOException JavaDoc {
636         synchronized (fileLock) {
637             return reverseDeps.get(view);
638         }
639     }
640
641     public void removeViewDependencies(String JavaDoc view)
642     throws SQLException JavaDoc, IOException JavaDoc
643     {
644     //#ifdef TRACE
645
if (Trace.bit(0)) {
646         Debug.println("removeViewDependencies(" + view + ")");
647     }
648     //#endif
649
synchronized (fileLock) {
650             Enumeration JavaDoc views = getViews(view);
651             if (views.hasMoreElements()) {
652                 throw new SQLException JavaDoc("view has dependencies: " + view,
653                                        "42000");
654             }
655             Enumeration JavaDoc bases = getBases(view);
656             while (bases.hasMoreElements()) {
657                 forwardDeps.delete(bases.nextElement().toString(), view);
658             }
659         }
660     }
661
662     /**
663      * This hacky crack of encapsulation is for sql.meta.MetaTables, to
664      * support DatabaseMetaData.getTables(). All he really needs is
665      * an Iterator which returns the table names....
666      */

667 // public Btree getRelationIndex() {
668
// return tableIndex;
669
// }
670

671     public Iterator JavaDoc getRelationNameIterator() throws IOException JavaDoc {
672         if (inMemory) {
673             return tableMap.keySet().iterator();
674         } else {
675             final BCursor tc = tableIndex.getCursor(false);
676             return new Iterator JavaDoc() {
677                     boolean called = false;
678                     boolean last = false;
679                     boolean closed = false;
680                     public boolean hasNext() {
681                         try {
682                             if (!closed && !called) {
683                                 last = tc.next();
684                                 called = true;
685                             }
686                         } catch (IOException JavaDoc ex) {
687                             last = false;
688                         }
689                         if (!last && !closed) {
690                             closed = true;
691                             try {
692                                 tc.release();
693                             } catch (Throwable JavaDoc t) {
694                                 Debug.print(t);
695                             }
696                         }
697                         return last;
698                     }
699                     public Object JavaDoc next() {
700                         Object JavaDoc ret = null;
701                         try {
702                             if (!called && !closed) {
703                                 last = tc.next();
704                             }
705                             if (!last) {
706                                 if (!closed) {
707                                     closed = true;
708                                     try {
709                                         tc.release();
710                                     } catch (Throwable JavaDoc t) {
711                                         Debug.print(t);
712                                     }
713                                 }
714                             } else {
715                                 ret = new String JavaDoc(tc.getKey());
716                                 called = false;
717                             }
718                         } catch (IOException JavaDoc ex) {
719                             ret = "<Exception: " + ex + ">";
720                         }
721                         return ret;
722                     }
723                     public void remove() {
724                         try {
725                             tc.delete();
726                         } catch (IOException JavaDoc e) {
727                             Debug.print(e);
728                             throw new RuntimeException JavaDoc("Error deleting record", e);
729                         }
730                     }
731                 };
732         }
733     }
734     
735 // public Collection getRelationNames() throws IOException {
736
// ArrayList list = new ArrayList();
737
// synchronized (fileLock) {
738
// if (inMemory) {
739
// return tableMap.keySet();
740
// }
741
// BCursor tc = tableIndex.getCursor(false);
742
// try {
743
// while (tc.next()) {
744
// list.add(new String(tc.getKey()));
745
// }
746
// } finally {
747
// tc.release();
748
// }
749
// }
750
// return list;
751
// }
752

753     /**
754      * Add a new connection (just remember the count...)
755      */

756     public int addConnection() {
757         synchronized (driverLock) {
758             //#ifdef TRACE
759
if (Trace.bit(8)) {
760                 Debug.println("addConnection, count now: " + (connectionCount+1));
761             }
762             //#endif
763
return ++connectionCount;
764         }
765     }
766
767     /**
768      * Sleight of hand to get my driver interface
769      */

770     public QDriver getDriver() {
771     return driver;
772     }
773
774     /**
775      * Remove a connection. If the connection count drops to zero, we
776      * get crafty.
777      */

778     public void removeConnection() {
779         synchronized (driverLock) {
780             if (--connectionCount == 0 && closeCount++ < 1) {
781                 try {
782                     getLog().checkpoint();
783                 } catch (Throwable JavaDoc e) {
784                 }
785             }
786             //#ifdef TRACE
787
if (Trace.bit(8)) {
788                 Debug.println("removeConnection, count now: " + (connectionCount));
789             }
790             //#endif
791
if (connectionCount == 0 && closeCount == 1) {
792                 driver.closeDatabase(origFileName);
793             }
794         }
795     }
796
797     /**
798      * Return the number of active connections
799      */

800     public int getConnectionCount() {
801     return connectionCount;
802     }
803
804     /**
805      * Return the backup directory name
806      */

807     public String JavaDoc getBackupDir() {
808     return nroot.getBackupDir();
809     }
810     
811     /**
812      * Set the backup directory
813      */

814     public void setBackupDir(String JavaDoc str) throws IOException JavaDoc {
815     nroot.setBackupDir(str);
816     flushRoot();
817     }
818
819     /**
820      * Return the backup count
821      */

822     public int getBackupCount() {
823     return nroot.getBackupCount();
824     }
825
826     /**
827      * Set the backup count
828      */

829     public void setBackupCount(int count) throws IOException JavaDoc {
830     nroot.setBackupCount(count);
831     flushRoot();
832     }
833
834     /**
835      * Return the backup days
836      */

837     public int getBackupDays() {
838     return nroot.getBackupDays();
839     }
840
841     /**
842      * Set the backup days
843      */

844     public void setBackupDays(int count) throws IOException JavaDoc {
845     nroot.setBackupDays(count);
846     flushRoot();
847     }
848
849     /**
850      * Return the backup last day
851      */

852     public int getBackupLastDay() {
853     return nroot.getBackupLastDay();
854     }
855
856     /**
857      * Set the backup last day
858      */

859     public void setBackupLastDay(int day) throws IOException JavaDoc {
860     nroot.setBackupLastDay(day);
861     flushRoot();
862     }
863
864     /**
865      * Return the backup time
866      */

867     public int getBackupTime() {
868     return nroot.getBackupTime();
869     }
870
871     /**
872      * Set the backup time
873      */

874     public void setBackupTime(int time) throws IOException JavaDoc {
875     nroot.setBackupTime(time);
876     flushRoot();
877     }
878
879     /**
880      * Return the backup format
881      */

882     public String JavaDoc getBackupFormat() {
883     return nroot.getBackupFormat();
884     }
885
886     /**
887      * Set the backup format
888      */

889     public void setBackupFormat(String JavaDoc str) throws IOException JavaDoc {
890     nroot.setBackupFormat(str);
891     flushRoot();
892     }
893
894     /**
895      * Check the specified authentication credentials; throw an exception
896      * if authorization shouldn't be granted.
897      *
898      * We really don't care about passwords. This is an embedded database,
899      * remember; it's really just a fancy API on top of a simple file!
900      */

901     public void checkAuth(String JavaDoc user, String JavaDoc passwd) throws SQLException JavaDoc {
902         // XXX impl
903
}
904
905     /**
906      * Return the current persistent refcount for the specified blob.
907      */

908     public int getPermBlobRefCount(long blob) throws IOException JavaDoc {
909     synchronized (fileLock) {
910         ByteUtil.putLong(key, 0, blob);
911         byte[] val = blobPermRefCounts.get(key);
912         int refcount = val == null ? 0 : ByteUtil.getInt(val, 0);
913         return refcount;
914     }
915     }
916
917
918     //------------------------------------------------------------------
919
// private functions
920
//------------------------------------------------------------------
921

922     private byte[] key = new byte[8];
923     
924     public int refCountBlob(long transId, long blob, int incr)
925         throws IOException JavaDoc
926     {
927     synchronized (fileLock) {
928         // find the current refcount
929
ByteUtil.putLong(key, 0, blob);
930         byte[] val = blobPermRefCounts.get(key);
931         int refcount = val == null ? 0 : ByteUtil.getInt(val, 0);
932             int preRefcount = refcount;
933
934         // if refcount 0 -> 1, remove from morgue
935
if (refcount == 0 && incr == 1) {
936                 val = new byte[4];
937         delMorgue(blob);
938         }
939         refcount += incr;
940         if (refcount < 0) {
941         throw new RuntimeException JavaDoc("freePermBlobRef(" + blob +
942                        "), refcount negative");
943         }
944
945         if (refcount <= 0) {
946                 if (preRefcount > 0) {
947                     addMorgue(blob, transId);
948                 }
949         blobPermRefCounts.delete(key);
950         } else {
951         // otherwise, update perm refcount
952
ByteUtil.putInt(val, 0, refcount);
953         blobPermRefCounts.set(key, val);
954         }
955
956         return refcount;
957     }
958     }
959
960     /**
961      * Add a blob/clob to the morgue
962      */

963     public void addMorgue(long blob, long transId) {
964     synchronized (fileLock) {
965         blobMorgue.put(blob, new Long JavaDoc(transId));
966     }
967     }
968
969     /**
970      * Remove a blob/clob from the morgue
971      */

972     public void delMorgue(long blob) {
973     synchronized (fileLock) {
974         blobMorgue.remove(blob);
975     }
976     }
977
978     /**
979      * Enumerate the morgue.
980      */

981     public LongIterator enumerateMorgue() {
982     return blobMorgue.keys();
983     }
984
985     /**
986      * Called during a checkpoint operation.
987      *
988      * @param activeTransactions is a map whose keys are the set of
989      * ids of active transactions.
990      */

991     public void checkpointHandler(LongMap activeTransactions)
992         throws IOException JavaDoc
993     {
994         emptyMorgue(activeTransactions);
995     }
996     
997     /**
998      * Empty the BLOB morgue. Remove any BLOBS in the morge that were
999      * deleted by transactions which have committed.
1000     */

1001    public void emptyMorgue(LongMap activeTransactions)
1002    throws IOException JavaDoc
1003    {
1004        // ---- temporarily disassociate the log from the file so we don't
1005
// ---- log the file writes we're about to do. This is OK because
1006
// ---- we're doing a checkpoint now.
1007
Log saveLog = getLog();
1008    getFile().setLog(null);
1009        try {
1010            synchronized (fileLock) {
1011                LongIterator iter = enumerateMorgue();
1012                while (iter.hasNext()) {
1013                    long blob = iter.nextLong();
1014                    long transId = ((Long JavaDoc)(blobMorgue.get(blob))).longValue();
1015                    if (activeTransactions.get(transId) == null) {
1016                        try {
1017                            getFile().freeStream(blob);
1018                        } catch (Throwable JavaDoc t) {
1019                            Debug.println("Error removing blob: " + blob);
1020                            Debug.print(t);
1021                        }
1022                        iter.remove();
1023                    }
1024                }
1025            }
1026        } finally {
1027            // ---- restore the {file <--> log} association
1028
getFile().setLog(saveLog);
1029        }
1030    }
1031
1032    Session zSession = null;
1033    Connection zConn = null;
1034
1035    final Session getSession(Transaction t) throws IOException JavaDoc, SQLException JavaDoc {
1036        if (zSession == null) {
1037            synchronized (this) {
1038                if (zSession == null) {
1039                    zConn = new Connection(this, "__SYSTEM", null);
1040                    zSession = zConn.createSession();
1041                }
1042            }
1043        }
1044        zConn.transId = t.getTransactionId(); // XXX what about sync?
1045
zConn.trans = t;
1046        return zSession;
1047    }
1048
1049    //#ifdef DEBUG
1050
public void display(PrintWriter JavaDoc w) throws IOException JavaDoc {
1051        w.println("Root:");
1052        w.println(" blobs: " + nroot.getBlobRefCountRoot());
1053        w.println(" relations: " + nroot.getRelationIndexNode());
1054        w.println(" indexes: " + nroot.getIndexIndexNode());
1055        w.println(" forward deps: " + nroot.getForwardDepsNode());
1056        w.println(" reverse deps: " + nroot.getReverseDepsNode());
1057        w.println(" next trans: " + nroot.getNextTransId());
1058        if (true) {// iterate tables, build node map
1059
}
1060        if (true) { // display indexes, update node map
1061
w.println("indexIndex:");
1062            indexIndex.display(w);
1063            w.println("tableIndex:");
1064            tableIndex.display(w);
1065        }
1066        if (true) { // traverse freelists, update node map
1067
w.println("forwardDeps:");
1068            forwardDeps.display(w);
1069            w.println("reverseDeps:");
1070            reverseDeps.display(w);
1071        }
1072        if (true) { // search for blocks missing from node map
1073
}
1074        if (true) { // dump node map in block order
1075
}
1076        // traverse block file with node map used for annotations.
1077
}
1078    //#endif
1079

1080    /**
1081      * Convenience method to write a new row to the database
1082      */

1083    public long putRow(Session session, BlockFile f,
1084                       Tuple t, Row row)
1085    throws SQLException JavaDoc, IOException JavaDoc
1086    {
1087        long rowId;
1088        if (inMemory) {
1089            rowId = f.putObject(row = new Row(row, t));
1090        } else {
1091            byte[] buf = LazyRow.writeRow(session, t, row);
1092            rowId = f.putBytes(buf);
1093        }
1094        //#ifdef TRACE
1095
if (Trace.bit(11)) {
1096            Debug.println((f == this.file ? "file" : "temp") + "." +
1097                          ((t == null) ? "temp" :
1098                           ("Table[" + t.getName() + "]")) +
1099                          ".putRow(" + row + "): " + rowId);
1100        }
1101        //#endif
1102
return rowId;
1103    }
1104
1105    /**
1106      * Convenience method to update a table row
1107      */

1108    public final void putRow(Session session, Tuple t,
1109                             long rowId, Row row)
1110    throws SQLException JavaDoc, IOException JavaDoc
1111    {
1112        //#ifdef TRACE
1113
if (Trace.bit(11)) {
1114            Debug.println("Table[" + t.getName() + "].putRow(" + row + ")");
1115        }
1116        //#endif
1117
if (inMemory) {
1118            getFile().updateObject(rowId, new Row(row, t));
1119        } else {
1120            byte[] ret = LazyRow.writeRow(session, t, row);
1121            getFile().updateBytes(rowId, ret);
1122        }
1123    }
1124
1125    public void getRow(long rowId, LazyRow row, boolean isTemp)
1126    throws IOException JavaDoc, SQLException JavaDoc
1127    {
1128        BlockFile f = isTemp ? getTempFile(false) : getFile();
1129        if (inMemory) {
1130            Row r = (Row)f.getObject(rowId);
1131            if (r == null) {
1132                throw new IOException JavaDoc("No row: " + rowId);
1133            }
1134            if (r.size() != row.size()) {
1135                //#ifdef DEBUG
1136
Debug.println((isTemp ? "temp" : "file") + ".getRow(" +
1137                          rowId + ") = " + row);
1138                Debug.println("Bad row size in getRow: " + r.size() +
1139                              " vs " + row.size());
1140                //#endif
1141
throw new IOException JavaDoc("Bad row size in getRow: " + r.size() +
1142                    " vs " + row.size());
1143            }
1144            for (int i = 1; i <= row.size(); i++) {
1145                row.set(i, r.getItem(i));
1146            }
1147        } else {
1148            byte[] rowBytes = f.getBytes(rowId);
1149            row.reset(rowBytes, this);
1150        }
1151        //#ifdef TRACE
1152
if (Trace.bit(11)) {
1153            Debug.println((isTemp ? "temp" : "file") + ".getRow(" +
1154                          rowId + ") = " + row);
1155        }
1156        //#endif
1157
}
1158
1159    public void removeRow(long rowId) throws IOException JavaDoc {
1160        removeRow(getFile(), rowId);
1161    }
1162
1163    public void removeRow(BlockFile file, long rowId) throws IOException JavaDoc {
1164        if (inMemory) {
1165            file.removeObject(rowId);
1166        } else {
1167            file.freeStream(rowId);
1168        }
1169    }
1170
1171    public boolean inMemory() {
1172        return inMemory;
1173    }
1174}
1175    
1176
Popular Tags