Accessing Records
Btrieve provides both physical and logical access to your data. With physical access, Btrieve retrieves records based on the physical record address within the file. With logical access, Btrieve retrieves records based on a key value contained in the record. In addition, Btrieve also allows you to access “chunks” of data within a record.
Accessing Records by Physical Location
Record accessing by physical location is faster for the following reasons:
Physical Currency
Physical currency is the effect on positioning when accessing records by physical location. When you insert a record, the MicroKernel writes that record into the first free space available in the file, regardless of any key values contained in the record. This location is referred to as the physical location, or address, of the record. The record remains in this location until you delete it from the file. The Btrieve Step operations use the physical location to access records.
The record accessed last is the current physical record. The next physical record is the record with the immediately higher address relative to the current physical record. The previous physical record is the record with the immediately lower address. There is no physical record previous to the first physical record; likewise, there is no physical record next to the last physical record.
Together, the current, next, and previous physical locations form the physical currency within a file.
Step Operations
Your application can use the Step operations to access records based on their physical location within a file. For example, the Step First operation (33) retrieves the record that is stored in the first, or lowest, physical location in the file.
*Note: You cannot perform Step operations on key-only files.
The Step Next operation (24) retrieves the record stored in the next higher physical location. The Step Previous operation (35) retrieves the record stored in the next lower physical location in the file. The Step Last operation (34) retrieves the record that is stored in the last, or highest, physical location in the file.
The Step Next Extended (38) and Step Previous Extended (39) operations retrieve one or more records from the physical location following or preceding the current record.
*Note: Each of the Step operations re-establishes the physical currency but destroys the logical currency (even if one existed before).
Accessing Records by Key Value
Accessing records by key value allows you to retrieve records based on their values for a specified key.
Logical Currency
Logical currency is the effect on positioning when accessing records by key value. When you insert a record into a file, the MicroKernel updates each B-tree index for which the appropriate key in the record has a non-null value. Each key of a file determines a logical ordering of the records. The ordering is determined by the key’s defined sort order or ACS.
The record accessed last is the current logical record. (This record is not necessarily the last record retrieved. The last record could have been retrieved by the Get Direct/Chunk operation (23), which does not change the logical currency.) The next logical record is the record that is immediately next in the defined logical sequence. The previous logical record is the record that is immediately previous in the defined logical sequence. There is no logical record previous to the first logical record; likewise, there is no logical record next to the last logical record.
Together, the current, next, and previous logical records form the logical currency within a file.
The current logical record is also the current physical record, except when you perform an operation that uses the no-currency-change (NCC) option or when you operate on a record with a null key value. For example, you can perform an NCC Insert operation (2) and have the same logical position in the file as you had prior to the insert. The physical currency is updated.
NCC operations are useful when you must preserve your logical currency in order to perform another operation. For example, you may want to insert or update a record and then use a Get Next operation (6) based on your original logical currency.
NCC Insert Operation
status = BTRV( B_GET_FIRST, posBlock, dataBuf, &dataLen, keyBuf, keyNum); /* gets first record in key path */
 
for (i = 0; i < numRecords; i++)
{ status = BTRV( B_INSERT, posBlock, dataBuf, &dataLen, keyBuf, -1); /* -1 for key num indicates no currency change */
} /* inserts several records */
 
status = BTRV( B_GET_NEXT, posBlock, dataBuf, &dataLen, keyBuf, keyNum); /* gets next record after first record in key path */
 
*Note: When you use an NCC operation, the MicroKernel does not return any information in the Key Buffer parameter. If you want to maintain logical currency, you must not change the value in the Key Buffer following an NCC operation. Otherwise, your next Get operation can have unpredictable results.
Get Operations
Your application can use the Get operations to retrieve records based on their values for a specified key. The appropriate Get operation can retrieve a specific record from a file or retrieve records in a certain order.
For example, the Get First operation (12) retrieves the first record by the key specified in the Key Number parameter. Likewise, the Get Last operation (13) retrieves the last record according to the logical order based on the specified key. Some Get operations, such as Get Equal (5) or Get Less Than (10), return a record based on a key value your application specifies in the Key Buffer parameter.
1
2
3
In addition to establishing logical currency, all Get operations except Get Position (22) establish the physical currency. As a result, you can continue with a Step Next (24) or Step Previous (35) operation. However, using the Step operations destroys the logical currency.
1
2
Reading Variable-Length Records
Reading a variable-length record is the same as reading a fixed-length record in that you use the Data Buffer Length parameter to tell the MicroKernel how much room you have for the record to be returned. Set this parameter to the size of your entire Data Buffer, which can accommodate the maximum amount of data.
*Note: Do not set the Data Buffer Length to a value larger than the number of bytes allocated to your Data Buffer; doing so could lead to a memory overwrite when running your application.
After a successful read operation, the Data Buffer Length parameter is changed to reflect the size of the returned record, which is the size of the fixed-length portion plus the amount of actual data in the variable portion (not the maximum size of a record). Your application should use this value to determine how much data is in the Data Buffer.
For example, suppose you have the following records in a data file:
Following are examples of Get Equal operations.
*Note: While developing and debugging your application, it helps to display the Data Buffer Length just before and after the read operation to verify that it is set correctly at each point.
Get Equal Operation in C
/* get the record with key 1= 263512477 using B_GET_EQUAL */
memset(&dataBuf, 0, sizeof(dataBuf));
dataBufLen = sizeof(dataBuf); /* this should be 1047 */
account = 263512477;
*(BTI_LONG BTI_FAR *)&keyBuf[0] = account;
status = BTRV( B_GET_EQUAL, posBlock, &dataBuf, &dataBufLen, keyBuf, 1);
/* the dataBufLen should now be 56 */
Get Equal Operation in Visual BASIC
dataBufLen= length(dataBuf) ' this should be 1047
account% = 263512477
status = BTRV(B_GETEQUAL, PosBlock$, dataBuf, dataBufLen, account%, 1)
' the dataBufLen should now be 56
If the returned record is longer than the value specified by the Data Buffer Length, the MicroKernel returns as much data as it can (according to the size the Data Buffer Length was set to) and Status Code 22.
Accessing Records by Chunks
Btrieve’s Data Buffer Length parameter, because it is a 16-bit unsigned integer, limits the record length to 65,535. Chunk operations expand the record length well beyond this limit by allowing you to read or write portions of a record. A chunk is defined as an offset and length; the offset can be as large as 64 GB, but the length is limited to 65,535 bytes. The limits that your operating system and the Data Buffer Length parameter impose also apply to the chunk operations; however, because the chunk operations can access any portion of a record, the limits have no effect on record length, only on the maximum size of a chunk accessible in a single operation.
For example, using the chunk operations, an application can read a 150,000 byte record by making three chunk retrieval calls. Each chunk in this example is 50,000 bytes long. The first chunk starts at offset zero, the next at offset 50,000, and the final at offset 100,000.
A chunk’s offset and length do not have to correspond to any of the internal structures of a record that are known to the MicroKernel, such as key segments, the fixed-length portion of a record, or a variable tail. Also, a chunk does not have to correspond to any portion of the record that your application defines (for example, a field), although you may find it useful to update such defined portions as chunks.
*Note: Chunks are defined only for the duration of the operation that defines them.
In some cases, the use of chunk operations in client/server environments allows the client Requester to use a smaller Data Buffer Length setting, which lowers the Requester’s memory requirement. For example, if your application used whole record operations and accessed records up to 50 KB long, your Requester would have to set its Data Buffer Length to at least 50 KB, thereby using 50 KB of RAM. But if your application used chunk operations and limited the size of each chunk to 10 KB, for example, then the Requester could set its Data Buffer Length to 10 KB, thereby saving 40 KB of RAM.
Intrarecord Currency
Intrarecord currency is relevant in chunk operations, because it tracks an offset within the current record. The current position is the offset that is one byte beyond the last byte of the chunk that was read or written. (This is true even if in your last operation you attempted to read an entire record and the MicroKernel could return only part of that record, which can happen when the Data Buffer Length is inadequate.)
The exception is for an Update Chunk operation (53) that uses the Truncate subfunction. In this case, the MicroKernel defines the current position in the truncated record as the offset that is one byte beyond the end of the record itself.
By tracking intrarecord currency, the MicroKernel can do the following:
You specify an original offset, length and number of chunks, and the MicroKernel calculates the subsequent offsets.
Intrarecord currency can speed up any chunk operation, as long as it operates on the same record that was last accessed by the Position Block, and as long as the next chunk offset is greater than the current position in the record. (That is, you do not have to access the next immediate byte in the record to benefit from intrarecord currency.)
*Note: The MicroKernel maintains intrarecord currency only for the current record. When you change physical or logical currency, the MicroKernel resets the intrarecord currency, as well.
Chunk Operations
You access chunks using the Get Direct/Chunk operation (23) and the Update Chunk operation (53). To use these operations, you must define a chunk descriptor structure that defines the offset and length of the chunk. For the Get Direct/Chunk operation, the chunk descriptor structure must also specify an address to which the MicroKernel returns the chunk.
Before you can use the Get Direct/Chunk operation, you must retrieve the physical address of the current record by issuing the Get Position operation (22). You can use a single Chunk operation to retrieve or update multiple chunks in a record by using a next-in-record subfunction bias.