From Code to Community: Sponsoring The Perl and Raku Conference 2025 Learn more

/*
+==========================================================+
| |
| ODBC extension for Win32 Perl |
| ----------------------------- |
| |
| by Dave Roth <rothd@roth.net> |
| |
| version v970208 |
| |
| Copyright (c) 1996-1997 Dave Roth. All rights reserved. |
| This program is free software; you can redistribute |
| it and/or modify it under the same terms as Perl itself. |
| |
+==========================================================+
based on original code by Dan DeMaggio (dmag@umich.edu)
Use under GNU General Public License or Larry Wall's "Artistic License"
*/
#define WIN32_LEAN_AND_MEAN
#include <stdlib.h>
#include <math.h>
#include <windows.h>
#include <stdio.h>
#include <SQL.H>
#include <SQLEXT.H>
#include <ODBCINST.H>
#if defined(__cplusplus)
extern "C" {
#endif
#include <EXTERN.h>
#include "perl.h"
#define NO_XSLOCKS
#include "XSub.h"
#if defined(__cplusplus)
}
#endif
#include "ODBCbuild.h"
#include "odbc.h"
#include "cresults.hpp"
CResults::CResults(ODBC_TYPE *h){
char szBuff[100];
int iTemp;
BOOL bResult = FALSE;
dRowSetSize = 0;
sNumOfCols = 0;
dSize = 0;
dReturnSize = 0;
szColumn = 0;
iODBC = h->conn;
SQLGetStmtOption(h->hstmt, SQL_ROWSET_SIZE, &dRowSetSize);
if (SQLNumResultCols(h->hstmt, (SWORD *)&sNumOfCols) == SQL_SUCCESS){
if (szColumn = new char* [sNumOfCols + 1]){ // Alloc array of strings
memset(szColumn, 0x00, sizeof(char*) * (sNumOfCols + 1));
}
// TESTING: multiply alloced data by dRowSetSize
if (dSize = new SDWORD [(sNumOfCols + 1) * dRowSetSize]){ // Alloc array of sizes
memset(dSize, 0x00, (sizeof(SDWORD) * (sNumOfCols + 1) * dRowSetSize));
}
// TESTING: multiply alloced data by dRowSetSize
if (dReturnSize = new SDWORD [(sNumOfCols + 1) * dRowSetSize]){ // Alloc array of return sizes
memset(dReturnSize, 0x00, (sizeof(SDWORD) * (sNumOfCols + 1) * dRowSetSize));
}
if(szColumn && dSize && dReturnSize){
SWORD dColType;
for(iTemp = 1; iTemp <= sNumOfCols; iTemp++){
if (SQLColAttributes(h->hstmt, iTemp, SQL_COLUMN_DISPLAY_SIZE, NULL, NULL, NULL, &dSize[iTemp]) == SQL_SUCCESS){
// If we cant resolve the size define it as the MAX Buffer
// size. Later we can change this if needed.
// This is just a patch. We need a mechanism to monitor
// if this needs more memory or not. Now a user can configure
// the memory size by hand. But this will be the size for ALL of this type.
// Sad. Very sad. We need to fix this hack!!!!!
if(dSize[iTemp] > MAX_DATA_ASSUME_SIZE){
dSize[iTemp] = h->iMaxBufSize;
}
// Here is dRowSetSize again.
if (!(szColumn[iTemp] = (char *) new UCHAR [(++dSize[iTemp]) * dRowSetSize])){
sprintf((char *)szBuff, "Could not allocate enough memory (%d bytes) for column %d.\n", (dSize[iTemp] * dRowSetSize), iTemp);
::ODBCError((char *)szBuff, h, "CResults()", "1");
break;
}
memset(szColumn[iTemp], 0x00, dSize[iTemp] * dRowSetSize);
}
}
// So far so good, now lets bind to the columns
if(! h->Error->ErrNum){
for(iTemp = 1; iTemp <= sNumOfCols; iTemp++){
SWORD sTemp;
SWORD sSQLType;
if (iTemp){
dColType = SQL_C_CHAR;
SQLDescribeCol(h->hstmt, iTemp, 0, 0, &sTemp, &sSQLType,(UDWORD *) &szBuff, &sTemp, &sTemp);
if ((sSQLType == SQL_LONGVARBINARY) || (sSQLType == SQL_BINARY) || (sSQLType == SQL_VARBINARY)){
dColType = SQL_C_BINARY;
}
}else{
dColType = SQL_C_BOOKMARK;
}
if(SQLBindCol(h->hstmt, iTemp, dColType, szColumn[iTemp], dSize[iTemp], &dReturnSize[iTemp]) != SQL_SUCCESS){
sprintf((char *)szBuff, "Could not bind column %d.\n", iTemp);
::ODBCError((char *)szBuff, h, "CResults()", "2");
bResult = FALSE;
}
}
bResult = TRUE;
}
}
}
if(bResult == FALSE){
RemoveBuffers();
if(dSize){
delete [] dSize;
}
if(dReturnSize){
delete [] dReturnSize;
}
sNumOfCols = 0;
}
}
CResults::~CResults(){
RemoveBuffers();
if(dSize){
delete [] dSize;
}
if(dReturnSize){
delete [] dReturnSize;
}
sNumOfCols = 0;
}
SDWORD CResults::Size(int iElement){
SDWORD dReturn = 0;
if (iElement >= 0 && iElement <= sNumOfCols){
dReturn = dSize[iElement];
}
return dReturn;
}
SDWORD CResults::ReturnSize(int iElement){
SDWORD dReturn = 0;
if (iElement >= 0 && iElement <= sNumOfCols){
// We should be reporting the correct size regardless of SQL_NULL_DATA so
// remark out the if (dReturnSize... for now...until we know we don't need it.
// if (dReturnSize[iElement] == SQL_NULL_DATA){
switch(dReturnSize[iElement]){
case SQL_NULL_DATA:
dReturn = 0;
break;
case SQL_NO_TOTAL:
// This is bad. If we don't know how long the data is this assumes
// that the data is a null terminated string. BAD!
dReturn = strlen(szColumn[iElement]);
break;
default:
dReturn = dReturnSize[iElement];
break;
}
// }
}
return dReturn;
}
SWORD CResults::NumOfCols(){
return sNumOfCols;
}
char *CResults::operator[](int iElement){;
char *szReturn = 0;
if (iElement >= 0 && iElement <= sNumOfCols){
switch(dReturnSize[iElement]){
case SQL_NULL_DATA:
szReturn = NULL_VALUE;
break;
case SQL_NO_TOTAL:
default:
szReturn = szColumn[iElement];
break;
}
}
return szReturn;
}
void CResults::RemoveBuffers(){
int iTemp;
ODBC_Conn *h;
if (h = ::_NT_ODBC_Verify(iODBC)){
if (h->hstmt){
SQLFreeStmt(h->hstmt, SQL_UNBIND);
}
}
if(szColumn){
for(iTemp = 0; iTemp <= sNumOfCols; iTemp++){
if (szColumn[iTemp]) delete [] szColumn[iTemp];
}
delete [] szColumn;
}
}
void CResults::Clean(){
int iTemp;
for(iTemp = 1; iTemp <= sNumOfCols; iTemp++){
memset(szColumn[iTemp], 0x00, (dSize[iTemp] * dRowSetSize));
}
}
DWORD CResults::RowSetSize(){
return dRowSetSize;
}