Row-wise binding

 

When using row-wise binding, an application defines a structure containing one, two, or in some cases three, elements for each column in which data is to be returned. The first element holds the data value and the second element holds the length/indicator buffer. Indicators and length values can be stored in separate buffers by setting the SQL_DESC_INDICATOR_PTR and SQL_DESC_OCTET_LENGTH_PTR descriptor fields to different values. If this is done, the structure contains a third element. The application then allocates an array of these structures, which contains as many elements as there are rows in the rowset.

 

The application declares the size of the structure to the driver with the SQL_ATTR_ROW_BIND_TYPE statement attribute. It then binds the address of each member in the first element of the array. Thus, the driver can calculate the address of the data for a particular row and column as:

 

Address = Bound Address + ((Row Number) * Structure Size)

 

where rows are numbered from 1 to the size of the rowset. Figure 5.4 shows how row-wise binding works. Generally, only columns that will be bound are included in the structure. The structure can contain fields that are unrelated to result set columns. The columns can be placed in the structure in any order, but are shown in sequential order for clarity.

 

Figure 5.4. Row-wise binding. Each row of the vector contains a structure. The structure has fields for the data (in blue) and fields for the length/indicator buffer (in green).

 

Example 5.6 is a modified version of Example 5.5. This example uses row-wise binding instead of column-wise binding. Both examples are very similar. The main differences are readily marked in the program. Note that to indicate row-wise binding, we set the attribute SQL_ATTR_ROW_BIND_TYPE to the size of the row structure (in this case to the size of the structure defined as TitlesStruct). The Fortran standard does not include a function to compute the size in bytes of a structure. However, most implementations offer a non-standard function to do so. In many compilers, the non-standard function, sizeof() returns the number of bytes used by a structure. The name of this function may be different in your compiler. Another important point to keep in mind when using row-wise binding from Fortran is that Fortran compilers do not necessarily store the fields in a structure in the same sequence they have been declared. Although having the variables in sequence is not a requirement for row-wise binding, in the example we use the time-stamp structure (TIMESTAMP_STRUCT) which is declared in sequence (this is necessary to ensure that time-stamp data are put in the right places by the driver). This forces us to declare TitlesStruct as a sequence structure (the standard in Fortran indicates that structures containing sequence sub-structures must also be declared with the sequence attribute).

 

Example 5.6

program Example56
 
!Demonstrates:
!Row-wise binding
 
!load f90SQL modules
use f90SQLConstants
use f90SQLStructures
use f90SQL
 
implicit none
integer(SQLUINTEGER_KIND),parameter::RowsetSize=10
integer(SQLINTEGER_KIND),parameter:: MaxStringLen=25
integer(SQLHENV_KIND):: EnvHndl
integer(SQLHDBC_KIND):: ConnHndl
integer(SQLHSTMT_KIND):: StmtHndl
integer(SQLRETURN_KIND)::iRet
integer(SQLSMALLINT_KIND)::ColNumber
integer i
 
!Structure to retrieve the resultset data
type TitlesStruct
SEQUENCE
character(len=MaxStringLen)::Title
integer(SQLINTEGER_KIND)::TitleLen
integer(SQLSMALLINT_KIND)::Year
integer(SQLINTEGER_KIND)::YearInd
real(SQLREAL_KIND)::Price
integer(SQLINTEGER_KIND)::PriceInd
type(TIMESTAMP_STRUCT)::EntryDate
integer(SQLINTEGER_KIND)::EntryDateInd
end type TitlesStruct
 
type(TitlesStruct)::Titles(RowsetSize)
 
!variables to keep status of rowset
integer(SQLUSMALLINT_KIND)::RowStatusArray(RowsetSize)
integer(SQLUINTEGER_KIND)::NumRowsFetched
 
!allocate an environment handle
call f90SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, EnvHndl, iRet)
 
!Set ODBC version we will be using (3.x in this case)
call f90SQLSetEnvAttr(EnvHndl, SQL_ATTR_ODBC_VERSION, SQL_OV_ODBC3, iRet)
 
!Allocate a connection handle
call f90SQLAllocHandle(SQL_HANDLE_DBC,EnvHndl, ConnHndl, iRet)
 
!connect to BookSales database
call f90SQLConnect(ConnHndl,'BookSales','Admin','',iRet)
 
if (iRet.eq.SQL_SUCCESS .or. iRet.eq. SQL_SUCCESS_WITH_INFO) then
 
!Allocate statement handle
call f90SQLAllocHandle(SQL_HANDLE_STMT,ConnHndl, StmtHndl, iRet)
 
!set statement attributes indicating row-wise binding
!note that sizeof() function is not standard fortran. Your compiler
!may have a different function to obtain the size (in bytes)
!of a derived type variable.
call f90SQLSetStmtAttr(StmtHndl, SQL_ATTR_ROW_BIND_TYPE, sizeof(Titles(1)), iRet)
call f90SQLSetStmtAttr(StmtHndl,SQL_ATTR_ROW_ARRAY_SIZE, RowsetSize, iRet)
call f90SQLSetStmtAttr(StmtHndl, SQL_ATTR_ROW_STATUS_PTR, loc(RowStatusArray), iRet)
call f90SQLSetStmtAttr(StmtHndl,SQL_ATTR_ROWS_FETCHED_PTR, loc(NumRowsFetched), iRet)
 
!bind columns of result set
!note that we only bind first entry of the array
ColNumber=1
call f90SQLBindCol(StmtHndl,ColNumber,SQL_F_CHAR, loc(Titles(1)%Title), MaxStringLen,loc(Titles(1)%TitleLen), iRet)
ColNumber=ColNumber+1
call f90SQLBindCol(StmtHndl,ColNumber, SQL_F_SHORT, loc(Titles(1)%Year), 0, loc(Titles(1)%YearInd), iRet)
ColNumber=ColNumber+1
call f90SQLBindCol(StmtHndl, ColNumber, SQL_F_FLOAT, loc(Titles(1)%Price), 0, loc(Titles(1)%PriceInd), iRet)
ColNumber=ColNumber+1
call f90SQLBindCol(StmtHndl, ColNumber, SQL_F_TYPE_TIMESTAMP, loc(Titles(1)%EntryDate), 0, loc(Titles(1)%EntryDateInd), iRet)
 
call f90SQLExecDirect(StmtHndl,'SELECT Title, [Year Published], '// 'Price, [Entry Date] FROM Titles', iRet)
print *,'Book title (just the first 25 chars), Year published, '// 'Price, Data Entry Timestamp (year, month, day)'
 
do while (.true.)
!Fetch and print result set
call f90SQLFetchScroll(StmtHndl,SQL_FETCH_NEXT, int(0,SQLINTEGER_KIND),iRet)
if (iRet.eq.SQL_NO_DATA) exit
do i=1,NumRowsFetched
if (RowStatusArray(i).eq.SQL_ROW_SUCCESS .or.
RowStatusArray(i).eq.SQL_ROW_SUCCESS_WITH_INFO) then
!At this point application should check the values
!of the len/indicators to see if any of the columns
!contain null data. Omitted here for brevity.
print *,Titles(i)%Title,',',Titles(i)%Year,',', Titles(i)%Price,',', Titles(i)%EntryDate%Year,Titles(i)%EntryDate%Month,Titles(i)%EntryDate%Day
endif
enddo
 
enddo
 
!Release statement handle
call f90SQLFreeHandle(SQL_HANDLE_STMT, StmtHndl, iRet)
 
endif
 
!disconnect
call f90SQLDisconnect(ConnHndl, iRet)
!release connection handles
call f90SQLFreeHandle(SQL_HANDLE_DBC, ConnHndl, iRet)
!release environment handle
call f90SQLFreeHandle(SQL_HANDLE_ENV, EnvHndl, iRet)
 
stop
end