Table of Contents

            4.2.5 IBIS Record Operations

4.2.5 IBIS Record Operations

A record is a user-defined subset of column numbers which are of current interest in a file, and for which one would like to perform buffered file-io. The actual values within a row are typically accessed through the record mechanism, rather than through the raw row data.

Records contain sets of buffers for the disjoint locations in the file where the row values are stored, which often permits much more efficient file I/O performance. Multiple record pointers may be created within the same file, for transferring data from one set of rows to another, or one set of columns to another.

A record may be thought of as a windowed buffer into the file, to be used primarily for sequential access to a subset of columns. It has a "current row" pointer, which starts out at the beginning of row 1, and is advanced either by IBISRecordRead/Write calls, or by explicitly calling IBISRecordSet to set the 'row' value.

If the current buffer has been modified by either IBISRecordWrite or IBISRecordClear, and a subsequent call requires moving to a 'row' value outside the scope of the current buffer, the modified data is flushed to the file. The record buffer is also explicitly flushed when either the record or the ibis file itself is closed, or if the rows that have been modified in the buffer are subsequently accessed using IBISColumnRead, IBISColumnWrite, IBISColumnClear, or any of the IBISRow routines. For this reason, alternating between IBISRecord routines and these other routines is not recommended in general. IBISRecordOpen

status = IBISRecordOpen( ibis, record,  group, cols, ncols,u_format);
call ibis_record_open( ibis, record,group,cols,ncols,u_format, status)

ibis		input	integer
record		output	integer
cols		input	integer array
ncols		input	integer
group		input	string
u_format	input	keyword
status		output	integer

u_format= { NONE | {FORMAT_TYPE} }, default=<fmt_default>

Create and initialize new record, using either the ncols columns from array cols, or else if ncols=0, the columns in group group. Each row element is translated into the format specified by the u_format parameter, which by default is the 'fmt_default' format of the file. The order in which the columns occur in the cols array or group is significant, as it determines the order that the data will be placed into buffers passed to IBISRecordRead/Write. The record is in no way dependent upon the group following this call; the group may even be modified or deleted with no effect on the record's structure.

The routine returns in variable record a handle to the record structure, similar to the way that IBISFileOpen returned a handle to the ibis structure. All of the other IBISRecord routines use this record handle, rather than the pure ibis structure.

When the record is open, it checks each column's file format, and determines the translation method necessary to convert the columns format into that specified by the u_format parameter. The record keeps track of its own format, and does not change the 'u_format' attribute of the column itself, which might have been set by IBISColumnSet.

In addition, the record "locks" each column, so that the column will not be deleted (using IBISColumnDelete). Even though a column is locked, it may still be added to other records, if desired. A column is unlocked only after all of the records which own it are closed.

If u_format ='none', the data will be returned as an unformatted array of bytes. Note that since numeric and ASCII formats cannot be converted into each other, the u_format ='none' method is the only way that both numeric and ASCII format columns may be placed in the same record.

If the group name is defaulted, the record will use the list of all columns currently existing in the file.

Example: one could define a record of all of the FULL format columns via:

IBISRecordOpen( ibis, &record, "format:FULL", 0, 0, IFMT_FULL ); IBISRecordClose

status = IBISRecordClose( record );
call ibis_record_close( record,status )

record		input	integer
status		output	integer

Flushes the record buffers to file, purges record. If the columns are not locked by other records, then the columns are unlocked. IBISRecordGet

count = IBISRecordGet( record, name, value, sval, nvals );
count = ibis_record_get( record, name, value, sval, nvals)

record		input	integer
name		input	string 
value		output	value
sval		input	integer
nvals		input	integer
count		output	integer


Get current attributes of record, and if multi-valued, return nvals values, starting with index sval. The number of values retrieved (which may be 0) is returned in count. If nvals=0, simply return the number of values in the attribute. The attributes of a record, including conditions under which they may be changed using IBISRecordSet, may be described as follows:

NR: Integer	
Count: 1	
Value: Number of Rows stored in record buffer.
Setable: Yes, if available memory is not exceeded.

NC: Integer	
Count: 1	
Value: Number of Columns in record.
Setable: No.

ROW: Integer	
Count: 1	
Value: Current position of file pointer.
Setable: Yes, if not past end-of-file.

COLUMNS: Integer Array	
Count: <NC>	
Value: List of all columns in record.
Setable: No.

U_FORMAT: keyword	
Count: 1	
Value={ NONE  |  {FORMAT_TYPE} } : format translation
Setable: Yes, except if current value='NONE', and there are mixed numeric and ASCII columns.

U_SIZE: Integer	
Count: 1	
Value:  Size of translated data, or 0 if U_FORMAT='NONE'.
Setable: No.

REC_SIZE: Integer	
Count: 1	
Value:  Size of a complete, assembled record using U_FORMAT. Not to
be confused with the 'RECSIZE' of the VICAR file.
Setable: No.

IUNIT: Integer	
Count: 1	
Value:  The IBIS file descriptor associated with the record.
Setable: No. IBISRecordSet

status = IBISRecordSet( record, name, value );
call ibis_record_set( record, name, value, status)

record		input	integer
name		input	string 
value		input	value
status		output	integer

name = { ROW | NR | U_FORMAT}

Set current attributes of record. The attributes are described in the above section on IBISRecordGet.

The 'row' value may be set in order to reposition the file pointer for subsequent read/write. If the row value is beyond the current scope of the record, this will cause modified buffer data to be flushed to the file.

The 'nr' value may be adjusted if a smaller/larger buffer is desired for the record. This may be the case if two buffers are being used to transfer data from one set of rows to another, and the buffers for each record are so large that they overlap.

The 'u_format' value may be set to change the format in which the columns are translated.

Note: if 'u_format' currently is 'none', and there are mixed numeric and ASCII columns in this record, any attempt to set the 'trans' type to a formatted translation will result in an error-status. IBISRecordRead

status = IBISRecordRead(record, buffer, row);
call ibis_record_read(record, buffer, row, status)

record		input	integer
buffer		output	value
row		input	integer
status		output	integer

Read the record at row into buffer, translating into the current 'u_format'. If row=0, use the current row. Following this operation, the 'row' value of the record is incremented by 1.

As mentioned previously, each element of a row is read using the current formatting settings. It should be pointed out here that ASCII columns cannot be mixed with numerical values in the same record unless the 'u_format' type is 'none'. That is, a formatted record may consist of various numerical formats, or various ASCII formats, but not both. This should not cause too much trouble, as one can always create a separate record for the ASCII columns.

If 'u_format' is set to 'none', then the data returned is an unformatted array of bytes, whose size is given by the 'rec_size' value of the record. The client program should make no assumptions about the internal structure of this data; the only meaningful thing that can be done with an untranslated record buffer is to pass it to another record of a file that uses the same 'REALFMT' and 'INTFMT', and whose columns are identical in format and number to the current record. Typically, this would be a second record using the same columns of the same file, having 'u_format' = 'none', but at a different row location. IBISRecordWrite

status = IBISRecordWrite(record,buffer,row);
call ibis_record_write(record,buffer,row,status)

record		input	integer
buffer		input	value
row		input	integer
status		output	integer

Write the record at row from buffer, translating from the current 'u_format'. If row=0, use the current row. Following this operation, the 'row' value of the record is incremented by 1.

You cannot write records past the current 'nr' value of the IBIS file. To append records past the end of the file, you must first allocate space for them by explicitly calling IBISFileSet to reset the 'nr' value. You may do this while records are open. In general this is much more efficient for row-oriented files than for column-oriented ones.

If 'u_format' is 'none', then the data passed in must be an unformatted array of 'rec_size' bytes. The only way useful data could be created is by an IBISRecordRead call from another record of a file that uses the same 'REALFMT' and 'INTFMT', and whose columns are identical in format and number to the current record. Typically, this would be a second record using the same columns of the same file, but at a different row location.

If two or more records are created which share some columns in common, they are known as "collaborators". This is because they need to communicate with each other regarding common areas of the file which they may both access. It is important to understand how collaborators will interact when their buffers cover overlapping areas of the file. This will be described here:

When IBISRecordWrite is first called, it does not communicate with its collaborators, but merely stores the new record data into an internal buffer until either the buffer is filled, the record is closed, the IBISRecordRead routine is called, or the 'row' pointer is reset outside the current buffer window. When any of these latter events occur, the record will then immediately flush its buffer to the file. In the process of flushing the buffer, the record will notify its collaborators of this fact, and if any of them have an overlapping buffer, they will be notified that their buffers need to be refreshed from the file before read again.

When IBISRecordRead is first called, it performs a refresh procedure to retrieve the column data from the file. In the refresh procedure the record notifies its collaborators to immediately flush their buffers to the file if their buffers overlap with the record; this will allow the changes made in other records to appear in the current one. However, from this point on the record will not refresh itself unless the current-row pointer has advanced (or was set) outset the bounds of the current buffer, or the record was notified to refresh itself by a collaborator.

In plain English, what this all means is that if you have two records open, say record_1 and record_2, both of which share columns, and they current cover some of the same rows, there is no guarantee that records written to one row of record_1 using IBISRecordWrite will immediately appear when that row is re-read by record_2 using IBISRecordRead.

It is always safe, however, to mix read/write calls within the same record.

If two open records both write to the same rows and columns of a file, the resulting values that appear in the file will be unpredictable, and so this practice is not recommended.

In general, the only safe way to use two records with overlapping columns, is to call IBISRecordRead for only one record, only call IBISRecordWrite for the other, and to do so in a way that guarantees that the "read-only" record reads a row before the "write-only" record writes to it. This is not a serious restriction, as the only imaginable reason for having multiple records sharing columns is to perform a transfer or conversion of row values, on a row-by-row basis.

If for some reason two records will both be writing to the same columns, but at different places in the file, the client program can guarantee that their buffers will not overlap by calling IBISRecordSet to adjust the 'NR' value of the record buffer. IBISRecordClear

status = IBISRecordClear (record,row,nrows);
call ibis_record_clear(record,row,nrows,status)

record		input	integer
row		input	integer
nrows		input	integer
status		output	integer

Clears nrows rows of the record, starting at row row (or the current row if 0); numeric values are set to 0, and strings are set to empty. The current-row pointer is advanced to just after the last cleared row.