The Storage Kit: BDatabase

Derived from: public BObject

Declared in: <storage/Database.h>


Overview

A BDatabase object represents a collection of structured, persistent data called a database. Each BDatabase object that you introduce to your application corresponds to an actual database and gives you access to it. Databases are contained within volumes , where a volume is a storage medium such as a hard disk, floppy disk, or CD-ROM. The relationship between databases and volumes is one-to-one: Each volume contains exactly one database. Part of the system's disk-formatting routine includes the creation of a database for the volume on the disk.


Finding a BDatabase

You never construct BDatabase objects yourself; instead, you ask the system to construct them and return them to you. There are two ways to do this:

      void DatabasePlucker(BList *dList)
      {
         BVolume this_vol;
         BDatabase *this_db;
         long index; 
      
         for (index = 0; this_vol = volume_at(index); index++)
         {
            this_db = this_vol.Database();
            dList->AddItem(this_db);
         }
      }

Just as you never construct BDatabase objects, so do you not destroy them. These tasks are performed automatically by the Storage Kit.

After you've retrieved a BDatabase object, you may wonder what you should do with it. A BDatabase has essentially two purposes: It acts as a key to the Storage Server, and it lets you find and create tables (BTable objects). These activities are described below.


BDatabase as a Key to the Storage Server

Every transaction with the Storage Server requires a database ID-- every time you retrieve a record or search for a file (as two examples), you need to tell the Storage Server which database to look in. Curiously, however, BDatabase objects don't appear in your application very often. This is because almost every Storage Kit object is created (or "validated" for whatever that means to the object) in reference to a paticular BDatabase which it (the newly created object) remembers for future use. In other words, BDatabase objects show up when you're creating other objects, but you can pretty much ignore them beyond that.

To give you a better idea of how this works, the following sections examine the relationships between BDatabase objects and instances of the other Storage Kit classes.

The Database Side: BTable, BRecord, and BQuery

These three classes, along with BDatabase itself, comprise the "database" side of the Storage Kit. BTable objects are created for you--each BDatabase object contains a list of BTable objects (as described in the next section). This proprietary relationship (between a BTable and the BDatabase that "owns" it) means that a BTable always knows how to get to a database.

BRecord objects are born knowing about the facts of life: Each of the four versions of the BRecord constructor takes an argument that, directly or indirectly, identifies a database.

Unlike the others, a BQuery object can be constructed without reference to a database. But such an object is essentially useless until you tell it which database it should operate on.

The File System Side: BVolume and BStore

BVolume and BStore along with BStore's derivations, BDirectory, BFile, and BResourceFile, are the Storage Kit's file system classes. The relationship between databases and volumes was described earlier: A BVolume object always knows its database.

The BStore class is similar to BQuery in that you can create an instance of (a class derived from) BStore without reference to a database, but the object will be useless until its database is set. You do this by setting the object's record_ref structure. The structure uniquely identifies a record in a database by listing (as structure fields) the database ID and the record ID (record ID numbers are unique within a database). record_ref structures (or refs ) are the primary means for identifying a file; they're visited again in the BStore class description.


Finding and Creating Tables

As mentioned earlier, BTable objects live within BDatabase objects: When you "open" a database (by asking for the BDatabase that represents it), the tables that are stored within are automatically represented in your BDatabase object as BTable objects. To get a BTable from a BDatabase, you can ask for it by name, through the FindTable() function, or you can step through the BDatabase's "table list" by using CountTables() and TableAt().

To create a table, you call the CreateTable() function. The function tells the Storage Server to manufacture a table in the database, and then constructs a BTable object to represent it, adds it to the BDatabase's table list, and returns the new object.

A BDatabase's table list can fall out of step with the database. Specifically, your object's table list isn't automatically updated when another application adds a new table to the database. To update your object's table list, you call BDatabase's Sync() function.


Constructor and Destructor

The BDatabase constructor and destructor are private. You never construct BDatabase objects directly; instead, you retrieve them from the system through the global database_for() function, or through BVolume's Database() function.


Member Functions


CountTables()

      long CountTables(void)

Returns the number of BTables in the BDatabase's table list.

See also: TableAt(), FindTable(), Sync()


CreateTable()

      BTable *CreateTable(char *tableName)
      BTable *CreateTable(char *tableName, char *parentName)

      BTable *CreateTable(char *tableName, BTable *parentTable)

Creates a table in the database, names it tableName, and constructs (and returns) a BTable object to represent it. The table that's created by the first version of this function will be empty--it won't contain any fields. In the other two versions, the new table will "inherit" the fields of the parent table (there's no functional difference between these two versions--they simply give you two ways to designate the parent table).

The BDatabase doesn't check to make sure that the name of the new table is unique: You can create a table with a given name even if that name identifies an existing table. If you want to make sure that your table's name won't collide with that of an existing table, you should call FindTable() first--and if you really want to be scrupulous, you should call Sync() just before that:

   /* Create a uniquely-named table called "Phylum". */
   a_db->Sync();
   if (a_db->FindTable("Phylum") == NULL)
      a_table = a_db->CreateTable("Phylum");

Furthermore, if you designate a parent but the parent isn't found, the new table is created without a parent. Again, you can check to make sure that the parent exists:

   /* Create a uniquely-named table that inherits from the
    * existing table called "Kingdom". 
    */
   a_db->Sync();
   if (a_db->FindTable("Phylum") == NULL &&
      a_db->FindTable("Kingdom") != NULL)
      a_table = a_db->CreateTable("Phylum", "Kingdom");

If CreateTable() can't create the table--this should only happen if the Storage Server can no longer communicate with the database--it returns NULL.

You never explicitly delete a BTable object. Constructing and deleting BTable objects is the BDatabase's responsibility.

See also: FindTable()


FindTable()

      BTable *FindTable(char *table_name)

Looks in the BDatabase's table list for the BTable that represents the named table. Returns the BTable if it's there; NULL if not. The table list includes all tables that live in the database --it isn't just a compilation of tables that were created by this particular object.

If you want to make sure that the list is up-to-date before looking for a table, you should first call BDatabase's Sync() function.

See also: TableAt(), Sync()


ID()

      database_id ID(void)

Returns an identifier that uniquely and persistently identifies the BDatabase's database. The value is meaningful system-wide--you can send it to other applications so they can find the same database, for example. The persistence of the value is eternal: The database that's identified by a particular database_id number today will still be identified by that number long after you've forgotten everything you ever knew.

Database ID numbers appear most commonly as the database fields of record_ref structures. A record_ref structure uniquely identifies a record among all records in all databases.

See also: BStore::SetRef()


IsValid()

      bool IsValid(void)

Returns TRUE if the BDatabase's database is (still) available; otherwise, it returns FALSE . The object will become invalid if the volume on which the database lives is unmounted.

Warning: Currently, this function always returns TRUE.


PrintToStream()

      void PrintToStream(void)

Displays, to standard output, information about the BTables that are contained in the BDatabase's table list. The information is displayed in this format:

   | index-table <name>, id #
         | fieldName1
         | fieldName2
         | fieldName3
         ...

For example, if the first BTable in the list is named "Shirts" and contains fields named "color," "texture," and "buttonCount," the display will look like this:

   | 0-table <Shirts>, id 0
   |  | color
   |  | texture
   |  | buttonCount

A BTable that inherits from another BTable is indented beneath its parent, and repeats the inherited fields:

   | 0-table <Shirts>, id 0
   |  | color
   |  | texture
   |  | buttonCount
   |  | 1-table <TackyShirts>, id 1
   |  |  | color
   |  |  | texture
   |  |  | buttonCount
   |  |  | hasStripes
   |  |  | isHawaiian

PrintToStream() is meant to be used as a debugging tool and party game.


Sync()

      void Sync(void)

Synchronizes the BDatabase object with the database that it represents by doing the following:

Calling Sync() is the only way to update the BDatabase's table list, whereas it isn't necessary to Sync() in order write committed data. Such data will (eventually) be written to the disk as a matter of routine (within seconds, typically); Sync(), in this regard, is a sop for the anxious.

See also: BRecord::Commit()


TableAt()

      BTable *TableAt(long index)

Returns the index'th BTable object in the BDatabase's table list (zero-based).

If you want to make sure that the list is up-to-date before looking for a table, you should first call BDatabase's Sync() function.

See also: CountTables() , Sync()


VolumeID()

      long VolumeID(void)

Returns the ID of the volume that contains the database that's represented by this BDatabase object.




The Be Book, HTML Edition, for Developer Release 8 of the Be Operating System.

Copyright © 1996 Be, Inc. All rights reserved.

Be, the Be logo, BeBox, BeOS, BeWare, and GeekPort are trademarks of Be, Inc.

Last modified September 6, 1996.