#include "table.h" #include <cstdio> /* The constructor initialises its members. */ Table::Table() { tab.colNum = 0; tab.colName = "<empty>"; tab.colType = 0; tab.colTypeName = "<empty>"; tab.colLength = 0; tab.colScale = 0; tab.colNullable = 0; tab.binding = NULL; tab.bytesReturned = 0; tab.colNameLen = 0; attribs.cRows = attribs.cColumns = 0; tableInstances++; }; /* This has no practical use */ int Table::tableInstances = 0; Table::~Table() { tableInstances--; }; void Table::initTable(COLATTRIBUTES a) { Schema.push_back(a); }; char * Table::getSchema(int c) { return (char *) Schema[c].colName.c_str(); }; void Table::setSchema(COLATTRIBUTES a) { Schema.push_back(a); }; RESULTATTR Table::getAttribs() { return attribs; }; void Table::setAttribs(int r, int c) { attribs.cRows = r; attribs.cColumns = c; }; /* bindColumns is used to assign storage for the results of an SQL statement. Each table has a set of column attributes which is stored in the Schema vector. Each column is described by the structure COLATTRIBUTES and one of the fields of this structure is binding which is a pointer to a piece of memory allocated to the data for this column. It also (via SQLBindCol) takes care of conversion of the native data to chars. */ SQLRETURN Table::bindColumns(HSTMT &hstmt) { SQLRETURN rc = 0; int i = 0; for (i = 0; i < Schema.size(); i++) { Schema[i].binding = new char[Schema[i].colLength + 10]; //allocate storage for data memset(Schema[i].binding, 0, sizeof(Schema[i].binding)); //and zero it rc = SQLBindCol ( hstmt, Schema[i].colNum, SQL_C_CHAR, Schema[i].binding, Schema[i].colLength, &Schema[i].bytesReturned ); } return rc; }; /* buildSchema calls SQLDescribeCol (from the ODBC library) to determine the relevant information for each attribute of the result set. The schema is stored in the member Schema which is a vector of the COLATTRIBUTES structure. Minimal error checking is done! and this method should probably be private. It is called with a statement handle reference and if this points to nowhere in particular - yikes! This would be a good place to throw an exception or at least use an assertion. */ SQLRETURN Table::buildSchema(HSTMT &hstmt, int numCols) { int i; SQLCHAR colName[32]; SWORD colNamelen, colType, scale, nullable; SQLUINTEGER colLen; SQLRETURN rc; COLATTRIBUTES a; for (i = 0; i < numCols; i++) { rc = SQLDescribeCol( hstmt, i + 1, colName, 32, &colNamelen, &colType, &colLen, &scale, &nullable); a.colNum = i + 1; a.colName = (char *) colName; a.colType = colType; a.colLength = max(colLen + 4, strlen((char *)colName)); a.colNullable = nullable; a.colScale = scale; a.colNameLen = colNamelen; switch (colType) { case SQL_VARCHAR: a.colTypeName = "VARCHAR"; break; case SQL_CHAR: a.colTypeName = "CHAR"; break; case SQL_FLOAT: a.colTypeName = "FLOAT"; break; case SQL_DOUBLE: a.colTypeName = "DOUBLE"; break; case SQL_NUMERIC: a.colTypeName = "NUMERIC"; break; case SQL_REAL: a.colTypeName = "REAL"; break; case SQL_DECIMAL: a.colTypeName = "DECIMAL"; break; case SQL_INTEGER: a.colTypeName = "INTEGER"; break; //Don't forget to case SQL_SMALLINT: a.colTypeName = "SMALLINT"; break; //add other data types default: a.colTypeName = "UNKNOWN"; break; } a.bytesReturned = 0; Schema.push_back(a); } }; /* Get the next row from the raw result hstmt and move it to an accessible storage. It is called with a statement handle reference and uses the tData member to store the data; tData is a vector of the structure tableData. At the moment this is just plain text, ie everything, ie the column data, is converted to text (see bindColumns) above. It might be better to use the native data type returned by the execution of a select statement but at the moment this isn't urgent. It should be a trivial matter to convert data back to some type in the user application. */ SQLRETURN Table::transferData(HSTMT & hstmt) { SQLRETURN rc = 0; int i = 0; while ( (rc = SQLFetch(hstmt)) != SQL_ERROR ) { if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) { for (i = 0; i < Schema.size(); i++) { rc = SQLGetData( hstmt, Schema[i].colNum, SQL_C_CHAR, Schema[i].binding, Schema[i].colLength, &Schema[i].bytesReturned ); tableData * d; d = new tableData; d->text = new char [Schema[i].colLength]; strcpy(d->text, Schema[i].binding); tData.push_back(*d); delete d; } } else //Something is wrong { return rc; } } return rc; }; char * Table::getField(int row, int col, int numCols) { return tData[(row * numCols) + col].text; };