home *** CD-ROM | disk | FTP | other *** search
- // Compute.cpp -- Use ODBC to execute a Transact-SQL SELECT statement
- // containing a COMPUTE clause.
- //
- // Illustrates fast retrieve on a default ODBC statement handle and
- // determining the properties of a result set using SQL Server ODBC
- // driver-specific column attributes.
- //
- // This file is part of Microsoft SQL Server online documentation.
- // Copyright (C) 1992-1997 Microsoft Corporation. All rights reserved.
- //
- // This source code is an intended supplement to the Microsoft SQL
- // Server online references and related electronic documentation.
- #include <windows.h>
- #include <stdio.h>
- #include <tchar.h>
-
- #include "sql.h"
- #include "sqlext.h"
- #include "odbcss.h"
-
- // Macros
- #define NUMROWS_CHUNK 20
- #define CBCOLNAME_MAX 64
-
- // Types
- typedef struct tagDRIVERBYLIST
- {
- SQLSMALLINT nBys;
- SQLSMALLINT aByList[1];
- } DRIVERBYLIST;
- typedef DRIVERBYLIST* PDRIVERBYLIST;
-
- typedef struct tagLOCALBYLIST
- {
- SQLSMALLINT nBys;
- SQLSMALLINT* pBys;
- } LOCALBYLIST;
- typedef LOCALBYLIST* PLOCALBYLIST;
-
- typedef struct tagODBCCOLINFO
- {
- SQLTCHAR szColName[CBCOLNAME_MAX + 1];
- SQLSMALLINT cbColName;
- SQLSMALLINT fSQLType;
- SQLUINTEGER cbColData;
- SQLSMALLINT cbScale;
- SQLSMALLINT fNullable;
- SQLSMALLINT fBindType;
- UINT obValue;
- UINT obIndicator;
- } ODBCCOLINFO;
- typedef ODBCCOLINFO* PODBCCOLINFO;
-
- typedef struct tagODBCSETINFO
- {
- SQLUSMALLINT nCols;
- SQLINTEGER nRows;
- SQLINTEGER cbResultSet;
- PODBCCOLINFO pODBCColInfo;
- PBYTE pRows;
- PLOCALBYLIST pByList;
- } ODBCSETINFO;
- typedef ODBCSETINFO* PODBCSETINFO;
-
- // Function prototypes
- SQLRETURN GetColumnsInfo(SQLHSTMT hstmt, SWORD nCols, ODBCCOLINFO** ppODBCColInfo);
- SQLRETURN BindCols(SQLHSTMT hstmt, SQLUSMALLINT nCols, PODBCCOLINFO pODBCColInfo,
- PBYTE pRows);
- void CreateDBBindings(PODBCSETINFO pODBCSetInfo);
- void GetData(SQLHSTMT hstmt);
-
- void DumpError(PTSTR pErrorText);
- void DumpError(SQLSMALLINT eHandleType, SQLHANDLE hodbc);
-
- // AdjustLen supports binding on four-byte boundaries.
- _inline SQLUINTEGER AdjustLen(SQLUINTEGER cb)
- {
- return ((cb + 3) & ~3);
- }
-
- int main()
- {
- // ODBC handles
- SQLHENV henv = NULL;
- SQLHDBC hdbc = NULL;
- SQLHSTMT hstmt = NULL;
-
- PTSTR szDataSource = _T("MyDatasource");
- PTSTR szUID = _T("MyUID");
- PTSTR szPWD = _T("MyPWD");
-
- PTSTR szSQLSelect =
- _T("SELECT")
- _T(" O.EmployeeID, O.OrderID,")
- _T(" FullPrice = (UnitPrice * Quantity), Discount,")
- _T(" Discounted = UnitPrice * (1 - Discount) * Quantity")
- _T(" FROM Orders O, [Order Details] OD")
- _T(" WHERE O.OrderID = OD.OrderID")
- _T(" ORDER BY EmployeeID, O.OrderID")
- _T(" COMPUTE")
- _T(" SUM(UnitPrice * Quantity),")
- _T(" SUM(UnitPrice * (1 - Discount) * Quantity)")
- _T(" BY EmployeeID, O.OrderID")
- _T(" COMPUTE")
- _T(" SUM(UnitPrice * Quantity),")
- _T(" SUM(UnitPrice * (1 - Discount) * Quantity)")
- _T(" BY EmployeeID")
- _T(" COMPUTE")
- _T(" SUM(UnitPrice * Quantity),")
- _T(" SUM(UnitPrice * (1 - Discount) * Quantity)");
-
- // Initialize the ODBC environment.
- if (SQLAllocHandle(SQL_HANDLE_ENV, NULL, &henv) == SQL_ERROR)
- goto EXIT;
- SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (void*) SQL_OV_ODBC3,
- SQL_IS_INTEGER);
-
- // Allocate a connection handle and connect to the data source.
- if (SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc) == SQL_ERROR)
- {
- DumpError(_T("AllocHandle on DBC failed."));
- goto EXIT;
- }
- if (SQLConnect(hdbc, (SQLTCHAR*) szDataSource, SQL_NTS,
- (SQLTCHAR*) szUID, SQL_NTS, (SQLTCHAR*) szPWD, SQL_NTS) == SQL_ERROR)
- {
- DumpError(SQL_HANDLE_DBC, hdbc);
- goto EXIT;
- }
-
- // Get a statement handle and execute a Transact-SQL SELECT statement
- // containing a COMPUTE clause.
- if (SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt) == SQL_ERROR)
- {
- DumpError(SQL_HANDLE_DBC, hdbc);
- goto EXIT;
- }
- if (SQLExecDirect(hstmt, (SQLTCHAR*) szSQLSelect, SQL_NTS) == SQL_ERROR)
- {
- DumpError(SQL_HANDLE_STMT, hstmt);
- goto EXIT;
- }
-
- // Retrieve data from multiple result sets.
- GetData(hstmt);
-
- EXIT:
- if (hstmt != NULL)
- {
- SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
- }
-
- if (hdbc != NULL)
- {
- SQLDisconnect(hdbc);
- SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
- }
-
- if (henv != NULL)
- {
- SQLFreeHandle(SQL_HANDLE_ENV, henv);
- }
-
- return (0);
- }
-
- // GetData(...) -- retrieve data for all result sets.
- void GetData
- (
- SQLHSTMT hstmt
- )
- {
- SQLUSMALLINT nCols;
- SQLUSMALLINT nCol;
- PODBCSETINFO pODBCSetInfo = NULL;
- SQLRETURN sRet;
- SQLINTEGER nRowsFetched = 0;
- UINT nRow;
- SQLINTEGER nComputes = 0;
- SQLINTEGER nSets = 1;
- SQLINTEGER nSet;
- BYTE* pValue;
- SQLINTEGER* pIndicator;
-
- // If SQLNumResultCols failed, then some error occured in statement
- // execution. Exit.
- if (!SQL_SUCCEEDED(sRet = SQLNumResultCols(hstmt, (SQLSMALLINT*) &nCols)))
- {
- DumpError(SQL_HANDLE_STMT, hstmt);
- goto EXIT;
- }
-
- // If SQLNumResultCols succeeded, but no columns are indicated, then
- // the statement did not return a results set.
- if (nCols == 0)
- {
- DumpError(_T("Invalid statement."));
- goto EXIT;
- }
-
- // Determine the presence of COMPUTE clause result sets. The SQL Server
- // driver uses column attributes to report multiple sets.
- SQLColAttribute(hstmt, 1, SQL_CA_SS_NUM_COMPUTES,
- NULL, 0, NULL, (SQLPOINTER) &nComputes);
-
- // The number of result sets is 1 (for the normal rows) + nComputes.
- nSets += nComputes;
-
- // Create a column info structure pointer array, one element for each
- // result set.
- pODBCSetInfo = new ODBCSETINFO[1 + nComputes];
- for (nSet = 0; nSet < 1 + nComputes; nSet++)
- {
- pODBCSetInfo[nSet].nCols = 0;
- pODBCSetInfo[nSet].nRows = 1;
- pODBCSetInfo[nSet].cbResultSet = 0;
- pODBCSetInfo[nSet].pODBCColInfo = NULL;
- pODBCSetInfo[nSet].pRows = NULL;
- pODBCSetInfo[nSet].pByList = NULL;
- }
-
- // Set up info structure for normal result set. The normal result set
- // can contain multiple rows. All COMPUTE clause sets will have only
- // a single row. We can optimize retrieval of rows for the SQL Server
- // driver by using row array binding for the normal set (0).
- pODBCSetInfo[0].nCols = nCols;
- pODBCSetInfo[0].nRows = NUMROWS_CHUNK;
- nSet = 0;
-
- while (TRUE)
- {
- // If required, get the column information for the result set.
- if (pODBCSetInfo[nSet].pODBCColInfo == NULL)
- {
- if (pODBCSetInfo[nSet].nCols == 0)
- {
- SQLNumResultCols(hstmt, (SQLSMALLINT*) &nCols);
- pODBCSetInfo[nSet].nCols = nCols;
- }
-
- if (GetColumnsInfo(hstmt, pODBCSetInfo[nSet].nCols,
- &(pODBCSetInfo[nSet].pODBCColInfo)) == SQL_ERROR)
- {
- goto EXIT;
- }
- }
-
- // If this is a COMPUTE clause result sets, get the ordering columns
- // (if any) for the set and display them.
- if (nSet > 0)
- {
- SQLSMALLINT nBy;
- SQLSMALLINT nBys;
- PLOCALBYLIST pLocalByList;
-
- if (pODBCSetInfo[nSet].pByList == NULL)
- {
- PDRIVERBYLIST pDriverByList;
- pLocalByList = new LOCALBYLIST;
-
- SQLColAttribute(hstmt, 1, SQL_CA_SS_COMPUTE_BYLIST, NULL,
- 0, NULL, (SQLPOINTER) &pDriverByList);
-
- if (pDriverByList)
- {
- pLocalByList->nBys = pDriverByList->nBys;
- nBys = pLocalByList->nBys;
- pLocalByList->pBys = new SQLSMALLINT[nBys];
- for (nBy = 0; nBy < nBys; nBy++)
- {
- pLocalByList->pBys[nBy] =
- pDriverByList->aByList[nBy];
- }
- }
- else
- {
- nBys = pLocalByList->nBys = 0;
- pLocalByList->pBys = NULL;
- }
-
- pODBCSetInfo[nSet].pByList = pLocalByList;
- }
- else
- {
- pLocalByList = pODBCSetInfo[nSet].pByList;
- nBys = pLocalByList->nBys;
- }
-
- for (nBy = 0; nBy < nBys; nBy++)
- {
- _tprintf(_T("This compute clause ordered by columns: "));
- for (nBy = 0; nBy < pLocalByList->nBys; )
- {
- _tprintf(_T("%u"), pLocalByList->pBys[nBy]);
- nBy++;
-
- if (nBy == pLocalByList->nBys)
- {
- _tprintf(_T("\n"));
- }
- else
- {
- _tprintf(_T(", "));
- }
- }
- }
- }
-
-
- // Create a string for bound return values if required.
- if (pODBCSetInfo[nSet].pRows == NULL)
- {
- CreateDBBindings(&(pODBCSetInfo[nSet]));
- }
-
- // Binding must be done on each result set.
- BindCols(hstmt, pODBCSetInfo[nSet].nCols,
- pODBCSetInfo[nSet].pODBCColInfo, pODBCSetInfo[nSet].pRows);
-
- // Set for ODBC row array retrieval. Fast retrieve for all sets.
- SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_BIND_TYPE,
- (void*) pODBCSetInfo[nSet].cbResultSet, SQL_IS_UINTEGER);
- SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE,
- (void*) pODBCSetInfo[nSet].nRows, SQL_IS_UINTEGER);
- SQLSetStmtAttr(hstmt, SQL_ATTR_ROWS_FETCHED_PTR, (void*) &nRowsFetched,
- sizeof(SQLINTEGER));
-
- while (TRUE)
- {
- // In ODBC 3.x, SQLFetch supports arrays of bound rows or columns.
- // SQLFetchScroll (or ODBC 2.x SQLExtendedFetch) is not necessary
- // to support fastest retrieval of our data rows.
- sRet = SQLFetch(hstmt);
- if (sRet == SQL_ERROR)
- {
- DumpError(SQL_HANDLE_STMT, hstmt);
- goto EXIT;
- }
- else if (sRet == SQL_NO_DATA)
- {
- break;
- }
- else if (sRet == SQL_SUCCESS_WITH_INFO)
- {
- DumpError(SQL_HANDLE_STMT, hstmt);
- }
-
- // Display the data. On each column, check the indicator and
- // print the string "<null>" if no data available.
- for (nRow = 0; nRow < (UINT) nRowsFetched; nRow++)
- {
- for (nCol = 0; nCol < pODBCSetInfo[nSet].nCols; nCol++)
- {
- _tprintf(_T("%s%s:"),
- pODBCSetInfo[nSet].pODBCColInfo[nCol].szColName,
- (pODBCSetInfo[nSet].pODBCColInfo[nCol].cbColName > 7 ?
- _T("\t") : _T("\t\t")));
-
- pIndicator = (SQLINTEGER*) (pODBCSetInfo[nSet].pRows +
- (nRow * pODBCSetInfo[nSet].cbResultSet) +
- pODBCSetInfo[nSet].pODBCColInfo[nCol].obIndicator);
- if (*pIndicator == SQL_NULL_DATA)
- {
- _tprintf(_T("\t<null>\n"));
- }
- else
- {
- pValue = pODBCSetInfo[nSet].pRows +
- ((nRow * pODBCSetInfo[nSet].cbResultSet) +
- pODBCSetInfo[nSet].pODBCColInfo[nCol].obValue);
- switch (pODBCSetInfo[nSet].pODBCColInfo[nCol].fBindType)
- {
- case SQL_C_CHAR:
- {
- printf("\t%s\n", (char*) pValue);
- break;
- }
-
- case SQL_C_SLONG:
- {
- printf("\t%lu\n", *(ULONG*) pValue);
- break;
- }
-
- case SQL_C_DOUBLE:
- {
- printf("\t%f\n", *(double*) pValue);
- break;
- }
-
- default:
- {
- _tprintf(_T("\tUnsupported conversion.\n"));
- break;
- }
- }
- }
- }
- _tprintf(_T("\n"));
- }
- }
-
- // If another result set exists, then get the next set.
- if (SQLMoreResults(hstmt) == SQL_SUCCESS)
- {
- // Determine the result set indicator (0 for normal result set,
- // number of COMPUTE clause set).
- sRet = SQLColAttribute(hstmt, 1, SQL_CA_SS_COMPUTE_ID,
- NULL, 0, NULL, (SQLPOINTER) &nSet);
- }
- else
- {
- break;
- }
- }
-
- EXIT:
- // Cleanup and go home.
- if (pODBCSetInfo != NULL)
- {
- for (nSet = 0; nSet < nComputes + 1; nSet++)
- {
- if (pODBCSetInfo[nSet].pODBCColInfo != NULL)
- delete [] pODBCSetInfo[nSet].pODBCColInfo;
- if (pODBCSetInfo[nSet].pRows != NULL)
- delete [] pODBCSetInfo[nSet].pRows;
- if (pODBCSetInfo[nSet].pByList != NULL)
- {
- if (pODBCSetInfo[nSet].pByList->pBys != NULL)
- delete [] pODBCSetInfo[nSet].pByList->pBys;
-
- delete [] pODBCSetInfo[nSet].pByList;
- }
- }
-
- delete [] pODBCSetInfo;
- }
-
- return;
- }
-
- // GetColumnsInfo(...) -- Query the result set to determine the properties
- // of result set columns.
- SQLRETURN GetColumnsInfo
- (
- SQLHSTMT hstmt,
- SWORD nCols,
- ODBCCOLINFO** ppODBCColInfo
- )
- {
- ODBCCOLINFO* pODBCColInfo;
- SWORD nCol;
- SQLRETURN sRet;
- SQLINTEGER nComputeCol;
-
- pODBCColInfo = new ODBCCOLINFO[nCols];
- if (pODBCColInfo == NULL)
- {
- DumpError(_T("Out of memory"));
- return (SQL_ERROR);
- }
-
- for (nCol = 0; nCol < nCols; nCol++)
- {
- sRet = SQLDescribeCol(hstmt, nCol+1,
- pODBCColInfo[nCol].szColName,
- CBCOLNAME_MAX + 1,
- &pODBCColInfo[nCol].cbColName,
- &pODBCColInfo[nCol].fSQLType,
- &pODBCColInfo[nCol].cbColData,
- &pODBCColInfo[nCol].cbScale,
- &pODBCColInfo[nCol].fNullable
- );
-
- if (sRet == SQL_ERROR)
- {
- DumpError(SQL_HANDLE_STMT, hstmt);
- break;
- }
-
- SQLColAttribute(hstmt, nCol+1, SQL_CA_SS_COLUMN_ID,
- NULL, 0, NULL, (SQLPOINTER) &nComputeCol);
-
- switch (pODBCColInfo[nCol].fSQLType)
- {
- case SQL_CHAR:
- case SQL_VARCHAR:
- case SQL_NUMERIC:
- case SQL_DECIMAL:
- {
- pODBCColInfo[nCol].fBindType = SQL_C_CHAR;
- pODBCColInfo[nCol].cbColData =
- AdjustLen(pODBCColInfo[nCol].cbColData + 1);
- break;
- }
-
- case SQL_REAL:
- case SQL_FLOAT:
- case SQL_DOUBLE:
- {
- pODBCColInfo[nCol].fBindType = SQL_C_DOUBLE;
- pODBCColInfo[nCol].cbColData = sizeof(double);
- break;
- }
-
- case SQL_INTEGER:
- case SQL_SMALLINT:
- case SQL_TINYINT:
- case SQL_BIT:
- {
- pODBCColInfo[nCol].fBindType = SQL_C_SLONG;
- pODBCColInfo[nCol].cbColData = sizeof(long);
- break;
- }
-
- default:
- {
- SQLColAttribute(hstmt, nCol+1, SQL_DESC_DISPLAY_SIZE,
- NULL, 0, NULL, &pODBCColInfo[nCol].cbColData);
- pODBCColInfo[nCol].fBindType = SQL_C_CHAR;
- pODBCColInfo[nCol].cbColData =
- AdjustLen(pODBCColInfo[nCol].cbColData + 1);
- break;
- }
- }
- }
-
- *ppODBCColInfo = pODBCColInfo;
-
- return (sRet);
- }
-
- // CreateDBBindings(...) -- Determine the row length for bound values.
- void CreateDBBindings
- (
- PODBCSETINFO pODBCSetInfo
- )
- {
- SQLUSMALLINT nCol;
- UDWORD cbRow = 0;
-
- for (nCol = 0; nCol < pODBCSetInfo->nCols; nCol++)
- {
- // Set the data buffer pointer and then add the width of the
- // bound buffer.
- pODBCSetInfo->pODBCColInfo[nCol].obValue = cbRow;
- cbRow += pODBCSetInfo->pODBCColInfo[nCol].cbColData;
-
- // Indicators bound beyond data.
- pODBCSetInfo->pODBCColInfo[nCol].obIndicator = cbRow;
- cbRow += sizeof(SQLINTEGER);
- }
-
- pODBCSetInfo->pRows = new BYTE[cbRow * pODBCSetInfo->nRows];
- pODBCSetInfo->cbResultSet = (SQLUINTEGER) cbRow;
-
- return;
- }
-
- // BindCols(...) -- Bind the columns in the structure.
- SQLRETURN BindCols
- (
- SQLHSTMT hstmt,
- SQLUSMALLINT nCols,
- PODBCCOLINFO pODBCColInfo,
- PBYTE pRows
- )
- {
- SQLUSMALLINT nCol;
-
- for (nCol = 0; nCol < nCols; nCol++)
- {
- if (SQLBindCol(hstmt, nCol + 1, pODBCColInfo[nCol].fBindType,
- (SQLPOINTER) (pRows + pODBCColInfo[nCol].obValue),
- pODBCColInfo[nCol].cbColData,
- (SQLINTEGER*) (pRows + pODBCColInfo[nCol].obIndicator)) ==
- SQL_ERROR)
- {
- return (SQL_ERROR);
- }
- }
-
- return (SQL_SUCCESS);
- }
-
- // DumpError(PTSTR) -- printf the string to the console.
- void DumpError
- (
- PTSTR pErrorText
- )
- {
- _tprintf(_T("%s\n"), pErrorText);
- }
-
- // DumpError(SQLSMALLINT, SQLHANDLE) -- printf the diagnostic records for the
- // handle received.
- void DumpError
- (
- SQLSMALLINT eHandleType,
- SQLHANDLE hodbc
- )
- {
- SQLTCHAR szState[SQL_SQLSTATE_SIZE + 1];
- SQLTCHAR szMessage[SQL_MAX_MESSAGE_LENGTH + 1];
- SQLINTEGER nServerError;
- SQLSMALLINT cbMessage;
- UINT nRec = 1;
-
- while (SQL_SUCCEEDED(SQLGetDiagRec(eHandleType, hodbc, nRec, szState,
- &nServerError, szMessage, SQL_MAX_MESSAGE_LENGTH + 1, &cbMessage)))
- {
- _tprintf(_T("SQLSTATE: %s\nNative error: %ld\nMessage: %s\n"),
- (PTSTR) szState, nServerError, (PTSTR) szMessage);
- nRec++;
- }
-
- return;
- }
-