After deciding on the basic storage structures, we can begin implementing operations on them. This section starts by providing road-map to example code for specific types of operations. The code should be self explanatory. Later sub-sections discuss some of the less obvious aspects of the implementation.
Before creating a storage volume, the device (raw device or Unix file) where it will reside must be initialized. A flag to setup_device_and_volume() indicates whether the device is to be initialized (ie. we're starting from scratch) or if there is already an initialized device.
If initialization is needed, the device is formatted using ss_m::format_dev. To begin using the device, we mount it with ss_m::mount_dev. Then, create a volume on the device with ss_m::create_vol. Since we will be using the SSM's logical ID support the ss_m::add_logical_id_index method must be called to create the index that maps from logical ID to physical IDs. The reason the logical ID index is not added automatically by ss_m::create_vol is an artifact of the fact that the SSM also has an interface based on physical IDs (the physical ID interface it not supported yet).
If no initialization is needed, we mount the device and use ss_m::list_volumes to find the ID of the volume on the device.
The code in command_server_t::init() uses the root index of the volume to determine if the grid storage structures have already been created. If not it creates them.
All the grid interface commands that update data involve changing more than one storage structure. For example, the ``add'' command creates an item and makes corresponding entries in the name and location indexes. If any of these operations should fail, it is necessary to roll back to the point just before the first operation to keep the database consistent. Therefore, the first thing grid_t::add_item does is create a save-point using ss_m::save_work. If any of the later create operations reports an error, ss_m::rollback_work is called.
The use of save-points around all operations that make changes to storage structures is highly recommended. That way even if an ss_m update method should fail, any partial work it completed is rolled back. The only other safe choice is to abort the entire transaction.
Reading records is accomplished by using the pin_i class to pin the record in the buffer pool. A pin_i object is often called a ``handle'' to a record. There are a couple of things here to keep in mind when using the pin_i interface.
The body method returns a const pointer to the record body. Never modify the record through these pointers, otherwise roll back and recovery information will not be generated. The most efficient way to modify a pinned record is to use the update_rec, append_rec and truncate_rec methods of class pin_i rather than those in ss_m . If the append or truncate methods cause the record to be moved, the pin object will continue to point to it.
A record can also be updated using the update_rec, append_rec and truncate_rec methods of class ss_m. However, if the record is already pinned, the pin object will not be changed to reflect the update and pin_i::repin would need to be called.