Column-wise binding

 

When using column-wise binding, an application binds one, two, or in some cases three, vectors to each column for which data is to be returned. The first vector holds the data values and the second vector holds length/indicator buffers. 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, a third vector is bound. Each array contains as many elements as there are rows in the rowset.

 

The application declares that it is using column-wise binding with the SQL_ATTR_ROW_BIND_TYPE statement attribute, which determines the bind type for rowset buffers, as opposed to parameter set buffers. The driver returns the data for each row in successive entries of each vector. Figure 5.3 shows how column-wise binding works.

 

Figure 5.3. Column-wise binding

 

Example 5.5 shows how to perform a column-wise binding. We query the table Titles in the BookSales database to retrieve book title, year it was published, price, and the timestamp of the record (i.e. the date the record was entered in the database). Note that for this last item we use a special structure called TIMESTAMP_STRUCT, which is defined in module f90SQLStructures.

 

In the example, we define four vectors that contain the data (Title, Year, Price and EntryDate); four vectors that contain the length/indicator values (TitleLen, YearInd, PriceInd and EntryDateInd); a vector that contains a fetching status for each row in the rowset (RowStatusArray), and an integer variable that contains the number of rows in the rowset after each fetch statement (NumRowsFetched). This last variable is necessary because in some cases, although the rowset size is set to a given value, the real number of rows returned in the rowset can be smaller. One example is if we reached the end of a resultset. The size of these vectors is the size of the rowset  we want to use (set to 10 in parameter RowsetSize). Note that after allocating a statement handle, we call subroutine f90SQLSetStmtAttr to specify:

 

 

Also note that when calling f90SQLBindCol, we do not pass the name of the vectors that will contain the data. Instead we pass their address (using the Fortran function loc()).

 

Example 5.5

program Example55
 
!Demonstrates:
!Column-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
 
!data vectors
character(len=MaxStringLen)::Title(RowsetSize)
integer(SQLSMALLINT_KIND)::Year(RowsetSize)
real(SQLREAL_KIND)::Price(RowsetSize)
type(TIMESTAMP_STRUCT)::EntryDate(RowsetSize)
!len/indicator vectors
integer(SQLINTEGER_KIND)::TitleLen(RowsetSize), YearInd(RowsetSize),
integer(SQLINTEGER_KIND)::PriceInd(RowsetSize), EntryDateInd(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 column-wise binding
call f90SQLSetStmtAttr(StmtHndl, SQL_ATTR_ROW_BIND_TYPE, SQL_BIND_BY_COLUMN, 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
ColNumber=1
call f90SQLBindCol(StmtHndl,ColNumber,SQL_F_CHAR,loc(Title), MaxStringLen,loc(TitleLen), iRet)
ColNumber=ColNumber+1
call f90SQLBindCol(StmtHndl,ColNumber, SQL_F_SHORT, loc(Year), 0, loc(YearInd), iRet)
ColNumber=ColNumber+1
call f90SQLBindCol(StmtHndl, ColNumber,SQL_F_FLOAT,loc(Price), 0, loc(PriceInd), iRet)
ColNumber=ColNumber+1
call f90SQLBindCol(StmtHndl, ColNumber, SQL_F_TYPE_TIMESTAMP, loc(EntryDate), 0, loc(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 *,Title(i),',',Year(i),',',Price(i),',', EntryDate(i)%Year,EntryDate(i)%Month, EntryDate(i)%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