1 24 25 package com.mckoi.database; 26 27 import java.util.ArrayList ; 28 import java.io.*; 29 import com.mckoi.util.IntegerListInterface; 30 import com.mckoi.util.IntegerIterator; 31 import com.mckoi.util.IntegerVector; 32 import com.mckoi.util.ByteArrayUtil; 33 import com.mckoi.util.UserTerminal; 34 import com.mckoi.util.Cache; 35 import com.mckoi.debug.*; 36 37 63 64 abstract class MasterTableDataSource { 65 66 67 69 73 private TransactionSystem system; 74 75 79 private StoreSystem store_system; 80 81 83 87 protected int table_id; 88 89 92 protected boolean is_closed; 93 94 96 104 private int root_lock; 105 106 108 112 protected DataTableDef table_def; 113 114 117 protected DataIndexSetDef index_def; 118 119 122 private TableName cached_table_name; 123 124 129 protected MultiVersionTableIndices table_indices; 130 131 135 protected RIDList[] column_rid_list; 136 137 139 142 protected boolean DATA_CELL_CACHING = true; 143 144 147 protected final DataCellCache cache; 148 149 152 protected int column_count; 153 154 155 156 158 163 private OpenTransactionList open_transactions; 164 165 167 171 protected MasterTableGarbageCollector garbage_collector; 172 173 175 179 protected BlobStoreInterface blob_store_interface; 180 181 183 186 protected String root_lock_key; 187 protected String total_hits_key; 188 protected String file_hits_key; 189 protected String delete_hits_key; 190 protected String insert_hits_key; 191 192 198 MasterTableDataSource(TransactionSystem system, 199 StoreSystem store_system, 200 OpenTransactionList open_transactions, 201 BlobStoreInterface blob_store_interface) { 202 203 this.system = system; 204 this.store_system = store_system; 205 this.open_transactions = open_transactions; 206 this.blob_store_interface = blob_store_interface; 207 this.garbage_collector = new MasterTableGarbageCollector(this); 208 this.cache = system.getDataCellCache(); 209 is_closed = true; 210 211 if (DATA_CELL_CACHING) { 212 DATA_CELL_CACHING = (cache != null); 213 } 214 215 } 216 217 220 public final TransactionSystem getSystem() { 221 return system; 222 } 223 224 227 public final DebugLogger Debug() { 228 return getSystem().Debug(); 229 } 230 231 234 public TableName getTableName() { 235 return getDataTableDef().getTableName(); 236 } 237 238 241 public String getName() { 242 return getDataTableDef().getName(); 243 } 244 245 248 public String getSchema() { 249 return getDataTableDef().getSchema(); 250 } 251 252 255 synchronized TableName cachedTableName() { 256 if (cached_table_name != null) { 257 return cached_table_name; 258 } 259 cached_table_name = getTableName(); 260 return cached_table_name; 261 } 262 263 277 synchronized void mergeJournalChanges(long commit_id) { 278 279 boolean all_merged = table_indices.mergeJournalChanges(commit_id); 280 if (all_merged && !isReadOnly()) { 282 checkForCleanup(); 283 } 284 285 } 286 287 295 synchronized MasterTableJournal[] findAllJournalsSince(long commit_id) { 296 return table_indices.findAllJournalsSince(commit_id); 297 } 298 299 301 304 int getTableID() { 305 return table_id; 306 } 307 308 313 DataTableDef getDataTableDef() { 314 return table_def; 315 } 316 317 321 DataIndexSetDef getDataIndexSetDef() { 322 return index_def; 323 } 324 325 327 335 protected static String makeTableFileName(TransactionSystem system, 336 int table_id, TableName table_name) { 337 338 String tid = Integer.toString(table_id); 342 int pad = 3 - tid.length(); 343 StringBuffer buf = new StringBuffer (); 344 for (int i = 0; i < pad; ++i) { 345 buf.append('0'); 346 } 347 348 String str = table_name.toString().replace('.', '_'); 349 350 StringBuffer osified_name = new StringBuffer (); 354 int count = 0; 355 for (int i = 0; i < str.length() || count > 64; ++i) { 356 char c = str.charAt(i); 357 if ((c >= 'a' && c <= 'z') || 358 (c >= 'A' && c <= 'Z') || 359 (c >= '0' && c <= '9') || 360 c == '_') { 361 osified_name.append(c); 362 ++count; 363 } 364 } 365 366 return new String (buf) + tid + new String (osified_name); 367 } 368 369 370 372 377 abstract String getSourceIdent(); 378 379 384 abstract int writeRecordType(int row_index, int row_state) 385 throws IOException; 386 387 390 abstract int readRecordType(int row_index) throws IOException; 391 392 396 abstract boolean recordDeleted(int row_index) throws IOException; 397 398 403 abstract int rawRowCount() throws IOException; 404 405 409 abstract void internalDeleteRow(int row_index) throws IOException; 410 411 418 abstract IndexSet createIndexSet(); 419 420 425 abstract void commitIndexSet(IndexSet index_set); 426 427 436 abstract int internalAddRow(RowData data) throws IOException; 437 438 444 abstract TObject internalGetCellContents(int column, int row); 445 446 449 abstract long currentUniqueID(); 450 451 454 abstract long nextUniqueID(); 455 456 461 abstract void setUniqueID(long value); 462 463 469 abstract void dispose(boolean pending_drop) throws IOException; 470 471 476 abstract boolean drop() throws IOException; 477 478 484 abstract void shutdownHookCleanup(); 485 486 487 488 493 boolean isWorthCompacting() { 494 return true; 495 } 496 497 504 synchronized SelectableScheme createSelectableSchemeForColumn( 505 IndexSet index_set, TableDataSource table, int column) { 506 DataTableColumnDef column_def = getDataTableDef().columnAt(column); 508 509 if (!column_def.isIndexableType()) { 511 return new BlindSearch(table, column); 512 } 513 514 String scheme_type = column_def.getIndexScheme(); 515 if (scheme_type.equals("InsertSearch")) { 516 DataIndexSetDef index_set_def = getDataIndexSetDef(); 518 int index_i = index_set_def.findIndexForColumns( 519 new String [] { column_def.getName() }); 520 return createSelectableSchemeForIndex(index_set, table, index_i); 521 } 522 else if (scheme_type.equals("BlindSearch")) { 523 return new BlindSearch(table, column); 524 } 525 else { 526 throw new Error ("Unknown scheme type"); 527 } 528 } 529 530 536 synchronized SelectableScheme createSelectableSchemeForIndex( 537 IndexSet index_set, TableDataSource table, int index_i) { 538 539 DataIndexDef index_def = getDataIndexSetDef().indexAt(index_i); 541 542 if (index_def.getType().equals("BLIST")) { 543 String [] cols = index_def.getColumnNames(); 544 DataTableDef table_def = getDataTableDef(); 545 if (cols.length == 1) { 546 int col_index = table_def.findColumnName(cols[0]); 548 IntegerListInterface index_list = 551 index_set.getIndex(index_def.getPointer()); 552 InsertSearch iis = new InsertSearch(table, col_index, index_list); 553 return iis; 554 } 555 else { 556 throw new RuntimeException ( 557 "Multi-column indexes not supported at this time."); 558 } 559 } 560 else { 561 throw new RuntimeException ("Unrecognised type."); 562 } 563 564 } 565 566 571 protected TableDataSource minimalTableDataSource( 572 final IntegerListInterface master_index) { 573 return new TableDataSource() { 576 public TransactionSystem getSystem() { 577 return system; 578 } 579 public DataTableDef getDataTableDef() { 580 return MasterTableDataSource.this.getDataTableDef(); 581 } 582 public int getRowCount() { 583 return master_index.size(); 586 } 587 public RowEnumeration rowEnumeration() { 588 final IntegerIterator iterator = master_index.iterator(); 592 return new RowEnumeration() { 594 public boolean hasMoreRows() { 595 return iterator.hasNext(); 596 } 597 public int nextRowIndex() { 598 return iterator.next(); 599 } 600 }; 601 } 602 public SelectableScheme getColumnScheme(int column) { 603 throw new Error ("Not implemented."); 604 } 605 public TObject getCellContents(int column, int row) { 606 return MasterTableDataSource.this.getCellContents(column, row); 607 } 608 }; 609 } 610 611 620 synchronized void buildIndexes() throws IOException { 621 IndexSet index_set = createIndexSet(); 622 623 DataIndexSetDef index_set_def = getDataIndexSetDef(); 624 625 final int row_count = rawRowCount(); 626 627 IntegerListInterface master_index = index_set.getIndex(0); 629 630 for (int row_index = 0; row_index < row_count; ++row_index) { 632 if (!recordDeleted(row_index)) { 634 boolean inserted = master_index.uniqueInsertSort(row_index); 636 if (!inserted) { 637 throw new RuntimeException ( 638 "Assertion failed: Master index entry was duplicated."); 639 } 640 } 641 } 642 643 commitIndexSet(index_set); 645 646 int index_count = index_set_def.indexCount(); 648 for (int i = 0; i < index_count; ++i) { 649 buildIndex(i); 650 } 651 652 } 653 654 663 synchronized void buildIndex(final int index_number) throws IOException { 664 DataIndexSetDef index_set_def = getDataIndexSetDef(); 665 666 IndexSet index_set = createIndexSet(); 667 668 IntegerListInterface master_index = index_set.getIndex(0); 670 TableDataSource min_table_source = minimalTableDataSource(master_index); 672 673 SelectableScheme scheme = createSelectableSchemeForIndex(index_set, 675 min_table_source, index_number); 676 677 int row_count = rawRowCount(); 679 for (int row_index = 0; row_index < row_count; ++row_index) { 680 681 if (!recordDeleted(row_index)) { 683 scheme.insert(row_index); 684 } 685 686 } 687 688 commitIndexSet(index_set); 690 691 } 692 693 694 695 696 704 synchronized void commitTransactionChange(long commit_id, 705 MasterTableJournal change, IndexSet index_set) { 706 if (isReadOnly()) { 708 throw new Error ("Can't commit transaction journal, table is read only."); 709 } 710 711 change.setCommitID(commit_id); 712 713 try { 714 715 table_indices.addTransactionJournal(change); 717 718 commitIndexSet(index_set); 721 722 739 int size = change.entries(); 740 for (int i = 0; i < size; ++i) { 741 byte b = change.getCommand(i); 742 int row_index = change.getRowIndex(i); 743 if (MasterTableJournal.isAddCommand(b)) { 745 746 int old_type = writeRecordType(row_index, 0x010); 748 if ((old_type & 0x0F0) != 0) { 751 writeRecordType(row_index, old_type & 0x0F0); 752 throw new Error ("Record " + row_index + " of table " + this + 753 " was not in an uncommitted state!"); 754 } 755 756 } 757 else if (MasterTableJournal.isRemoveCommand(b)) { 758 759 int old_type = writeRecordType(row_index, 0x020); 761 if ((old_type & 0x0F0) != 0x010) { 763 writeRecordType(row_index, old_type & 0x0F0); 764 throw new Error ("Record " + row_index + " of table " + this + 766 " was not in an added state!"); 767 } 768 garbage_collector.markRowAsDeleted(row_index); 770 771 } 772 } 773 774 } 775 catch (IOException e) { 776 Debug().writeException(e); 777 throw new Error ("IO Error: " + e.getMessage()); 778 } 779 780 } 781 782 787 synchronized void rollbackTransactionChange(MasterTableJournal change) { 788 789 if (isReadOnly()) { 791 throw new Error ( 792 "Can't rollback transaction journal, table is read only."); 793 } 794 795 798 try { 799 int size = change.entries(); 801 for (int i = 0; i < size; ++i) { 802 byte b = change.getCommand(i); 803 int row_index = change.getRowIndex(i); 804 if (MasterTableJournal.isAddCommand(b)) { 806 int old_type = writeRecordType(row_index, 0x020); 809 if ((old_type & 0x0F0) != 0) { 812 writeRecordType(row_index, old_type & 0x0F0); 814 throw new Error ("Record " + row_index + " was not in an " + 815 "uncommitted state!"); 816 } 817 garbage_collector.markRowAsDeleted(row_index); 819 } 820 else if (MasterTableJournal.isRemoveCommand(b)) { 821 } 824 } 825 826 } 829 catch (IOException e) { 830 Debug().writeException(e); 831 throw new Error ("IO Error: " + e.getMessage()); 832 } 833 } 834 835 844 MutableTableDataSource createTableDataSourceAtCommit( 845 SimpleTransaction transaction) { 846 return createTableDataSourceAtCommit(transaction, 847 new MasterTableJournal(getTableID())); 848 } 849 850 858 MutableTableDataSource createTableDataSourceAtCommit( 859 SimpleTransaction transaction, MasterTableJournal journal) { 860 return new MMutableTableDataSource(transaction, journal); 861 } 862 863 865 870 protected synchronized void setupDataIndexSetDef() { 871 index_def = new DataIndexSetDef(table_def.getTableName()); 873 for (int i = 0; i < table_def.columnCount(); ++i) { 874 DataTableColumnDef col_def = table_def.columnAt(i); 875 if (col_def.isIndexableType() && 876 col_def.getIndexScheme().equals("InsertSearch")) { 877 index_def.addDataIndexDef(new DataIndexDef("ANON-COLUMN:" + i, 878 new String [] { col_def.getName() }, i + 1, 879 "BLIST", false)); 880 } 881 } 882 } 883 884 888 protected synchronized void setupDataTableDef(DataTableDef table_def) { 889 890 if ((table_id & 0x0F0000000) != 0) { 892 throw new Error ("'table_id' exceeds maximum possible keys."); 893 } 894 895 this.table_def = table_def; 896 897 TableName table_name = table_def.getTableName(); 899 900 table_indices = new MultiVersionTableIndices(getSystem(), 902 table_name, table_def.columnCount()); 903 column_rid_list = new RIDList[table_def.columnCount()]; 905 906 setupDataIndexSetDef(); 908 } 909 910 913 protected synchronized void loadInternal() { 914 String table_name = table_def.getName(); 916 String schema_name = table_def.getSchema(); 917 String n = table_name; 918 if (schema_name.length() > 0) { 919 n = schema_name + "." + table_name; 920 } 921 root_lock_key = "MasterTableDataSource.RootLocks." + n; 922 total_hits_key = "MasterTableDataSource.Hits.Total." + n; 923 file_hits_key = "MasterTableDataSource.Hits.File." + n; 924 delete_hits_key = "MasterTableDataSource.Hits.Delete." + n; 925 insert_hits_key = "MasterTableDataSource.Hits.Insert." + n; 926 927 column_count = table_def.columnCount(); 928 929 is_closed = false; 930 931 } 932 933 936 synchronized boolean isClosed() { 937 return is_closed; 938 } 939 940 943 boolean isReadOnly() { 944 return system.readOnlyAccess(); 945 } 946 947 951 protected StoreSystem storeSystem() { 952 return store_system; 953 } 954 955 964 int addRow(RowData data) throws IOException { 965 int row_number; 966 967 synchronized(this) { 968 969 row_number = internalAddRow(data); 970 971 } 973 getSystem().stats().increment(insert_hits_key); 975 976 return row_number; 978 } 979 980 988 private synchronized void doHardRowRemove(int row_index) throws IOException { 989 990 for (int i = 0; i < column_count; ++i) { 993 RIDList rid_list = column_rid_list[i]; 994 if (rid_list != null) { 995 rid_list.removeRID(row_index); 996 } 997 } 998 999 internalDeleteRow(row_index); 1001 1002 system.stats().increment(delete_hits_key); 1004 1005 } 1006 1007 1019 synchronized void hardRemoveRow(final int record_index) throws IOException { 1020 if (!isRootLocked()) { 1022 int type_key = readRecordType(record_index); 1024 if ((type_key & 0x0F0) == 0x020) { 1026 doHardRowRemove(record_index); 1027 } 1028 else { 1029 throw new Error ( 1030 "Row isn't marked as committed removed: " + record_index); 1031 } 1032 } 1033 else { 1034 throw new Error ("Assertion failed: " + 1035 "Can't remove row, table is under a root lock."); 1036 } 1037 } 1038 1039 1044 synchronized boolean hardCheckAndReclaimRow(final int record_index) 1045 throws IOException { 1046 if (!isRootLocked()) { 1048 if (!recordDeleted(record_index)) { 1050 int type_key = readRecordType(record_index); 1051 if ((type_key & 0x0F0) == 0x020) { 1053 doHardRowRemove(record_index); 1056 return true; 1057 } 1058 } 1059 return false; 1060 } 1061 else { 1062 throw new Error ("Assertion failed: " + 1063 "Can't remove row, table is under a root lock."); 1064 } 1065 } 1066 1067 1071 synchronized int recordTypeInfo(int record_index) throws IOException { 1072 if (recordDeleted(record_index)) { 1074 return RawDiagnosticTable.DELETED; 1075 } 1076 int type_key = readRecordType(record_index) & 0x0F0; 1077 if (type_key == 0) { 1078 return RawDiagnosticTable.UNCOMMITTED; 1079 } 1080 else if (type_key == 0x010) { 1081 return RawDiagnosticTable.COMMITTED_ADDED; 1082 } 1083 else if (type_key == 0x020) { 1084 return RawDiagnosticTable.COMMITTED_REMOVED; 1085 } 1086 return RawDiagnosticTable.RECORD_STATE_ERROR; 1087 1088 } 1089 1090 1095 protected synchronized void doOpeningScan() throws IOException { 1096 long in_time = System.currentTimeMillis(); 1097 1098 if (isRootLocked() || hasTransactionChangesPending()) { 1101 throw new RuntimeException ( 1103 "Odd, we are root locked or have pending journal changes."); 1104 } 1105 1106 if (!isReadOnly()) { 1108 MasterTableJournal journal = new MasterTableJournal(); 1110 1111 IndexSet index_set = createIndexSet(); 1113 IntegerListInterface master_index = index_set.getIndex(0); 1114 1115 1118 int row_count = rawRowCount(); 1119 for (int i = 0 ; i < row_count; ++i) { 1120 if (!recordDeleted(i)) { 1122 int type = recordTypeInfo(i); 1124 if (type == RawDiagnosticTable.COMMITTED_REMOVED || 1127 type == RawDiagnosticTable.UNCOMMITTED) { 1128 if (!master_index.contains(i)) { 1130 doHardRowRemove(i); 1132 } 1133 else { 1134 Debug().write(Lvl.ERROR, this, 1135 "Inconsistant: Row is indexed but marked as " + 1136 "removed or uncommitted."); 1137 Debug().write(Lvl.ERROR, this, 1138 "Row: " + i + " Type: " + type + 1139 " Table: " + getTableName()); 1140 writeRecordType(i, 0x010); 1142 1143 } 1144 } 1145 else { 1146 if (!master_index.contains(i)) { 1148 Debug().write(Lvl.ERROR, this, 1150 "Inconsistant: Row committed added but not in master index."); 1151 Debug().write(Lvl.ERROR, this, 1152 "Row: " + i + " Type: " + type + 1153 " Table: " + getTableName()); 1154 writeRecordType(i, 0x020); 1157 1158 } 1159 } 1160 } 1161 else { if (master_index.contains(i)) { 1164 Debug().write(Lvl.ERROR, this, 1167 "Inconsistant: Row is removed but in index."); 1168 Debug().write(Lvl.ERROR, this, 1169 "Row: " + i + " Table: " + getTableName()); 1170 writeRecordType(i, 0x010); 1172 1173 } 1174 } 1175 } 1177 index_set.dispose(); 1179 1180 } 1181 1182 long bench_time = System.currentTimeMillis() - in_time; 1183 if (Debug().isInterestedIn(Lvl.INFORMATION)) { 1184 Debug().write(Lvl.INFORMATION, this, 1185 "Opening scan for " + toString() + " (" + getTableName() + ") took " + 1186 bench_time + "ms."); 1187 } 1188 1189 } 1190 1191 1192 1196 RawDiagnosticTable getRawDiagnosticTable() { 1197 return new MRawDiagnosticTable(); 1198 } 1199 1200 1201 1207 TObject getCellContents(int column, int row) { 1208 if (row < 0) { 1209 throw new Error ("'row' is < 0"); 1210 } 1211 return internalGetCellContents(column, row); 1212 } 1213 1214 1222 synchronized void addRootLock() { 1223 system.stats().increment(root_lock_key); 1224 ++root_lock; 1225 } 1226 1227 1235 synchronized void removeRootLock() { 1236 if (!is_closed) { 1237 system.stats().decrement(root_lock_key); 1238 if (root_lock == 0) { 1239 throw new Error ("Too many root locks removed!"); 1240 } 1241 --root_lock; 1242 if (root_lock == 0) { 1244 checkForCleanup(); 1245 } 1246 } 1247 } 1248 1249 1253 synchronized boolean isRootLocked() { 1254 return root_lock > 0; 1255 } 1256 1257 1261 protected synchronized void clearAllRootLocks() { 1262 root_lock = 0; 1263 } 1264 1265 1269 abstract void checkForCleanup(); 1270 1271 1272 synchronized String transactionChangeString() { 1273 return table_indices.transactionChangeString(); 1274 } 1275 1276 1280 synchronized boolean hasTransactionChangesPending() { 1281 return table_indices.hasTransactionChangesPending(); 1282 } 1283 1284 1285 1286 1287 1288 1290 1295 private final class MRawDiagnosticTable implements RawDiagnosticTable { 1296 1297 1299 public int physicalRecordCount() { 1300 try { 1301 return rawRowCount(); 1302 } 1303 catch (IOException e) { 1304 throw new Error (e.getMessage()); 1305 } 1306 } 1307 1308 public DataTableDef getDataTableDef() { 1309 return MasterTableDataSource.this.getDataTableDef(); 1310 } 1311 1312 public int recordState(int record_index) { 1313 try { 1314 return recordTypeInfo(record_index); 1315 } 1316 catch (IOException e) { 1317 throw new Error (e.getMessage()); 1318 } 1319 } 1320 1321 public int recordSize(int record_index) { 1322 return -1; 1323 } 1324 1325 public TObject getCellContents(int column, int record_index) { 1326 return MasterTableDataSource.this.getCellContents(column, 1327 record_index); 1328 } 1329 1330 public String recordMiscInformation(int record_index) { 1331 return null; 1332 } 1333 1334 } 1335 1336 1345 private final class MMutableTableDataSource 1346 implements MutableTableDataSource { 1347 1348 1353 private SimpleTransaction transaction; 1354 1355 1358 private boolean tran_read_only; 1359 1360 1363 private TableName table_name; 1364 1365 1369 private int row_list_rebuild; 1370 1371 1375 private IntegerListInterface row_list; 1376 1377 1381 private int[] scheme_rebuilds; 1382 1383 1386 private IndexSet index_set; 1387 1388 1392 private SelectableScheme[] column_schemes; 1393 1394 1397 private MasterTableJournal table_journal; 1398 1399 1403 private int last_entry_ri_check; 1404 1405 1408 public MMutableTableDataSource(SimpleTransaction transaction, 1409 MasterTableJournal journal) { 1410 this.transaction = transaction; 1411 this.index_set = 1412 transaction.getIndexSetForTable(MasterTableDataSource.this); 1413 int col_count = getDataTableDef().columnCount(); 1414 this.table_name = getDataTableDef().getTableName(); 1415 this.tran_read_only = transaction.isReadOnly(); 1416 row_list_rebuild = 0; 1417 scheme_rebuilds = new int[col_count]; 1418 column_schemes = new SelectableScheme[col_count]; 1419 table_journal = journal; 1420 last_entry_ri_check = table_journal.entries(); 1421 } 1422 1423 1428 private void executeUpdateReferentialAction( 1429 Transaction.ColumnGroupReference constraint, 1430 TObject[] original_key, TObject[] new_key, 1431 QueryContext context) { 1432 1433 final String update_rule = constraint.update_rule; 1434 if (update_rule.equals("NO ACTION") && 1435 constraint.deferred != Transaction.INITIALLY_IMMEDIATE) { 1436 return; 1438 } 1439 1440 MutableTableDataSource key_table = 1443 transaction.getTable(constraint.key_table_name); 1444 DataTableDef table_def = key_table.getDataTableDef(); 1445 int[] key_cols = TableDataConglomerate.findColumnIndices( 1446 table_def, constraint.key_columns); 1447 IntegerVector key_entries = 1448 TableDataConglomerate.findKeys(key_table, key_cols, original_key); 1449 1450 if (key_entries.size() > 0) { 1452 if (update_rule.equals("NO ACTION")) { 1453 throw new DatabaseConstraintViolationException( 1455 DatabaseConstraintViolationException.FOREIGN_KEY_VIOLATION, 1456 TableDataConglomerate.deferredString(constraint.deferred) + 1457 " foreign key constraint violation on update (" + 1458 constraint.name + ") Columns = " + 1459 constraint.key_table_name.toString() + "( " + 1460 TableDataConglomerate.stringColumnList(constraint.key_columns) + 1461 " ) -> " + constraint.ref_table_name.toString() + "( " + 1462 TableDataConglomerate.stringColumnList(constraint.ref_columns) + 1463 " )"); 1464 } 1465 else { 1466 int sz = key_entries.size(); 1468 for (int i = 0; i < sz; ++i) { 1469 int row_index = key_entries.intAt(i); 1470 RowData row_data = new RowData(key_table); 1471 row_data.setFromRow(row_index); 1472 if (update_rule.equals("CASCADE")) { 1473 for (int n = 0; n < key_cols.length; ++n) { 1475 row_data.setColumnData(key_cols[n], new_key[n]); 1476 } 1477 key_table.updateRow(row_index, row_data); 1478 } 1479 else if (update_rule.equals("SET NULL")) { 1480 for (int n = 0; n < key_cols.length; ++n) { 1481 row_data.setColumnToNull(key_cols[n]); 1482 } 1483 key_table.updateRow(row_index, row_data); 1484 } 1485 else if (update_rule.equals("SET DEFAULT")) { 1486 for (int n = 0; n < key_cols.length; ++n) { 1487 row_data.setColumnToDefault(key_cols[n], context); 1488 } 1489 key_table.updateRow(row_index, row_data); 1490 } 1491 else { 1492 throw new RuntimeException ( 1493 "Do not understand referential action: " + update_rule); 1494 } 1495 } 1496 key_table.constraintIntegrityCheck(); 1498 } 1499 } 1500 } 1501 1502 1507 private void executeDeleteReferentialAction( 1508 Transaction.ColumnGroupReference constraint, 1509 TObject[] original_key, QueryContext context) { 1510 1511 final String delete_rule = constraint.delete_rule; 1512 if (delete_rule.equals("NO ACTION") && 1513 constraint.deferred != Transaction.INITIALLY_IMMEDIATE) { 1514 return; 1516 } 1517 1518 MutableTableDataSource key_table = 1521 transaction.getTable(constraint.key_table_name); 1522 DataTableDef table_def = key_table.getDataTableDef(); 1523 int[] key_cols = TableDataConglomerate.findColumnIndices( 1524 table_def, constraint.key_columns); 1525 IntegerVector key_entries = 1526 TableDataConglomerate.findKeys(key_table, key_cols, original_key); 1527 1528 if (key_entries.size() > 0) { 1530 if (delete_rule.equals("NO ACTION")) { 1531 throw new DatabaseConstraintViolationException( 1533 DatabaseConstraintViolationException.FOREIGN_KEY_VIOLATION, 1534 TableDataConglomerate.deferredString(constraint.deferred) + 1535 " foreign key constraint violation on delete (" + 1536 constraint.name + ") Columns = " + 1537 constraint.key_table_name.toString() + "( " + 1538 TableDataConglomerate.stringColumnList(constraint.key_columns) + 1539 " ) -> " + constraint.ref_table_name.toString() + "( " + 1540 TableDataConglomerate.stringColumnList(constraint.ref_columns) + 1541 " )"); 1542 } 1543 else { 1544 int sz = key_entries.size(); 1546 for (int i = 0; i < sz; ++i) { 1547 int row_index = key_entries.intAt(i); 1548 RowData row_data = new RowData(key_table); 1549 row_data.setFromRow(row_index); 1550 if (delete_rule.equals("CASCADE")) { 1551 key_table.removeRow(row_index); 1553 } 1554 else if (delete_rule.equals("SET NULL")) { 1555 for (int n = 0; n < key_cols.length; ++n) { 1556 row_data.setColumnToNull(key_cols[n]); 1557 } 1558 key_table.updateRow(row_index, row_data); 1559 } 1560 else if (delete_rule.equals("SET DEFAULT")) { 1561 for (int n = 0; n < key_cols.length; ++n) { 1562 row_data.setColumnToDefault(key_cols[n], context); 1563 } 1564 key_table.updateRow(row_index, row_data); 1565 } 1566 else { 1567 throw new RuntimeException ( 1568 "Do not understand referential action: " + delete_rule); 1569 } 1570 } 1571 key_table.constraintIntegrityCheck(); 1573 } 1574 } 1575 } 1576 1577 1581 private IntegerListInterface getRowIndexList() { 1582 if (row_list == null) { 1583 row_list = index_set.getIndex(0); 1584 } 1585 return row_list; 1586 } 1587 1588 1594 private void ensureRowIndexListCurrent() { 1595 int rebuild_index = row_list_rebuild; 1596 int journal_count = table_journal.entries(); 1597 while (rebuild_index < journal_count) { 1598 byte command = table_journal.getCommand(rebuild_index); 1599 int row_index = table_journal.getRowIndex(rebuild_index); 1600 if (MasterTableJournal.isAddCommand(command)) { 1601 boolean b = getRowIndexList().uniqueInsertSort(row_index); 1603 if (b == false) { 1604 throw new Error ( 1605 "Row index already used in this table (" + row_index + ")"); 1606 } 1607 } 1608 else if (MasterTableJournal.isRemoveCommand(command)) { 1609 boolean b = getRowIndexList().removeSort(row_index); 1611 if (b == false) { 1612 throw new Error ("Row index removed that wasn't in this table!"); 1613 } 1614 } 1615 else { 1616 throw new Error ("Unrecognised journal command."); 1617 } 1618 ++rebuild_index; 1619 } 1620 row_list_rebuild = rebuild_index; 1622 } 1623 1624 1628 private void ensureColumnSchemeCurrent(int column) { 1629 SelectableScheme scheme = column_schemes[column]; 1630 int rebuild_index = scheme_rebuilds[column]; 1635 int journal_count = table_journal.entries(); 1636 while (rebuild_index < journal_count) { 1637 byte command = table_journal.getCommand(rebuild_index); 1638 int row_index = table_journal.getRowIndex(rebuild_index); 1639 if (MasterTableJournal.isAddCommand(command)) { 1640 scheme.insert(row_index); 1641 } 1642 else if (MasterTableJournal.isRemoveCommand(command)) { 1643 scheme.remove(row_index); 1644 } 1645 else { 1646 throw new Error ("Unrecognised journal command."); 1647 } 1648 ++rebuild_index; 1649 } 1650 scheme_rebuilds[column] = rebuild_index; 1651 } 1652 1653 1655 public TransactionSystem getSystem() { 1656 return MasterTableDataSource.this.getSystem(); 1657 } 1658 1659 public DataTableDef getDataTableDef() { 1660 return MasterTableDataSource.this.getDataTableDef(); 1661 } 1662 1663 public int getRowCount() { 1664 ensureRowIndexListCurrent(); 1666 return getRowIndexList().size(); 1667 } 1668 1669 public RowEnumeration rowEnumeration() { 1670 ensureRowIndexListCurrent(); 1672 final IntegerIterator iterator = getRowIndexList().iterator(); 1674 return new RowEnumeration() { 1676 public boolean hasMoreRows() { 1677 return iterator.hasNext(); 1678 } 1679 public int nextRowIndex() { 1680 return iterator.next(); 1681 } 1682 }; 1683 } 1684 1685 public TObject getCellContents(int column, int row) { 1686 return MasterTableDataSource.this.getCellContents(column, row); 1687 } 1688 1689 public SelectableScheme getColumnScheme(int column) { 1691 SelectableScheme scheme = column_schemes[column]; 1692 if (scheme == null) { 1694 scheme = createSelectableSchemeForColumn(index_set, this, column); 1695 column_schemes[column] = scheme; 1696 } 1697 1698 ensureColumnSchemeCurrent(column); 1700 1701 return scheme; 1702 } 1703 1704 1706 public int addRow(RowData row_data) { 1707 1708 if (tran_read_only) { 1710 throw new RuntimeException ("Transaction is read only."); 1711 } 1712 1713 if (isReadOnly()) { 1715 throw new Error ("Can not add row - table is read only."); 1716 } 1717 1718 int row_index; 1720 try { 1721 row_index = MasterTableDataSource.this.addRow(row_data); 1722 } 1723 catch (IOException e) { 1724 Debug().writeException(e); 1725 throw new Error ("IO Error: " + e.getMessage()); 1726 } 1727 1728 table_journal.addEntry(MasterTableJournal.TABLE_ADD, row_index); 1732 1733 return row_index; 1734 } 1735 1736 public void removeRow(int row_index) { 1737 1738 if (tran_read_only) { 1740 throw new RuntimeException ("Transaction is read only."); 1741 } 1742 1743 if (isReadOnly()) { 1745 throw new Error ("Can not remove row - table is read only."); 1746 } 1747 1748 1753 table_journal.addEntry(MasterTableJournal.TABLE_REMOVE, row_index); 1757 1758 } 1759 1760 public int updateRow(int row_index, RowData row_data) { 1761 1762 if (tran_read_only) { 1764 throw new RuntimeException ("Transaction is read only."); 1765 } 1766 1767 if (isReadOnly()) { 1769 throw new Error ("Can not update row - table is read only."); 1770 } 1771 1772 table_journal.addEntry(MasterTableJournal.TABLE_UPDATE_REMOVE, row_index); 1776 1777 int new_row_index; 1779 try { 1780 new_row_index = MasterTableDataSource.this.addRow(row_data); 1781 } 1782 catch (IOException e) { 1783 Debug().writeException(e); 1784 throw new Error ("IO Error: " + e.getMessage()); 1785 } 1786 1787 table_journal.addEntry(MasterTableJournal.TABLE_UPDATE_ADD, new_row_index); 1791 1792 return new_row_index; 1793 } 1794 1795 1796 public void flushIndexChanges() { 1797 ensureRowIndexListCurrent(); 1798 for (int i = 0; i < column_schemes.length; ++i) { 1800 getColumnScheme(i); 1801 } 1802 } 1803 1804 public void constraintIntegrityCheck() { 1805 try { 1806 1807 if (last_entry_ri_check == table_journal.entries()) { 1809 return; 1810 } 1811 1812 DataTableDef table_def = getDataTableDef(); 1814 TableName table_name = table_def.getTableName(); 1815 QueryContext context = 1816 new SystemQueryContext(transaction, table_name.getSchema()); 1817 1818 IntegerVector rows_updated = new IntegerVector(); 1821 IntegerVector rows_deleted = new IntegerVector(); 1822 IntegerVector rows_added = new IntegerVector(); 1823 1824 int size = table_journal.entries(); 1825 for (int i = last_entry_ri_check; i < size; ++i) { 1826 byte tc = table_journal.getCommand(i); 1827 int row_index = table_journal.getRowIndex(i); 1828 if (tc == MasterTableJournal.TABLE_REMOVE || 1829 tc == MasterTableJournal.TABLE_UPDATE_REMOVE) { 1830 rows_deleted.addInt(row_index); 1831 int ra_i = rows_added.indexOf(row_index); 1833 if (ra_i != -1) { 1834 rows_added.removeIntAt(ra_i); 1835 } 1836 } 1837 else if (tc == MasterTableJournal.TABLE_ADD || 1838 tc == MasterTableJournal.TABLE_UPDATE_ADD) { 1839 rows_added.addInt(row_index); 1840 } 1841 1842 if (tc == MasterTableJournal.TABLE_UPDATE_REMOVE) { 1843 rows_updated.addInt(row_index); 1844 } 1845 else if (tc == MasterTableJournal.TABLE_UPDATE_ADD) { 1846 rows_updated.addInt(row_index); 1847 } 1848 } 1849 1850 if (rows_deleted.size() > 0) { 1852 Transaction.ColumnGroupReference[] foreign_constraints = 1854 Transaction.queryTableImportedForeignKeyReferences(transaction, 1855 table_name); 1856 1857 for (int n = 0; n < foreign_constraints.length; ++n) { 1859 Transaction.ColumnGroupReference constraint = 1860 foreign_constraints[n]; 1861 for (int i = 0; i < rows_deleted.size(); ++i) { 1863 int row_index = rows_deleted.intAt(i); 1864 int[] cols = TableDataConglomerate.findColumnIndices( 1866 table_def, constraint.ref_columns); 1867 TObject[] original_key = new TObject[cols.length]; 1868 int null_count = 0; 1869 for (int p = 0; p < cols.length; ++p) { 1870 original_key[p] = getCellContents(cols[p], row_index); 1871 if (original_key[p].isNull()) { 1872 ++null_count; 1873 } 1874 } 1875 if (null_count != cols.length) { 1877 int update_index = rows_updated.indexOf(row_index); 1879 if (update_index != -1) { 1880 int row_index_add = rows_updated.intAt(update_index + 1); 1882 boolean key_changed = false; 1885 TObject[] key_updated_to = new TObject[cols.length]; 1886 for (int p = 0; p < cols.length; ++p) { 1887 key_updated_to[p] = getCellContents(cols[p], row_index_add); 1888 if (original_key[p].compareTo(key_updated_to[p]) != 0) { 1889 key_changed = true; 1890 } 1891 } 1892 if (key_changed) { 1893 executeUpdateReferentialAction(constraint, 1896 original_key, key_updated_to, context); 1897 } 1898 } 1900 else { 1901 executeDeleteReferentialAction(constraint, original_key, 1905 context); 1906 } 1907 1908 } 1910 } 1912 } 1914 } 1915 1916 if (rows_added.size() > 0) { 1918 int[] row_indices = rows_added.toIntArray(); 1919 1920 TableDataConglomerate.checkFieldConstraintViolations( 1922 transaction, this, row_indices); 1923 TableDataConglomerate.checkAddConstraintViolations( 1925 transaction, this, 1926 row_indices, Transaction.INITIALLY_IMMEDIATE); 1927 } 1928 1929 } 1930 catch (DatabaseConstraintViolationException e) { 1931 1932 int rollback_point = table_journal.entries() - last_entry_ri_check; 1935 if (row_list_rebuild <= rollback_point) { 1936 table_journal.rollbackEntries(rollback_point); 1937 } 1938 else { 1939 System.out.println( 1940 "WARNING: rebuild_pointer is after rollback point so we can't " + 1941 "rollback to the point before the constraint violation."); 1942 } 1943 1944 throw e; 1945 1946 } 1947 finally { 1948 last_entry_ri_check = table_journal.entries(); 1950 } 1951 1952 } 1953 1954 public MasterTableJournal getJournal() { 1955 return table_journal; 1956 } 1957 1958 public void dispose() { 1959 for (int i = 0; i < column_schemes.length; ++i) { 1963 SelectableScheme scheme = column_schemes[i]; 1964 if (scheme != null) { 1965 scheme.dispose(); 1966 column_schemes[i] = null; 1967 } 1968 } 1969 row_list = null; 1970 table_journal = null; 1971 scheme_rebuilds = null; 1972 index_set = null; 1973 transaction = null; 1974 } 1975 1976 public void addRootLock() { 1977 MasterTableDataSource.this.addRootLock(); 1978 } 1979 1980 public void removeRootLock() { 1981 MasterTableDataSource.this.removeRootLock(); 1982 } 1983 1984 } 1985 1986} 1987 | Popular Tags |