1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
|
int DatabaseODBC::Query(std::vector< std::vector<void*> > &ResultTable, char* QueryName, bool SavedQuery)
{
// Check that the database connection is established
if (SQLHandleDatabaseConnection == SQL_NULL_HDBC)
return -1;
SqlReturnCode = SQLAllocHandle(SQL_HANDLE_STMT, SQLHandleDatabaseConnection, &SQLHandleStatement);
if(SqlReturnCode != SQL_SUCCESS && SqlReturnCode != SQL_SUCCESS_WITH_INFO)
return -2;
char QueryToExecute[8000];
if (SavedQuery)
{
// Retrieve the actual query from the table of queries
char GetQuery[8000];
strcpy_s(GetQuery, "SELECT QueryText FROM QueryList WHERE QueryName LIKE '");
strcat_s(GetQuery, QueryName);
strcat_s(GetQuery, "'");
SqlReturnCode = SQLExecDirect(SQLHandleStatement, (SQLCHAR*)GetQuery, SQL_NTS);
if(SqlReturnCode != SQL_SUCCESS && SqlReturnCode != SQL_SUCCESS_WITH_INFO)
return -3;
strcpy_s(QueryToExecute,"");
while (SQLFetch(SQLHandleStatement) == SQL_SUCCESS)
{
SqlReturnCode=SQLGetData(
SQLHandleStatement, //SQLHSTMT StatementHandle,
1, //SQLUSMALLINT ColumnNumber,
SQL_C_CHAR, //SQLSMALLINT TargetType,
_DBRow_QueryList.Text, //SQLPOINTER TargetValuePtr,
sizeof(_DBRow_QueryList.Text), //SQLINTEGER BufferLength,
&_DBRow_QueryList.TextLength); //SQLINTEGER * StrLen_or_IndPtr
strcat_s(QueryToExecute, (char*)_DBRow_QueryList.Text);
}
//free the statement handle so that it can be reused
SQLFreeHandle(SQL_HANDLE_STMT, SQLHandleStatement);
}
else
strcpy_s(QueryToExecute, QueryName);
//allocate the statement handle again
SqlReturnCode = SQLAllocHandle(SQL_HANDLE_STMT, SQLHandleDatabaseConnection, &SQLHandleStatement);
if(SqlReturnCode != SQL_SUCCESS && SqlReturnCode != SQL_SUCCESS_WITH_INFO)
return -2;
//execute the query string retrieved from the QueryList table
SqlReturnCode = SQLExecDirect(SQLHandleStatement, (SQLCHAR*)QueryToExecute, SQL_NTS);
if(SqlReturnCode != SQL_SUCCESS && SqlReturnCode != SQL_SUCCESS_WITH_INFO)
return -3;
SQLSMALLINT NumColumns;
SqlReturnCode = SQLNumResultCols(SQLHandleStatement, &NumColumns);
if(SqlReturnCode != SQL_SUCCESS && SqlReturnCode != SQL_SUCCESS_WITH_INFO)
return -4;
//Get specifications for each column in order to fetch the data
std::vector<ColumnInfo> ColInfo;
ColInfo.resize(NumColumns);
for (SQLSMALLINT i=0; i<NumColumns; i++)
{
SQLDescribeCol (
SQLHandleStatement,
i+1,
ColInfo[i].ColumnName,
sizeof (ColInfo[i].ColumnName),
&ColInfo[i].ColumnNameLength,
&ColInfo[i].ColumnType,
&ColInfo[i].ColumnSize,
&ColInfo[i].DecimalDigits,
&ColInfo[i].Nullable);
//convert to SQL_CHAR if necessary so SqlGetData knows how to process
switch (ColInfo[i].ColumnType)
{
case SQL_VARCHAR : ColInfo[i].ColumnType = SQL_CHAR; break;
default : break;
}
}
//ResultTable accesses its data via ResultTable[Column][Row]
//This requires fewer resizes, since the number of columns is predetermined
ResultTable.resize(NumColumns);
ColumnType* CT;
int IndexRow = 0;
while (SQLFetch(SQLHandleStatement) == SQL_SUCCESS)
{
for (SQLSMALLINT IndexColumn=0; IndexColumn < NumColumns; IndexColumn++)
{
CT = new ColumnType; // if CT is not deleted, possible memory leak!
switch(ColInfo[IndexColumn].ColumnType)
{
// case SQL_UNKNOWN_TYPE : //0
case SQL_CHAR : //1
{
SqlReturnCode=SQLGetData(
SQLHandleStatement, //SQLHSTMT StatementHandle,
IndexColumn+1, //SQLUSMALLINT ColumnNumber, starting at 1
ColInfo[IndexColumn].ColumnType, //SQLSMALLINT TargetType,
&CT->_SqlChar, //SQLPOINTER TargetValuePtr,
sizeof(CT->_SqlChar), //SQLINTEGER BufferLength,
&CT->_ObjectLength); //SQLINTEGER * StrLen_or_IndPtr
ResultTable[IndexColumn].push_back((void*)CT->_SqlChar);
//To dereference this value, use the following as a template
// char* x = (char*)ResultTable[IndexColumn][IndexRow];
} break;
case SQL_NUMERIC : //2
{
} break;
case SQL_DECIMAL : //3
{
} break;
case SQL_INTEGER : //4
{
SqlReturnCode=SQLGetData(
SQLHandleStatement, //SQLHSTMT StatementHandle,
IndexColumn+1, //SQLUSMALLINT ColumnNumber, starting at 1
ColInfo[IndexColumn].ColumnType, //SQLSMALLINT TargetType,
&CT->_SqlInteger, //SQLPOINTER TargetValuePtr,
sizeof(CT->_SqlInteger), //SQLINTEGER BufferLength,
&CT->_ObjectLength); //SQLINTEGER * StrLen_or_IndPtr
ResultTable[IndexColumn].push_back(&CT->_SqlInteger);
//To dereference this value, use the following as a template
// int x = *((int*)(ResultTable[IndexColumn][IndexRow]));
} break;
case SQL_SMALLINT : //5
{
} break;
case SQL_FLOAT : //6
{
} break;
case SQL_REAL : //7
{
} break;
case SQL_DOUBLE : //8
{
} break;
case SQL_DATETIME : //9
{
} break;
case SQL_VARCHAR : //12
{
} break;
case SQL_TYPE_DATE : //91
{
} break;
case SQL_TYPE_TIME : //92
{
} break;
case SQL_TYPE_TIMESTAMP : //93
{
} break;
default :
{
}
}
// Does leaving this commented cause a memory leak?
// If so, how do I delete CT while not reusing a memory address for the 2D vector of pointers?
// delete CT;
}
IndexRow++;
}
//Release the query's handle from memory
if (SQLHandleStatement != SQL_NULL_HSTMT)
SQLFreeHandle(SQL_HANDLE_STMT, SQLHandleStatement);
return 0;
}
|