· 6 years ago · Nov 16, 2019, 11:50 PM
1package edu.berkeley.cs186.database;
2
3import java.io.File;
4import java.nio.file.Path;
5import java.nio.file.Paths;
6import java.util.*;
7import java.util.concurrent.ConcurrentHashMap;
8import java.util.concurrent.locks.Condition;
9import java.util.concurrent.locks.ReentrantLock;
10
11import edu.berkeley.cs186.database.common.BacktrackingIterator;
12import edu.berkeley.cs186.database.common.Pair;
13import edu.berkeley.cs186.database.concurrency.*;
14import edu.berkeley.cs186.database.databox.DataBox;
15import edu.berkeley.cs186.database.databox.Type;
16import edu.berkeley.cs186.database.index.BPlusTree;
17import edu.berkeley.cs186.database.index.BPlusTreeException;
18import edu.berkeley.cs186.database.io.Page;
19import edu.berkeley.cs186.database.query.QueryPlan;
20import edu.berkeley.cs186.database.query.QueryPlanException;
21import edu.berkeley.cs186.database.query.SortOperator;
22import edu.berkeley.cs186.database.table.Record;
23import edu.berkeley.cs186.database.table.RecordId;
24import edu.berkeley.cs186.database.table.RecordIterator;
25import edu.berkeley.cs186.database.table.Schema;
26import edu.berkeley.cs186.database.table.Table;
27import edu.berkeley.cs186.database.table.stats.TableStats;
28import edu.berkeley.cs186.database.io.PageAllocator.PageIterator;
29
30public class Database {
31 private Map<String, Table> tableLookup;
32 private Map<String, BPlusTree> indexLookup;
33 private Map<String, List<String>> tableIndices;
34 private Map<Long, Transaction> activeTransactions;
35 private long numTransactions;
36 private String fileDir;
37 private LockManager lockManager;
38 private int numMemoryPages;
39
40 /**
41 * Creates a new database with locking disabled.
42 *
43 * @param fileDir the directory to put the table files in
44 * @throws DatabaseException
45 */
46 public Database(String fileDir) throws DatabaseException {
47 this (fileDir, 5);
48 }
49
50 /**
51 * Creates a new database with locking disabled.
52 *
53 * @param fileDir the directory to put the table files in
54 * @param numMemoryPages the number of pages of memory Database Operations should use when executing Queries
55 * @throws DatabaseException
56 */
57 public Database(String fileDir, int numMemoryPages) throws DatabaseException {
58 this(fileDir, numMemoryPages, new DummyLockManager());
59 }
60
61 /**
62 * Creates a new database.
63 *
64 * @param fileDir the directory to put the table files in
65 * @param numMemoryPages the number of pages of memory Database Operations should use when executing Queries
66 * @param lockManager the lock manager
67 * @throws DatabaseException
68 */
69 public Database(String fileDir, int numMemoryPages, LockManager lockManager)
70 throws DatabaseException {
71 this.numMemoryPages = numMemoryPages;
72 this.fileDir = fileDir;
73 numTransactions = 0;
74 tableLookup = new ConcurrentHashMap<>();
75 indexLookup = new ConcurrentHashMap<>();
76 tableIndices = new ConcurrentHashMap<>();
77 activeTransactions = new ConcurrentHashMap<>();
78
79 File dir = new File(fileDir);
80 this.lockManager = lockManager;
81
82 if (!dir.exists()) {
83 dir.mkdirs();
84 }
85
86 File[] files = dir.listFiles();
87
88 try (Transaction initTransaction = new Transaction(-1)) {
89 LockContext lockContext = lockManager.databaseContext();
90 lockContext.acquire(initTransaction, LockType.X);
91 for (File f : files) {
92 String fName = f.getName();
93 if (fName.endsWith(Table.FILENAME_EXTENSION)) {
94 int lastIndex = fName.lastIndexOf(Table.FILENAME_EXTENSION);
95 String tableName = fName.substring(0, lastIndex);
96 tableLookup.put(tableName, newTable(tableName, f.toPath().toString(),
97 lockContext.childContext("table-" + tableName), initTransaction));
98 if (!tableIndices.containsKey(tableName)) {
99 tableIndices.put(tableName, new ArrayList<>());
100 }
101 } else if (fName.endsWith(BPlusTree.FILENAME_EXTENSION)) {
102 int lastIndex = fName.lastIndexOf(BPlusTree.FILENAME_EXTENSION);
103 String indexName = fName.substring(0, lastIndex);
104 String tableName = indexName.split(",", 2)[0];
105 indexLookup.put(indexName, new BPlusTree(f.toString(), getIndexContext(indexName),
106 initTransaction));
107 if (!tableIndices.containsKey(tableName)) {
108 tableIndices.put(tableName, new ArrayList<>());
109 }
110 tableIndices.get(tableName).add(indexName);
111 }
112 }
113 }
114 }
115
116 /**
117 * Close this database.
118 */
119 public synchronized void close() {
120 try (Transaction closeTransaction = new Transaction(-2)) {
121 lockManager.databaseContext().acquire(closeTransaction, LockType.X);
122
123 for (Table t : this.tableLookup.values()) {
124 t.close();
125 }
126
127 for (BPlusTree t : this.indexLookup.values()) {
128 t.close();
129 }
130
131 this.tableLookup.clear();
132 this.indexLookup.clear();
133 this.tableIndices.clear();
134 }
135 }
136
137 public Table getTable(String tableName) {
138 return tableLookup.get(tableName);
139 }
140
141 private LockContext getTableContext(String table) {
142 return lockManager.databaseContext().childContext("table-" + table);
143 }
144
145 private LockContext getIndexContext(String index) {
146 return lockManager.databaseContext().childContext("index-" + index);
147 }
148
149 /**
150 * Start a new transaction.
151 *
152 * @return the new Transaction
153 */
154 public synchronized Transaction beginTransaction() {
155 Transaction t = new Transaction(this.numTransactions);
156 this.activeTransactions.put(this.numTransactions, t);
157 this.numTransactions++;
158 return t;
159 }
160
161 /**
162 * This transaction implementation assumes that exactly one transaction runs
163 * on a thread at a time, and that, aside from the unblock() method, no methods
164 * of the transaction are called from a different thread than the thread that the
165 * transaction is associated with. This implementation blocks the thread when
166 * block() is called.
167 */
168 public class Transaction extends BaseTransaction {
169 long transNum;
170 boolean active;
171 boolean blocked;
172 HashMap<String, Table> tempTables;
173 HashMap<String, String> aliasMaps;
174 long tempTableCounter;
175
176 final ReentrantLock transactionLock = new ReentrantLock();
177 final Condition unblocked = transactionLock.newCondition();
178
179 protected Transaction(long tNum) {
180 this.transNum = tNum;
181 this.active = true;
182 this.blocked = false;
183 this.tempTables = new HashMap<String, Table>();
184 this.aliasMaps = new HashMap<String, String>();
185 this.tempTableCounter = 0;
186 }
187
188 public long getTransNum() {
189 return this.transNum;
190 }
191
192 public boolean isActive() {
193 return this.active;
194 }
195
196 public void end() {
197 assert(this.active);
198
199 List<Lock> locks = lockManager.getLocks(this);
200 if (locks == null) {
201 return;
202 }
203
204 while (!locks.isEmpty()) {
205 for (Lock lock1 : locks) {
206 boolean comp = true;
207 for (Lock lock2 : locks) {
208 if (!lock1.equals(lock2) && lock2.name.isDescendantOf(lock1.name)) {
209 comp = false;
210 }
211 }
212 if (comp) {
213 lockManager.release(this, lock1.name);
214 locks.remove(lock1);
215 break;
216 }
217 }
218 }
219 deleteAllTempTables();
220 this.active = false;
221 Database.this.activeTransactions.remove(this.transNum);
222
223 }
224
225
226 /**
227 * Create a new table in this database.
228 *
229 * @param s the table schema
230 * @param tableName the name of the table
231 * @throws DatabaseException
232 */
233 public void createTable(Schema s, String tableName) throws DatabaseException {
234 // TODO(hw5_part2): add DDL locking
235
236 LockContext tableContext = getTableContext(tableName);
237 LockUtil.ensureSufficientLockHeld(this, tableContext, LockType.X);
238
239 if (Database.this.tableLookup.containsKey(tableName)) {
240 throw new DatabaseException("Table name already exists");
241 }
242
243 Path path = Paths.get(fileDir, tableName + Table.FILENAME_EXTENSION);
244 Database.this.tableLookup.put(tableName, newTable(tableName, s, path.toString(), tableContext,
245 this));
246 Database.this.tableIndices.put(tableName, new ArrayList<>());
247 }
248
249 /**
250 * Create a new table in this database with an index on each of the given column names.
251 * @param s the table schema
252 * @param tableName the name of the table
253 * @param indexColumns the list of unique columnNames on the maintain an index on
254 * @throws DatabaseException
255 */
256 public void createTableWithIndices(Schema s, String tableName,
257 List<String> indexColumns) throws DatabaseException {
258 // TODO(hw5_part2): add locking
259
260 LockContext tableContext = getTableContext(tableName);
261 LockUtil.ensureSufficientLockHeld(this, tableContext, LockType.X);
262
263
264 List<String> schemaColNames = s.getFieldNames();
265 List<Type> schemaColType = s.getFieldTypes();
266
267 HashSet<String> seenColNames = new HashSet<String>();
268 List<Integer> schemaColIndex = new ArrayList<Integer>();
269 for (int i = 0; i < indexColumns.size(); i++) {
270 String col = indexColumns.get(i);
271 if (!schemaColNames.contains(col)) {
272 throw new DatabaseException("Column desired for index does not exist");
273 }
274 if (seenColNames.contains(col)) {
275 throw new DatabaseException("Column desired for index has been duplicated");
276 }
277 seenColNames.add(col);
278 schemaColIndex.add(schemaColNames.indexOf(col));
279 }
280
281 if (Database.this.tableLookup.containsKey(tableName)) {
282 throw new DatabaseException("Table name already exists");
283 }
284
285 Path path = Paths.get(fileDir, tableName + Table.FILENAME_EXTENSION);
286 Database.this.tableLookup.put(tableName, newTable(tableName, s, path.toString(), tableContext,
287 this));
288 Database.this.tableIndices.put(tableName, new ArrayList<>());
289 for (int i : schemaColIndex) {
290 String colName = schemaColNames.get(i);
291 Type colType = schemaColType.get(i);
292 String indexName = tableName + "," + colName;
293 Path p = Paths.get(Database.this.fileDir, indexName + BPlusTree.FILENAME_EXTENSION);
294 LockContext indexContext = getIndexContext(indexName);
295 try {
296 Database.this.indexLookup.put(indexName, new BPlusTree(p.toString(), colType,
297 BPlusTree.maxOrder(Page.pageSize, colType), indexContext, this));
298 Database.this.tableIndices.get(tableName).add(indexName);
299 } catch (BPlusTreeException e) {
300 throw new DatabaseException(e.getMessage());
301 }
302 }
303 }
304
305 /**
306 * Delete a table in this database.
307 *
308 * @param tableName the name of the table
309 * @return true if the database was successfully deleted
310 */
311 public boolean deleteTable(String tableName) {
312 // TODO(hw5_part2): add locking
313 LockContext tableContext = getTableContext(tableName);
314 LockUtil.ensureSufficientLockHeld(this, tableContext, LockType.X);
315
316 if (!Database.this.tableLookup.containsKey(tableName)) {
317 return false;
318 }
319
320 Database.this.tableLookup.get(tableName).close();
321 Database.this.tableLookup.remove(tableName);
322
323 File f = new File(fileDir + tableName + Table.FILENAME_EXTENSION);
324 f.delete();
325
326 Iterator<String> indices = Database.this.tableIndices.get(tableName).iterator();
327 while (indices.hasNext()) {
328 String indexName = indices.next();
329 indices.remove();
330 Database.this.indexLookup.get(indexName).close();
331 Database.this.indexLookup.remove(indexName);
332
333 File indexFile = new File(fileDir + indexName + BPlusTree.FILENAME_EXTENSION);
334 indexFile.delete();
335 }
336 Database.this.tableIndices.remove(tableName);
337
338 return true;
339 }
340
341 /**
342 * Delete all tables from this database.
343 */
344 public void deleteAllTables() {
345 // TODO(hw5_part2): add locking
346 LockUtil.ensureSufficientLockHeld(this, lockManager.databaseContext(), LockType.X);
347
348 List<String> tableNames = new ArrayList<>(tableLookup.keySet());
349
350 for (String s : tableNames) {
351 deleteTable(s);
352 }
353 }
354
355 public QueryPlan query(String tableName) throws DatabaseException {
356 assert(this.active);
357 return new QueryPlan(this, tableName);
358 }
359
360 public void queryAs(String tableName, String alias) throws DatabaseException {
361 assert(this.active);
362
363 if (Database.this.tableLookup.containsKey(alias)
364 || this.tempTables.containsKey(alias)
365 || this.aliasMaps.containsKey(alias)) {
366 throw new DatabaseException("Table name already exists");
367 }
368
369 if (Database.this.tableLookup.containsKey(tableName)) {
370 this.aliasMaps.put(alias, tableName);
371 } else if (tempTables.containsKey(tableName)) {
372 this.aliasMaps.put(alias, tableName);
373 } else {
374 throw new DatabaseException("Table name not found");
375 }
376 }
377
378 public String createTempTable(Schema schema) throws DatabaseException {
379 assert(this.active);
380 String tempTableName = "tempTable" + tempTableCounter;
381 tempTableCounter++;
382 createTempTable(schema, tempTableName);
383 return tempTableName;
384 }
385
386 public void createTempTable(Schema schema, String tempTableName) throws DatabaseException {
387 assert(this.active);
388
389 if (Database.this.tableLookup.containsKey(tempTableName)
390 || this.tempTables.containsKey(tempTableName)) {
391 throw new DatabaseException("Table name already exists");
392 }
393
394 Path dir = Paths.get(Database.this.fileDir, "temp");
395 File f = new File(dir.toAbsolutePath().toString());
396 if (!f.exists()) {
397 f.mkdirs();
398 }
399
400 Path path = Paths.get(Database.this.fileDir, "temp", tempTableName + Table.FILENAME_EXTENSION);
401 LockContext lockContext = lockManager.orphanContext("temp-" + tempTableName);
402 // TODO(hw5_part2): more efficient locking on temporary tables
403 lockContext.disableChildLocks();
404 lockContext.acquire(this, LockType.X);
405 this.tempTables.put(tempTableName, newTable(tempTableName, schema, path.toString(), lockContext,
406 this));
407 }
408
409 public boolean indexExists(String tableName, String columnName) {
410 try {
411 resolveIndexFromName(tableName, columnName);
412 } catch (DatabaseException e) {
413 return false;
414 }
415 return true;
416 }
417
418 public Iterator<Record> sortedScan(String tableName, String columnName) throws DatabaseException {
419 Table tab = getTable(tableName);
420
421 // TODO(hw5_part2): scan locking
422 LockUtil.ensureSufficientLockHeld(this, getTableContext(tableName), LockType.S);
423 try {
424 Pair<String, BPlusTree> index = resolveIndexFromName(tableName, columnName);
425 return new RecordIterator(this, tab, index.getSecond().scanAll(this));
426 } catch (DatabaseException e1) {
427 int offset = getTable(tableName).getSchema().getFieldNames().indexOf(columnName);
428 try {
429 return new SortOperator(this, tableName,
430 Comparator.comparing((Record r) -> r.getValues().get(offset))).iterator();
431 } catch (QueryPlanException e2) {
432 throw new DatabaseException(e2);
433 }
434 }
435 }
436
437 public Iterator<Record> sortedScanFrom(String tableName, String columnName,
438 DataBox startValue) throws DatabaseException {
439 Table tab = getTable(tableName);
440 Pair<String, BPlusTree> index = resolveIndexFromName(tableName, columnName);
441 // TODO(hw5_part2): scan locking
442 LockUtil.ensureSufficientLockHeld(this, getTableContext(tableName), LockType.S);
443 return new RecordIterator(this, tab, index.getSecond().scanGreaterEqual(this, startValue));
444 }
445
446 public Iterator<Record> lookupKey(String tableName, String columnName,
447 DataBox key) throws DatabaseException {
448 Table tab = getTable(tableName);
449 Pair<String, BPlusTree> index = resolveIndexFromName(tableName, columnName);
450 return new RecordIterator(this, tab, index.getSecond().scanEqual(this, key));
451 }
452
453 public boolean contains(String tableName, String columnName, DataBox key) throws DatabaseException {
454 Pair<String, BPlusTree> index = resolveIndexFromName(tableName, columnName);
455 return index.getSecond().get(this, key).isPresent();
456 }
457
458 public RecordId addRecord(String tableName, List<DataBox> values) throws DatabaseException {
459 assert(this.active);
460
461 Table tab = getTable(tableName);
462 RecordId rid = tab.addRecord(this, values);
463 Schema s = tab.getSchema();
464 List<String> colNames = s.getFieldNames();
465
466 for (int i = 0; i < colNames.size(); i++) {
467 String col = colNames.get(i);
468 if (indexExists(tableName, col)) {
469 try {
470 resolveIndexFromName(tableName, col).getSecond().put(this, values.get(i), rid);
471 } catch (BPlusTreeException e) {
472 throw new DatabaseException(e.getMessage());
473 }
474 }
475 }
476 return rid;
477 }
478
479 public int getNumMemoryPages() throws DatabaseException {
480 assert(this.active);
481 return Database.this.numMemoryPages;
482 }
483
484 public RecordId deleteRecord(String tableName, RecordId rid) throws DatabaseException {
485 assert(this.active);
486
487 Table tab = getTable(tableName);
488 Schema s = tab.getSchema();
489
490 Record rec = tab.deleteRecord(this, rid);
491 List<DataBox> values = rec.getValues();
492 List<String> colNames = s.getFieldNames();
493 for (int i = 0; i < colNames.size(); i++) {
494 String col = colNames.get(i);
495 if (indexExists(tableName, col)) {
496 resolveIndexFromName(tableName, col).getSecond().remove(this, values.get(i));
497 }
498 }
499
500 return rid;
501 }
502
503 public Record getRecord(String tableName, RecordId rid) throws DatabaseException {
504 assert(this.active);
505 return getTable(tableName).getRecord(this, rid);
506 }
507
508 public RecordIterator getRecordIterator(String tableName) throws DatabaseException {
509 assert(this.active);
510 return getTable(tableName).iterator(this);
511 }
512
513 public RecordId updateRecord(String tableName, List<DataBox> values,
514 RecordId rid) throws DatabaseException {
515 return runUpdateRecord(tableName, values, rid);
516 }
517
518 public PageIterator getPageIterator(String tableName) throws DatabaseException {
519 assert(this.active);
520 return getTable(tableName).getAllocator().iterator(this);
521 }
522
523 public BacktrackingIterator<Record> getBlockIterator(String tableName,
524 Page[] block) throws DatabaseException {
525 assert(this.active);
526 return getTable(tableName).blockIterator(this, block);
527 }
528
529 public BacktrackingIterator<Record> getBlockIterator(String tableName,
530 BacktrackingIterator<Page> block) throws DatabaseException {
531 assert(this.active);
532 return getTable(tableName).blockIterator(this, block);
533 }
534
535 public BacktrackingIterator<Record> getBlockIterator(String tableName, Iterator<Page> block,
536 int maxPages) throws DatabaseException {
537 assert(this.active);
538 return getTable(tableName).blockIterator(this, block, maxPages);
539 }
540
541 public RecordId runUpdateRecordWhere(String tableName, String targetColumnName, DataBox targetVaue,
542 String predColumnName, DataBox predValue) throws DatabaseException {
543 Table tab = getTable(tableName);
544 Iterator<RecordId> recordIds = tab.ridIterator(this);
545
546 Schema s = tab.getSchema();
547 int uindex = s.getFieldNames().indexOf(targetColumnName);
548 int pindex = s.getFieldNames().indexOf(predColumnName);
549
550 while(recordIds.hasNext()) {
551 RecordId curRID = recordIds.next();
552 Record cur = getRecord(tableName, curRID);
553 List<DataBox> record_copy = new ArrayList<DataBox>(cur.getValues());
554
555 if (record_copy.get(pindex).equals(predValue)) {
556 record_copy.set(uindex, targetVaue);
557 runUpdateRecord(tableName, record_copy, curRID);
558 }
559 }
560 return null;
561 }
562
563 private RecordId runUpdateRecord(String tableName, List<DataBox> values,
564 RecordId rid) throws DatabaseException {
565 assert(this.active);
566 Table tab = getTable(tableName);
567 Schema s = tab.getSchema();
568
569 Record rec = tab.updateRecord(this, values, rid);
570
571 List<DataBox> oldValues = rec.getValues();
572 List<String> colNames = s.getFieldNames();
573
574 for (int i = 0; i < colNames.size(); i++) {
575 String col = colNames.get(i);
576 if (indexExists(tableName, col)) {
577 BPlusTree tree = resolveIndexFromName(tableName, col).getSecond();
578 tree.remove(this, oldValues.get(i));
579 try {
580 tree.put(this, values.get(i), rid);
581 } catch (BPlusTreeException e) {
582 throw new DatabaseException(e.getMessage());
583 }
584 }
585 }
586
587 return rid;
588 }
589
590 public TableStats getStats(String tableName) throws DatabaseException {
591 assert(this.active);
592 return getTable(tableName).getStats();
593 }
594
595 public int getNumDataPages(String tableName) throws DatabaseException {
596 assert(this.active);
597 return getTable(tableName).getNumDataPages();
598 }
599
600 public int getNumEntriesPerPage(String tableName) throws DatabaseException {
601 assert(this.active);
602 return getTable(tableName).getNumRecordsPerPage();
603 }
604
605 public byte[] readPageHeader(String tableName, Page p) throws DatabaseException {
606 assert(this.active);
607 return getTable(tableName).getBitMap(this, p);
608 }
609
610 public int getPageHeaderSize(String tableName) throws DatabaseException {
611 assert(this.active);
612 return getTable(tableName).getBitmapSizeInBytes();
613 }
614
615 public int getEntrySize(String tableName) throws DatabaseException {
616 assert(this.active);
617 return getTable(tableName).getSchema().getSizeInBytes();
618 }
619
620 public long getNumRecords(String tableName) throws DatabaseException {
621 assert(this.active);
622 return getTable(tableName).getNumRecords();
623 }
624
625 public int getNumIndexPages(String tableName, String columnName) throws DatabaseException {
626 assert(this.active);
627 return this.resolveIndexFromName(tableName, columnName).getSecond().getNumPages();
628 }
629
630 public Schema getSchema(String tableName) throws DatabaseException {
631 assert(this.active);
632 return getTable(tableName).getSchema();
633 }
634
635 public Schema getFullyQualifiedSchema(String tableName) throws DatabaseException {
636 assert(this.active);
637
638 Schema schema = getTable(tableName).getSchema();
639 List<String> newColumnNames = new ArrayList<String>();
640 for (String oldName : schema.getFieldNames()) {
641 newColumnNames.add(tableName + "." + oldName);
642 }
643
644 return new Schema(newColumnNames, schema.getFieldTypes());
645 }
646
647 private Pair<String, BPlusTree> resolveIndexFromName(String tableName,
648 String columnName) throws DatabaseException {
649 while (aliasMaps.containsKey(tableName)) {
650 tableName = aliasMaps.get(tableName);
651 }
652 if (columnName.contains(".")) {
653 String columnPrefix = columnName.split("\\.")[0];
654 while (aliasMaps.containsKey(columnPrefix)) {
655 columnPrefix = aliasMaps.get(columnPrefix);
656 }
657 if (!tableName.equals(columnPrefix)) {
658 throw new DatabaseException("Column: " + columnName + " is not a column of " + tableName);
659 }
660 columnName = columnName.split("\\.")[1];
661 }
662 String indexName = tableName + "," + columnName;
663 if (Database.this.indexLookup.containsKey(indexName)) {
664 return new Pair<>(indexName, Database.this.indexLookup.get(indexName));
665 }
666 throw new DatabaseException("Index does not exist");
667 }
668
669 private Table getTable(String tableName) throws DatabaseException {
670 if (this.tempTables.containsKey(tableName)) {
671 return this.tempTables.get(tableName);
672 }
673
674 while (aliasMaps.containsKey(tableName)) {
675 tableName = aliasMaps.get(tableName);
676 }
677
678 if (!Database.this.tableLookup.containsKey(tableName)) {
679 throw new DatabaseException("Table: " + tableName + "does not exist");
680 }
681
682 return Database.this.tableLookup.get(tableName);
683 }
684
685 public void deleteTempTable(String tempTableName) {
686 assert(this.active);
687
688 if (!this.tempTables.containsKey(tempTableName)) {
689 return;
690 }
691
692 this.tempTables.get(tempTableName).close();
693 Database.this.tableLookup.remove(tempTableName);
694
695 File f = new File(Database.this.fileDir + "temp/" + tempTableName + Table.FILENAME_EXTENSION);
696 f.delete();
697 }
698
699 private void deleteAllTempTables() {
700 Set<String> keys = tempTables.keySet();
701
702 for (String tableName : keys) {
703 deleteTempTable(tableName);
704 }
705 }
706
707 public void block() {
708 this.transactionLock.lock();
709 try {
710 this.blocked = true;
711 while (this.blocked) {
712 this.unblocked.awaitUninterruptibly();
713 }
714 } finally {
715 this.transactionLock.unlock();
716 }
717 }
718
719 public void unblock() {
720 this.transactionLock.lock();
721 try {
722 this.blocked = false;
723 this.unblocked.signal();
724 } finally {
725 this.transactionLock.unlock();
726 }
727 }
728
729 public boolean getBlocked() {
730 return this.blocked;
731 }
732 }
733
734 /* ******************************************************************************** */
735 /* Every that follows is solely for the purpose of testing certain homeworks without
736 requiring that previous homeworks be properly implemented. You should not change anything
737 below. */
738 /* ******************************************************************************** */
739 protected Table newTable(String name, Schema schema, String filename, LockContext lockContext,
740 BaseTransaction transaction) {
741 return new Table(name, schema, filename, lockContext, transaction);
742 }
743
744 protected Table newTable(String name, String filename, LockContext lockContext,
745 BaseTransaction transaction) throws DatabaseException {
746 return new Table(name, filename, lockContext, transaction);
747 }
748}