Legacy Documentclose button

Important: The information in this document is obsolete and should not be used for new development.

Previous Book Contents Book Index Next

Inside Macintosh: Interapplication Communication /
Chapter 12 - Data Access Manager / Using the Data Access Manager


Processing Query Results

You can use the low-level function DBGetItem to retrieve a single data item returned by a query, or you can use the high-level function DBGetQueryResults to retrieve all of the query results at once. If you use the DBGetQueryResults function, you can then use the DBResultsToText function to convert the results to ASCII text. The DBResultsToText function calls routines called result handlers, which are installed in memory by applications or by system extensions (files containing 'INIT' resources). This section discusses the use of the DBGetItem and DBGetQueryResults functions and describes how to write and install a result handler.

Getting Query Results

The DBGetItem function retrieves a single data item that was returned by a data source in response to a query. When you call the DBGetItem function, you specify the data type to be retrieved. If you do not know what data type to expect, you can specify the typeAnyType constant for the dataType parameter, and the data server returns the next data item regardless of data type. It also returns information about the data item, including data type and length.

If you do not know the length of the next data item, you can specify NIL for the buffer parameter in the DBGetItem function, and the data server returns the data type, length, and number of decimal places without retrieving the data item. The next time you call the DBGetItem function with a nonzero value for the buffer parameter, the function retrieves the data item.

If you want to skip a data item, specify the typeDiscard constant for the dataType parameter. Then the next time you call the DBGetItem function, it retrieves the following data item.

You should use the DBGetItem function if you want complete control over the retrieval of each item of data. If you want the Data Access Manager to retrieve the data for you, use the DBGetQueryResults function instead.

Table 12-1 shows the data types recognized by the Data Access Manager. You use a constant to specify each data type, as follows:

CONST {data types}
      typeAnyType    =  0;       {can be any data type} 
      typeNone       = 'none';   {no more data expected}
      typeBoolean    = 'bool';   {Boolean}
      typeSMInt      = 'shor';   {short integer}
      typeInteger    = 'long';   {integer}
      typeSMFloat    = 'sing';   {short floating point}
      typeFloat      = 'doub';   {floating point}
      typeDate       = 'date';   {date}
      typeTime       = 'time';   {time}
      typeTimeStamp  = 'tims';   {date and time}
      typeChar       = 'TEXT';   {character}
      typeDecimal    = 'deci';   {decimal number}
      typeMoney      = 'mone';   {money value}
      typeVChar      = 'vcha';   {variable character}
      typeVBin       = 'vbin';   {variable binary}
      typeLChar      = 'lcha';   {long character}
      typeLBin       = 'lbin';   {long binary}
      typeDiscard    = 'disc';   {discard next data item}
      typeUnknown    = 'unkn';   {result handler for unknown }
                                 { data type}
      typeColBreak   = 'colb';   {result handler for column break}
      typeRowBreak   = 'rowb';   {result handler for end of line}
The writer of a database extension can define other data types to support specific data sources or data servers.

Each data type has a standard definition, shown in Table 12-1. For example, if the DBGetItem function returns the typeInteger constant for the dataType parameter, you know that the data item represents an integer value and that a 4-byte buffer is necessary to hold it. Similarly, if you are using the DBSendItem function to send to the data server a data item that you identify as typeFloat, the data server expects to receive an 8-byte floating-point value.

Notice that some of these data types are defined to have a specific length (referred to as an implied length), and some do not. The len parameter of the DBSendItem and DBGetItem functions indicates the length of an individual data item. The DBGetQueryResults function returns a handle to an array of lengths, decimal places, and flags in the colInfo field of the results record. The typeAnyType, typeColBreak, and typeRowBreak constants do not refer to specific data types, and therefore the length specification is not applicable for these constants.

Table 12-1 Data types defined by the Data Access Manager
ConstantLengthDefinition
typeAnyTypeNAAny data type (used as an input parameter to the DBGetItem function only; never returned by the function).
typeNone0Empty.
typeBoolean1 byteTRUE (1) or FALSE (0).
typeSMInt2 bytesSigned integer value.
typeInteger4 bytesSigned long integer value.
typeSMFloat4 bytesSigned floating-point value.
typeFloat8 bytesSigned floating-point value.
typeDate4 bytesDate; a long integer value consisting of a year (most significant 16 bits), month (8 bits), and day (least significant 8 bits).
typeTime4 bytesTime; a long integer value consisting of an hour (0-23; most significant 8 bits), minute (8 bits), second (8 bits), and hundredths of a second (least significant 8 bits).
typeTimeStamp8 bytesDate and time. A long integer date value followed by a long integer time value.
typeCharAnyFixed-length character string, not NULL terminated. The length of the string is defined by the specific data source.
typeDecimalAnyPacked decimal string. A contiguous string of 4-bit nibbles, each of which contains a decimal number, except for the low nibble of the highest-addressed byte (that is, the last nibble in the string), which contains a sign. The value of the sign nibble can be 10, 12, 14, or 15 for a positive number or 11 or 13 for a negative number; 12 is recommended for a positive number and 13 is recommended for a negative number. The most significant digit is the high-order nibble of the lowest-addressed byte (that is, the first nibble to appear in the string).

The total number of nibbles (including the sign nibble) must be even; therefore, the high nibble of the highest-addressed byte of a number with an even number of digits must be 0.

For example, the number +123 is represented as $123C.

graphics/DA-L-13.jpg

The number -1234 is represented as $01234D.

graphics/DA-L-14.jpg

The length of a packed decimal string is defined as the number of bytes, including any extra leading 0 and the sign nibble. A packed decimal string can have from 0 to 31 digits, not including the sign nibble.

In addition to the length of a packed decimal string, each data item has an associated value that indicates the number of digits that follow the decimal place. The places parameter in the DBGetItem and DBSendItem functions indicates the number of decimal places in an individual data item. The DBGetQueryResults function returns the number of decimal places.

typeMoneyAnySame as typeDecimal, but always has two decimal places.
typeVCharAnyVariable-length character string, NULL terminated.
   
typeVBinAnyNot defined. Reserved for future use.
typeLCharAnyNot defined. Reserved for future use.
typeLBinAnyNot defined. Reserved for future use.
typeDiscardNADo not retrieve the next data item (used as an input parameter to the DBGetItem function only; never returned by the function).
typeUnknownNAA dummy data type for the result handler that processes any data type for which no other result handler is available (used as an input parameter to the DBInstallResultHandler, DBRemoveResultHandler, and DBGetResultHandler functions only; never returned by the DBGetItem function).
typeColBreakNAA dummy data type for the result handler that the DBGetQueryResults function calls after each item that is not the last item in a row (used as an input parameter to the DBInstallResultHandler, DBRemoveResultHandler, and DBGetResultHandler functions only; never returned by the DBGetItem function).
typeRowBreakNAA dummy data type for the result handler that the DBGetQueryResults function calls at the end of each row (used as an input parameter to the DBInstallResultHandler, DBRemoveResultHandler, and DBGetResultHandler functions only; never returned by the DBGetItem function).

The DBGetQueryResults function retrieves all of the data that was returned by a data source in response to a query, unless insufficient memory is available to hold the data, in which case it retrieves as many complete rows of data as possible. The DBGetQueryResults function stores the data in a structure called a results record. You must allocate the results record data structure and pass this record to the DBGetQueryResults function. The Data Access Manager allocates the handles inside the results record. When your application is finished using the results record, you must deallocate both the results record and the handles inside the results record.

The results record is defined by the ResultsRecord data type.

TYPE ResultsRecord = 
   RECORD
      numRows:    Integer;          {number of rows retrieved}
      numCols:    Integer;          {number of columns per row}
      colTypes:   ColTypesHandle;   {type of data in each column}
      colData:    Handle;           {array of data items}
      colInfo:    ColInfoHandle;    {info about each data item}
   END;
The numRows field in the results record indicates the total number of rows retrieved. If the DBGetQueryResults function returns a result code other than rcDBValue, then not all of the data actually returned by the data source was retrieved. This could happen, for instance, if the user's computer does not have sufficient memory space to hold all the data. In this case, your application can make more space available (by writing the data in the data record to disk, for example) and then call the DBGetQueryResults function again to complete retrieval of the data.

Note
The DBGetQueryResults function retrieves whole rows only; if it runs out of space in the middle of a row, it stores the partial row in a private buffer so that the data in the results record ends with the last complete row. Because the last partial row is no longer available from the data server, you cannot start to retrieve data with the DBGetQueryResults function and then switch to the DBGetItem function to complete the data retrieval.
The numCols field indicates the number of columns in each row of data.

The colTypes field is a handle to an array of data types, specifying the type of data in each column. The number of elements in the array is equal to the value in the numCols field. Table 12-1 beginning on page 12-39 shows the standard data types.

The colData field is a handle to the data retrieved by the DBGetQueryResults function.

The colInfo field is a handle to an array of records of type DBColInfoRecord, each of which specifies the length, places, and flags for a data item. There are as many records in the array as there are data items retrieved by the DBGetQueryResults function. Here is the DBColInfoRecord type definition:

TYPE DBColInfoRecord = 
   RECORD
      len:        Integer;       {length of data item}
      places:     Integer;       {places for decimal and }
                                 { money data items}
      flags:      Integer;       {flags for data item}
   END;
The len field indicates the length of the data item. The DBGetQueryResults function returns a value in this field only for those data types that do not have implied lengths; see Table 12-1 beginning on page 12-39.

The places field indicates the number of decimal places in data items of types typeMoney and typeDecimal. For all other data types, the places field returns 0.

The least significant bit of the flags field is set to 1 if the data item is in the last column of the row. The third bit of the flags field is 1 if the data item is NULL. You can use the constants kDBLastColFlag and kDBNullFlag to test for these flag bits.

Converting Query Results to Text

The DBResultsToText function provided by the high-level interface converts the data retrieved by the DBGetQueryResults function into strings of ASCII text. This function makes it easier for you to display retrieved data for the user.

For the DBResultsToText function to convert data of a specific type to text, either the application or the system software must have a routine called a result handler. With System 7, Apple Computer, Inc., provides system result handlers for the data types listed here. (These data types are described in Table 12-1 beginning on page 12-39.)
Data typeConstantData typeConstant
BooleantypeBooleanTimetypeTime
Short integertypeSMIntDate and timetypeTimeStamp
IntegertypeIntegerCharactertypeChar
Short floating pointtypeSMFloatDecimal numbertypeDecimal
Floating pointtypeFloatMoney valuetypeMoney
DatetypeDateVariable charactertypeVChar

Note
Apple's system result handler for the variable character (typeVChar) data type strips trailing spaces from the character string.
In addition to the result handlers for these standard data types, Apple provides the following three system result handlers, which correspond to no specific data type:
Data typeConstant
Unknown typeUnknown
Column breaktypeColBreak
End of linetypeRowBreak

The typeUnknown result handler processes any data type for which no other result handler is available. The DBResultsToText function calls the typeColBreak result handler after each item that is not the last item in a row. This result handler does not correspond to any data type, but adds a delimiter character to separate columns of text. The default typeColBreak result handler inserts a tab character. Similarly, the DBResultsToText function calls the typeRowBreak result handler at the end of each row of data to add a character that separates the rows of text. The default typeRowBreak result handler inserts a return character. Your application can install your own typeColBreak and typeRowBreak result handlers to insert whatever characters you wish--or to insert no character at all, if you prefer.

You can install result handlers for any data types you know about. When you call the DBInstallResultHandler function, you can specify whether the result handler you are installing is a system result handler. A system result handler is available to all applications that use the system. All other result handlers (called application result handlers) are associated with a particular application. The DBResultsToText function always uses a result handler for the current application in preference to a system result handler for the same data type. When you install a system result handler for the same data type as an already installed system result handler, the new result handler replaces the old one. Similarly, when you install an application result handler for the same data type as a result handler already installed for the same application, the new result handler replaces the old one for that application.

Result handlers are stored in memory. The Data Access Manager installs its system result handlers the first time the Macintosh Operating System loads the Data Access Manager into memory. You must reinstall your own application result handlers each time your application starts up. You can also install your own system result handlers each time your application starts up, or you can provide a system extension (that is, a file with an 'INIT' resource) that installs system result handlers each time the user starts up the system.

Here is a function declaration for a result handler function:

FUNCTION MyResultHandler (dataType: DBType; 
                          theLen, thePlaces, theFlags: Integer; 
                          theData: Ptr; theText: Handle): OSErr;
The dataType parameter specifies the data type of the data item that the DBResultsToText function is passing to the result handler. Table 12-1 beginning on page 12-39 describes the standard data types.

The parameters theLen and thePlaces specify the length and number of decimal places of the data item that the DBResultsToText function wants the result handler to convert to text.

The parameter theFlags is the value returned for the flags parameter by the DBGetItem function. If the least significant bit of this parameter is set to 1, the data item is in the last column of the row. If the third bit of this parameter is set to 1, the data item is NULL. You can use the constants kDBLastColFlag and kDBNullFlag to test for these flag bits.

The parameter theData is a pointer to the data that the result handler is to convert to text.

The parameter theText is a handle to the buffer that is to hold the text version of the data. The result handler should use the Memory Manager's SetHandleSize function to increase the size of the buffer as necessary to hold the new text, and append the new text to the end of the text already in the buffer. The SetHandleSize function is described in the chapter "Memory Manager" in Inside Macintosh: Memory.

If the result handler successfully converts the data to text, it should return a result code of 0 (noErr).

You can use the DBInstallResultHandler function to install a result handler and the DBRemoveResultHandler function to remove an application result handler. You can install and replace system result handlers, but you cannot remove them.

The following line of code installs an application result handler. The first parameter (typeInteger) specifies the data type that this result handler processes. The second parameter (MyTypeIntegerHandler) is a pointer to the result handler routine. The last parameter (FALSE) is a Boolean value specifying that this routine is not a system result handler.

err := DBInstallResultHandler
                      (typeInteger,@MyTypeIntegerHandler,FALSE);
Listing 12-6 shows a result handler that converts the integer data type to text.

Listing 12-6 A result handler

FUNCTION MyTypeIntegerHandler(datatype: DBType; theLen: Integer;
                              theData: Ptr; 
                              theText: Handle): OSErr;
VAR   
   theInt:        LongInt;
   theTextLen:    LongInt;
   temp:          Str255;
   atemp1:        Ptr;
   atemp2:        LongInt;
   atemp3:        LongInt;
BEGIN
   BlockMove(theData, @theInt, sizeof(theInt));
   NumToString(theInt, temp);                {convert to text}
   theTextLen := GetHandleSize(theText);     {get current size }
                                             { of theText}
                                             {size text handle}
   SetHandleSize(theText, theTextLen + LongInt(LENGTH(temp)));
   IF (MemError <> noErr) THEN 
      MyTypeIntegerHandler := MemError
   ELSE
   BEGIN
      atemp1 := Ptr(ORD(@temp));
      atemp2 := LongInt(theText^) + theTextLen;
      atemp3 := LongInt(LENGTH(temp));
      {use BlockMove to append text}
      BlockMove(P2CStr(atemp1), Ptr(atemp2), atemp3);
      MyTypeIntegerHandler := MemError;
   END;
END;

Previous Book Contents Book Index Next

© Apple Computer, Inc.
7 JUL 1996