mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 10:19:23 +02:00
551 lines
15 KiB
Java
551 lines
15 KiB
Java
/* ###
|
|
* IP: GHIDRA
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
package db;
|
|
|
|
import static org.junit.Assert.*;
|
|
|
|
import java.io.File;
|
|
import java.io.IOException;
|
|
import java.util.Arrays;
|
|
|
|
import org.junit.*;
|
|
|
|
import db.buffers.*;
|
|
import generic.test.AbstractGenericTest;
|
|
import ghidra.util.exception.CancelledException;
|
|
import ghidra.util.exception.DuplicateFileException;
|
|
import utilities.util.FileUtilities;
|
|
|
|
/**
|
|
* Test the creation of a new database with indexed and non-indexed tables.
|
|
* Tests include the removal of tables and are performed for both a stored and
|
|
* non-stored database.
|
|
*/
|
|
public class DBTest extends AbstractGenericTest {
|
|
|
|
private static final int BUFFER_SIZE = 256;
|
|
private static final int CACHE_SIZE = 4 * 1024 * 1024;
|
|
|
|
private File testDir;
|
|
private static final String dbName = "test";
|
|
private static final String dbName2 = "test2";
|
|
|
|
private BufferFileManager fileMgr;
|
|
private DBHandle dbh;
|
|
private BufferFile bfile;
|
|
|
|
@Before
|
|
public void setUp() throws Exception {
|
|
|
|
testDir = createTempDirectory(getClass().getSimpleName());
|
|
dbh = new DBHandle(BUFFER_SIZE, CACHE_SIZE);
|
|
}
|
|
|
|
@After
|
|
public void tearDown() throws Exception {
|
|
if (dbh != null) {
|
|
dbh.close();
|
|
}
|
|
if (bfile != null) {
|
|
bfile.close();
|
|
}
|
|
FileUtilities.deleteDir(testDir);
|
|
}
|
|
|
|
private void saveAsAndReopen(String name) throws IOException {
|
|
try {
|
|
BufferFileManager mgr = DBTestUtils.getBufferFileManager(testDir, name);
|
|
BufferFile bf = new LocalManagedBufferFile(dbh.getBufferSize(), mgr, -1);
|
|
dbh.saveAs(bf, true, null);
|
|
dbh.close();
|
|
fileMgr = mgr;
|
|
}
|
|
catch (CancelledException e) {
|
|
Assert.fail("Should not happen");
|
|
}
|
|
bfile = new LocalManagedBufferFile(fileMgr, true, -1, -1);
|
|
dbh = new DBHandle(bfile);
|
|
}
|
|
|
|
private void saveAndReopen() throws IOException {
|
|
assertTrue(dbh.canUpdate());
|
|
long dbId = dbh.getDatabaseId();
|
|
try {
|
|
dbh.save(null, null, null);
|
|
}
|
|
catch (CancelledException e) {
|
|
Assert.fail("Should not happen");
|
|
}
|
|
dbh.close();
|
|
bfile = new LocalManagedBufferFile(fileMgr, true, -1, -1);
|
|
dbh = new DBHandle(bfile);
|
|
assertEquals(dbId, dbh.getDatabaseId());
|
|
}
|
|
|
|
@Test
|
|
public void testCreateDatabase() {
|
|
|
|
assertTrue(!dbh.canUpdate());
|
|
assertTrue(!dbh.canRedo());
|
|
assertTrue(!dbh.canUndo());
|
|
|
|
try {
|
|
dbh.checkTransaction();
|
|
Assert.fail();
|
|
}
|
|
catch (NoTransactionException e) {
|
|
// expected
|
|
}
|
|
|
|
assertEquals(dbh.getTableCount(), 0);
|
|
}
|
|
|
|
@Test
|
|
public void testCreateExistingDatabase() throws IOException {
|
|
saveAsAndReopen(dbName);
|
|
try {
|
|
saveAsAndReopen(dbName);
|
|
Assert.fail();
|
|
}
|
|
catch (DuplicateFileException e) {
|
|
// good; expected
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testCreateDatabaseCopies() throws IOException {
|
|
long dbId = dbh.getDatabaseId();
|
|
saveAsAndReopen(dbName);
|
|
assertTrue(dbId == dbh.getDatabaseId());// First SaveAs should preserve ID
|
|
try {
|
|
saveAsAndReopen(dbName2);
|
|
assertTrue(dbId != dbh.getDatabaseId());// Second SaveAs should change ID to avoid duplication
|
|
}
|
|
catch (DuplicateFileException e) {
|
|
Assert.fail();
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testOpenForReadOnlyDuringUpdate() throws IOException {
|
|
saveAsAndReopen(dbName);
|
|
|
|
BufferFile bf2 = new LocalManagedBufferFile(fileMgr, false, -1, -1);
|
|
DBHandle dbh2 = null;
|
|
try {
|
|
dbh2 = new DBHandle(bf2);
|
|
createNonIndexedTables(false, DBTestUtils.MAX_SCHEMA_TYPE + 1);
|
|
saveAndReopen();
|
|
assertEquals(DBTestUtils.MAX_SCHEMA_TYPE + 1, dbh.getTableCount());
|
|
assertEquals(0, dbh2.getTableCount());
|
|
}
|
|
finally {
|
|
if (dbh2 != null) {
|
|
dbh2.close();
|
|
}
|
|
bf2.close();
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testCreateLongKeyTable() throws IOException {
|
|
long txId = dbh.startTransaction();
|
|
Table table =
|
|
DBTestUtils.createLongKeyTable(dbh, "TABLE1", DBTestUtils.ALL_TYPES, false, false);
|
|
dbh.endTransaction(txId, true);
|
|
String[] names = table.getSchema().getFieldNames();
|
|
assertTrue(Arrays.equals(DBTestUtils.getFieldNames(DBTestUtils.ALL_TYPES), names));
|
|
}
|
|
|
|
@Test
|
|
public void testCreateVarKeyTable() throws IOException {
|
|
long txId = dbh.startTransaction();
|
|
Table table = DBTestUtils.createBinaryKeyTable(dbh, "TABLE1", DBTestUtils.ALL_TYPES, false);
|
|
dbh.endTransaction(txId, true);
|
|
String[] names = table.getSchema().getFieldNames();
|
|
assertTrue(Arrays.equals(DBTestUtils.getFieldNames(DBTestUtils.ALL_TYPES), names));
|
|
}
|
|
|
|
@Test
|
|
public void testStoredCreateLongKeyTable() throws IOException {
|
|
long txId = dbh.startTransaction();
|
|
DBTestUtils.createLongKeyTable(dbh, "TABLE1", DBTestUtils.ALL_TYPES, false, false);
|
|
dbh.endTransaction(txId, true);
|
|
saveAsAndReopen(dbName);
|
|
Table table = dbh.getTable("TABLE1");
|
|
assertTrue(table.useLongKeys());
|
|
String[] names = table.getSchema().getFieldNames();
|
|
assertTrue(Arrays.equals(DBTestUtils.getFieldNames(DBTestUtils.ALL_TYPES), names));
|
|
}
|
|
|
|
@Test
|
|
public void testStoredCreateVarKeyTable() throws IOException {
|
|
long txId = dbh.startTransaction();
|
|
DBTestUtils.createBinaryKeyTable(dbh, "TABLE1", DBTestUtils.ALL_TYPES, false);
|
|
dbh.endTransaction(txId, true);
|
|
saveAsAndReopen(dbName);
|
|
Table table = dbh.getTable("TABLE1");
|
|
assertTrue(!table.useLongKeys());
|
|
assertTrue(table.getSchema().getKeyFieldType().getClass().equals(BinaryField.class));
|
|
String[] names = table.getSchema().getFieldNames();
|
|
assertTrue(Arrays.equals(DBTestUtils.getFieldNames(DBTestUtils.ALL_TYPES), names));
|
|
}
|
|
|
|
private void createNonIndexedTables(boolean testStoredDB, int cnt) throws IOException {
|
|
|
|
// Create table for each schema defined by DBTestUtils
|
|
long txId = dbh.startTransaction();
|
|
for (int i = 0; i < cnt; i++) {
|
|
DBTestUtils.createLongKeyTable(dbh, "TABLE" + i, i % (DBTestUtils.MAX_SCHEMA_TYPE + 1),
|
|
false, false);
|
|
}
|
|
dbh.endTransaction(txId, true);
|
|
assertEquals(cnt, dbh.getTableCount());
|
|
|
|
if (testStoredDB) {
|
|
saveAsAndReopen(dbName);
|
|
assertEquals(cnt, dbh.getTableCount());
|
|
}
|
|
|
|
// Check Master Table entries
|
|
TableRecord[] tableRecords = dbh.getMasterTable().getTableRecords();
|
|
for (int i = 0; i < tableRecords.length; i++) {
|
|
String name = "TABLE" + i;
|
|
assertEquals(name, tableRecords[i].getName());
|
|
assertEquals(-1, tableRecords[i].getIndexedColumn());
|
|
assertEquals(Long.MIN_VALUE, tableRecords[i].getMaxKey());
|
|
assertEquals(0, tableRecords[i].getRecordCount());
|
|
assertEquals(-1, tableRecords[i].getRootBufferId());
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testNonIndexedTables() throws IOException {
|
|
createNonIndexedTables(false, DBTestUtils.MAX_SCHEMA_TYPE + 1);
|
|
}
|
|
|
|
@Test
|
|
public void testStoredNonIndexedTables() throws IOException {
|
|
createNonIndexedTables(true, DBTestUtils.MAX_SCHEMA_TYPE + 1);
|
|
}
|
|
|
|
private void createIndexedTables(boolean testStoredDB) throws IOException {
|
|
|
|
// Create table for each schema defined by DBTestUtils
|
|
// All schema fields are indexed
|
|
long txId = dbh.startTransaction();
|
|
for (int i = 0; i <= DBTestUtils.MAX_SCHEMA_TYPE; i++) {
|
|
DBTestUtils.createLongKeyTable(dbh, "TABLE" + i, i, true, false);
|
|
}
|
|
assertEquals(DBTestUtils.MAX_SCHEMA_TYPE + 1, dbh.getTableCount());
|
|
dbh.endTransaction(txId, true);
|
|
|
|
if (testStoredDB) {
|
|
saveAsAndReopen(dbName);
|
|
assertEquals(DBTestUtils.MAX_SCHEMA_TYPE + 1, dbh.getTableCount());
|
|
}
|
|
|
|
// Check Master Table entries
|
|
TableRecord[] tableRecords = dbh.getMasterTable().getTableRecords();
|
|
Table lastTable = null;
|
|
int tableCnt = 0;
|
|
int indexCnt = 0;
|
|
for (TableRecord tableRecord : tableRecords) {
|
|
if (tableRecord.getIndexedColumn() < 0) {
|
|
if (tableCnt > 0) {
|
|
assertEquals(DBTestUtils.getIndexedColumnCount(tableCnt - 1), indexCnt);
|
|
}
|
|
String name = "TABLE" + tableCnt;
|
|
lastTable = dbh.getTable(name);
|
|
assertEquals(name, tableRecord.getName());
|
|
assertEquals(Long.MIN_VALUE, tableRecord.getMaxKey());
|
|
assertEquals(0, tableRecord.getRecordCount());
|
|
assertEquals(-1, tableRecord.getRootBufferId());
|
|
++tableCnt;
|
|
indexCnt = 0;
|
|
}
|
|
else {
|
|
if (lastTable == null) {
|
|
Assert.fail();
|
|
}
|
|
int[] indexedColumns = DBTestUtils.getIndexedColumns(tableCnt - 1);
|
|
assertTrue(indexCnt < indexedColumns.length);
|
|
assertEquals(indexedColumns[indexCnt], tableRecord.getIndexedColumn());
|
|
assertEquals(lastTable.getName(), tableRecord.getName());
|
|
assertEquals(Long.MIN_VALUE, tableRecord.getMaxKey());
|
|
assertEquals(0, tableRecord.getRecordCount());
|
|
assertEquals(-1, tableRecord.getRootBufferId());
|
|
++indexCnt;
|
|
}
|
|
|
|
}
|
|
assertEquals(DBTestUtils.getIndexedColumnCount(tableCnt - 1), indexCnt);
|
|
assertEquals(DBTestUtils.MAX_SCHEMA_TYPE + 1, tableCnt);
|
|
|
|
}
|
|
|
|
@Test
|
|
public void testIndexedTables() throws IOException {
|
|
createIndexedTables(false);
|
|
}
|
|
|
|
@Test
|
|
public void testStoredIndexedTables() throws IOException {
|
|
createIndexedTables(true);
|
|
}
|
|
|
|
private void removeNonIndexedTables(boolean testStoredDB) throws IOException {
|
|
|
|
// Create table for each schema defined by DBTestUtils
|
|
createNonIndexedTables(testStoredDB, DBTestUtils.MAX_SCHEMA_TYPE + 1);
|
|
|
|
// Delete odd numbered tables
|
|
long txId = dbh.startTransaction();
|
|
int totalTableCnt = 0;
|
|
for (int i = 0; i <= DBTestUtils.MAX_SCHEMA_TYPE; i++) {
|
|
if ((i % 2) == 1) {
|
|
dbh.deleteTable("TABLE" + i);
|
|
}
|
|
else {
|
|
++totalTableCnt;
|
|
}
|
|
}
|
|
dbh.endTransaction(txId, true);
|
|
assertEquals(totalTableCnt, dbh.getTableCount());
|
|
|
|
if (testStoredDB) {
|
|
saveAndReopen();
|
|
assertEquals(totalTableCnt, dbh.getTableCount());
|
|
}
|
|
|
|
// Check Master Table entries
|
|
TableRecord[] tableRecords = dbh.getMasterTable().getTableRecords();
|
|
for (int i = 0; i < tableRecords.length; i++) {
|
|
String name = "TABLE" + (2 * i);
|
|
assertEquals(name, tableRecords[i].getName());
|
|
assertEquals(-1, tableRecords[i].getIndexedColumn());
|
|
assertEquals(Long.MIN_VALUE, tableRecords[i].getMaxKey());
|
|
assertEquals(0, tableRecords[i].getRecordCount());
|
|
assertEquals(-1, tableRecords[i].getRootBufferId());
|
|
}
|
|
assertEquals(totalTableCnt, tableRecords.length);
|
|
|
|
}
|
|
|
|
@Test
|
|
public void testRemoveNonIndexedTables() throws IOException {
|
|
removeNonIndexedTables(false);
|
|
}
|
|
|
|
@Test
|
|
public void testStoredRemoveNonIndexedTables() throws IOException {
|
|
removeNonIndexedTables(true);
|
|
}
|
|
|
|
private void removeIndexedTables(boolean testStoredDB) throws IOException {
|
|
|
|
// Create table for each schema defined by DBTestUtils
|
|
// All schema fields are indexed
|
|
createIndexedTables(testStoredDB);
|
|
|
|
// Delete odd numbered tables
|
|
long txId = dbh.startTransaction();
|
|
int totalTableCnt = 0;
|
|
for (int i = 0; i <= DBTestUtils.MAX_SCHEMA_TYPE; i++) {
|
|
if ((i % 2) == 1) {
|
|
dbh.deleteTable("TABLE" + i);
|
|
}
|
|
else {
|
|
++totalTableCnt;
|
|
}
|
|
}
|
|
dbh.endTransaction(txId, true);
|
|
assertEquals(totalTableCnt, dbh.getTableCount());
|
|
|
|
if (testStoredDB) {
|
|
saveAndReopen();
|
|
assertEquals(totalTableCnt, dbh.getTableCount());
|
|
}
|
|
|
|
// Check Master Table entries
|
|
TableRecord[] tableRecords = dbh.getMasterTable().getTableRecords();
|
|
Table lastTable = null;
|
|
int tableCnt = 0;
|
|
int indexCnt = 0;
|
|
for (TableRecord tableRecord : tableRecords) {
|
|
if (tableRecord.getIndexedColumn() < 0) {
|
|
if (tableCnt > 0) {
|
|
assertEquals(DBTestUtils.getIndexedColumnCount(2 * (tableCnt - 1)), indexCnt);
|
|
}
|
|
String name = "TABLE" + (2 * tableCnt);
|
|
lastTable = dbh.getTable(name);
|
|
assertEquals(name, tableRecord.getName());
|
|
assertEquals(Long.MIN_VALUE, tableRecord.getMaxKey());
|
|
assertEquals(0, tableRecord.getRecordCount());
|
|
assertEquals(-1, tableRecord.getRootBufferId());
|
|
++tableCnt;
|
|
indexCnt = 0;
|
|
}
|
|
else {
|
|
if (lastTable == null) {
|
|
Assert.fail();
|
|
}
|
|
int[] indexedColumns = DBTestUtils.getIndexedColumns(2 * (tableCnt - 1));
|
|
assertTrue(indexCnt < indexedColumns.length);
|
|
assertEquals(indexedColumns[indexCnt], tableRecord.getIndexedColumn());
|
|
assertEquals(lastTable.getName(), tableRecord.getName());
|
|
assertEquals(Long.MIN_VALUE, tableRecord.getMaxKey());
|
|
assertEquals(0, tableRecord.getRecordCount());
|
|
assertEquals(-1, tableRecord.getRootBufferId());
|
|
++indexCnt;
|
|
}
|
|
|
|
}
|
|
Schema schema = lastTable.getSchema();
|
|
assertEquals(schema.getFields().length - 2, indexCnt); // ByteField and BooleanField do not support indexing
|
|
assertEquals(totalTableCnt, tableCnt);
|
|
|
|
}
|
|
|
|
@Test
|
|
public void testRemoveIndexedTables() throws IOException {
|
|
removeIndexedTables(false);
|
|
}
|
|
|
|
@Test
|
|
public void testStoredRemoveIndexedTables() throws IOException {
|
|
removeIndexedTables(true);
|
|
}
|
|
|
|
@Test
|
|
public void testLargeMasterTable() throws IOException {
|
|
createNonIndexedTables(false, 1000);
|
|
}
|
|
|
|
@Test
|
|
public void testStoredLargeMasterTable() throws IOException {
|
|
createNonIndexedTables(true, 1000);
|
|
}
|
|
|
|
@Test
|
|
public void testMasterTableUndo() throws IOException {
|
|
createIndexedTables(false);
|
|
Table[] tables = dbh.getTables();
|
|
String table1Name = tables[1].getName();
|
|
|
|
// delete a table - and rollback
|
|
long txId = dbh.startTransaction();
|
|
for (Table table : tables) {
|
|
dbh.deleteTable(table.getName());
|
|
}
|
|
dbh.endTransaction(txId, false);
|
|
|
|
assertTrue(dbh.getBufferMgr().atCheckpoint());
|
|
|
|
try {
|
|
tables[1].getName();
|
|
Assert.fail();
|
|
}
|
|
catch (Exception e) {
|
|
// Ignore
|
|
}
|
|
|
|
Schema s = tables[1].getSchema();
|
|
DBRecord rec = s.createRecord(1);
|
|
|
|
txId = dbh.startTransaction();
|
|
try {
|
|
tables[1].putRecord(rec);
|
|
Assert.fail();
|
|
}
|
|
catch (Exception e) {
|
|
// Ignore
|
|
}
|
|
assertTrue(dbh.getBufferMgr().atCheckpoint());
|
|
|
|
Table t = dbh.getTable(table1Name);
|
|
assertEquals(table1Name, t.getName());
|
|
|
|
t.putRecord(rec);
|
|
assertTrue(!dbh.getBufferMgr().atCheckpoint());
|
|
dbh.endTransaction(txId, true);
|
|
|
|
assertEquals(rec, t.getRecord(1));
|
|
|
|
// delete a table - and commit
|
|
txId = dbh.startTransaction();
|
|
dbh.deleteTable(table1Name);
|
|
dbh.endTransaction(txId, true);
|
|
|
|
assertTrue(dbh.undo());
|
|
|
|
t = dbh.getTable(table1Name);
|
|
assertEquals(table1Name, t.getName());
|
|
|
|
assertTrue(dbh.redo());
|
|
|
|
t = dbh.getTable(table1Name);
|
|
assertNull(t);
|
|
|
|
}
|
|
|
|
@Test
|
|
public void testTableWithIndexRecreateUndo() throws IOException {
|
|
|
|
createIndexedTables(false);
|
|
|
|
Table[] tables = dbh.getTables();
|
|
String table1Name = tables[1].getName();
|
|
|
|
Table t1 = dbh.getTable(table1Name);
|
|
int[] indexedColumns = t1.getIndexedColumns();
|
|
assertEquals(t1.getSchema().getFieldCount(), indexedColumns.length);
|
|
for (int i = 0; i < indexedColumns.length; i++) {
|
|
assertEquals(i, indexedColumns[i]);
|
|
}
|
|
|
|
Schema schema = t1.getSchema();
|
|
|
|
long txId = dbh.startTransaction();
|
|
try {
|
|
t1.putRecord(schema.createRecord(1));
|
|
}
|
|
finally {
|
|
dbh.endTransaction(txId, true);
|
|
}
|
|
|
|
txId = dbh.startTransaction();
|
|
try {
|
|
dbh.deleteTable(table1Name);
|
|
dbh.createTable(table1Name, schema, indexedColumns);
|
|
}
|
|
finally {
|
|
dbh.endTransaction(txId, true);
|
|
}
|
|
|
|
dbh.undo();
|
|
|
|
Table t1prime = dbh.getTable(table1Name);
|
|
indexedColumns = t1prime.getIndexedColumns();
|
|
assertEquals(t1prime.getSchema().getFieldCount(), indexedColumns.length);
|
|
for (int i = 0; i < indexedColumns.length; i++) {
|
|
assertEquals(i, indexedColumns[i]);
|
|
}
|
|
|
|
assertNotNull(t1prime.getRecord(1));
|
|
|
|
}
|
|
}
|