Class DBAnnotatedObject

java.lang.Object
ghidra.program.database.DatabaseObject
ghidra.util.database.DBAnnotatedObject
Direct Known Subclasses:
AbstractDBTraceSpaceBasedManager.DBTraceSpaceEntry, AbstractDBTraceSymbol, DBTraceAddressPropertyManager.DBTraceAddressPropertyEntry, DBTraceCodeManager.DBTraceCodePrototypeEntry, DBTraceEquate, DBTraceGuestPlatform, DBTraceGuestPlatform.DBTraceGuestLanguage, DBTraceGuestPlatformMappedRange, DBTraceMemoryBufferEntry, DBTraceObject, DBTraceObjectManager.DBTraceObjectSchemaEntry, DBTraceOverlaySpaceAdapter.DBTraceOverlaySpaceEntry, DBTraceRegisterContextSpace.DBTraceRegisterEntry, DBTraceSnapshot, DBTraceStaticMapping, DBTraceSymbolManager.DBTraceVariableStorageEntry, DBTreeRecord

public class DBAnnotatedObject extends DatabaseObject
An object backed by a DBRecord

Essentially, this is a data access object (DAO) for Ghidra's custom database engine. Not all object fields necessarily have a corresponding database field. Instead, those fields are annotated, and various methods are provided for updating the record, and conversely, re-loading fields from the record. These objects are managed using a DBCachedObjectStore. An example object definition:


 interface Person {
 	// ...
 }
 
 @DBAnnotatedObjectInfo(version = 1)
 public class DBPerson extends DBAnnotatedObject implements Person {
 	public static final String TABLE_NAME = "Person"; // Conventionally defined here
 
 	// Best practice is to define column names, then use in annotations
 	static final String NAME_COLUMN_NAME = "Name";
 	static final String ADDRESS_COLUMN_NAME = "Address";
 
 	// Column handles
 	@DBAnnotatedColumn(NAME_COLUMN_NAME)
 	static DBObjectColumn NAME_COLUMN;
 	@DBAnnotatedColumn(ADDRESS_COLUMN_NAME)
 	static DBObjectColumn ADDRESS_COLUMN;
 
 	// Column-backed fields
 	@DBAnnotatedField(column = NAME_COLUMN_NAME, indexed = true)
 	private String name;
 	@DBAnnotatedField(column = ADDRESS_COLUMN_NAME)
 	private String address;
 
 	DBPerson(DBCachedObjectStore<DBPerson> store, DBRecord record) {
 		super(store, record);
 	}
 
 	// Not required, but best practice
 	private void set(String name, String address) {
 		this.name = name;
 		this.address = address;
 		update(NAME_COLUMN, ADDRESS_COLUMN);
 	}
 
 	// ... other methods, getters, setters
 }
 

See DBCachedObjectStoreFactory for example code that uses the example DBPerson class.

All realizations of DBAnnotatedObject must be annotated with DBAnnotatedObjectInfo. This, along with the field annotations, are used to derive the table schema. Note the inclusion of a TABLE_NAME field. It is not required, nor is it used implicitly. It's included in this example as a manner of demonstrating best practice. When instantiating the object store, the field is used to provide the table name.

Next, we define the column names. These are not required nor used implicitly, but using literal strings in the column annotations is discouraged. Next, we declare variables to receive column handles. These are essentially the column numbers, but we have a named handle for each. They are initialized automatically the first time a store is created for this class.

Next we declare the variables representing the actual column values. Their initialization varies depending on how the object is instantiated. When creating a new object, the fields remain uninitialized. In some cases, it may be appropriate to provide an initial (default) value in the usual fashion, e.g., private String address = "123 Pine St."; In this case, the corresponding database field of the backing record is implicitly initialized upon creation. If the object is being loaded from a table, its fields are initialized with values from its backing record.

Next we define the constructor. There are no requirements on its signature, but it must call super, so it likely takes its containing store and its backing record. Having the same signature as its super constructor allows the store to be created using a simple method reference, e.g., DBPerson::new. Additional user-defined parameters may be accepted. To pass such parameters, a lambda is recommended when creating the object store.

Finally, we demonstrate how to update the record. The record is not implicitly updated by direct modification of an annotated field. All setters must call update(DBObjectColumn...) after updating a field. A common practice, especially when the object will have all its fields set at once, is to include a set method that initializes the fields and updates the record in one update(DBObjectColumn...).

Note that there is no way to specify the primary key. For object stores, the primary key is always the object id, and its type is always long.

  • Constructor Details

    • DBAnnotatedObject

      protected DBAnnotatedObject(DBCachedObjectStore<?> store, DBRecord record)
      The object constructor
      Parameters:
      store - the store containing this object
      record - the record backing this object
  • Method Details

    • getObjectKey

      public ObjectKey getObjectKey()
      Get an opaque unique id for this object, whose hash is immutable
      Returns:
      the opaque object id
    • doWrite

      protected void doWrite(DBObjectColumn column)
    • update

      protected void update(DBObjectColumn column)
      1-arity version of update(DBObjectColumn...)
      Parameters:
      column - the column
    • update

      protected void update(DBObjectColumn col1, DBObjectColumn col2)
      2-arity version of update(DBObjectColumn...)
      Parameters:
      col1 - a column
      col2 - another column
    • update

      protected void update(DBObjectColumn col1, DBObjectColumn col2, DBObjectColumn col3)
      3-arity version of update(DBObjectColumn...)
      Parameters:
      col1 - a column
      col2 - another column
      col3 - another column
    • update

      protected void update(DBObjectColumn... columns)
      Write the given columns into the record and update the table
      Parameters:
      columns - the columns to update
    • doUpdateAll

      protected void doUpdateAll() throws IOException
      Throws:
      IOException
    • doUpdated

      protected void doUpdated() throws IOException
      Throws:
      IOException
    • fresh

      protected void fresh(boolean created) throws IOException
      Extension point: Called when the object's fields are populated.

      This provides an opportunity for the object to initialize any non-database-backed fields that depend on the database-backed fields. Note that its use may indicate a situation better solved by a custom DBCachedObjectStoreFactory.DBFieldCodec. If both the database-backed and non-database-backed fields are used frequently, then a codec may not be indicated. If the database-backed fields are only used in this method or to encode another frequently-used field, then a codec is likely better.

      For a new object, the database-backed fields remain at their initial values. They will be saved after this method returns, so they may be further initialized with custom logic.

      For an object loaded from the database, the database-backed fields are already populated from the record when this method is called. They are not automatically saved after this method returns. This method should not further initialize database-backed fields in this case.

      Parameters:
      created - true when object is being created, or false when it is being loaded.
      Throws:
      IOException - if further initialization fails.
    • refresh

      protected boolean refresh()
      Description copied from class: DatabaseObject
      Tells the object to refresh its state from the database.
      Specified by:
      refresh in class DatabaseObject
      Returns:
      true if the object was able to refresh itself. Return false if the object was deleted. Objects that extend this class must implement a refresh method. If an object can never refresh itself, then it should always return false.
    • refresh

      protected boolean refresh(DBRecord rec)
      Description copied from class: DatabaseObject
      Tells the object to refresh its state from the database using the specified record if not null. NOTE: The default implementation ignores the record and invokes refresh(). Implementations of this method must take care if multiple database tables are used since the record supplied could correspond to another object. In some cases it may be best not to override this method or ignore the record provided.
      Overrides:
      refresh in class DatabaseObject
      Parameters:
      rec - valid record associated with object's key (optional, may be null to force record lookup or other refresh technique)
      Returns:
      true if the object was able to refresh itself. Return false if record is null and object was deleted. Objects that extend this class must implement a refresh method. If an object can never refresh itself, then it should always return false.
    • doRefresh

      protected boolean doRefresh(DBRecord rec) throws IOException
      Throws:
      IOException
    • isDeleted

      public boolean isDeleted()
      Check if this object has been deleted
      Returns:
      true if deleted
      See Also:
    • getTableName

      public String getTableName()