Improved new GetColumns, based on the old one + smoother database-type conversion and proper field order

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/branches/WX_2_2_BRANCH@7494 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Bart A.M. Jourquin
2000-05-26 11:13:28 +00:00
parent af899cb15b
commit 9879663237

View File

@@ -409,10 +409,10 @@ bool wxDb::Open(char *Dsn, char *Uid, char *AuthStr)
// Integer
if (! getDataTypeInfo(SQL_INTEGER, typeInfInteger))
// If SQL_INTEGER is not supported, use the floating point
// data type to store integers as well as floats
if (! getDataTypeInfo(typeInfFloat.FsqlType, typeInfInteger))
if (! getDataTypeInfo(SQL_INTEGER, typeInfInteger))
// If SQL_INTEGER is not supported, use the floating point
// data type to store integers as well as floats
if (! getDataTypeInfo(typeInfFloat.FsqlType, typeInfInteger))
return(FALSE);
else
typeInfInteger.FsqlType = typeInfFloat.FsqlType;
@@ -811,25 +811,25 @@ bool wxDb::getDataTypeInfo(SWORD fSqlType, wxDbSqlTypeInfo &structSQLTypeInfo)
*/
RETCODE retcode;
SDWORD cbRet;
// Get information about the data type specified
if (SQLGetTypeInfo(hstmt, fSqlType) != SQL_SUCCESS)
return(DispAllErrors(henv, hdbc, hstmt));
// Fetch the record
if ((retcode = SQLFetch(hstmt)) != SQL_SUCCESS)
{
{
#ifdef DBDEBUG_CONSOLE
if (retcode == SQL_NO_DATA_FOUND)
cout << "SQL_NO_DATA_FOUND fetching inf. about data type." << endl;
if (retcode == SQL_NO_DATA_FOUND)
cout << "SQL_NO_DATA_FOUND fetching inf. about data type." << endl;
#endif
DispAllErrors(henv, hdbc, hstmt);
SQLFreeStmt(hstmt, SQL_CLOSE);
return(FALSE);
}
DispAllErrors(henv, hdbc, hstmt);
SQLFreeStmt(hstmt, SQL_CLOSE);
return(FALSE);
}
// Obtain columns from the record
if (SQLGetData(hstmt, 1, SQL_C_CHAR, (UCHAR*) structSQLTypeInfo.TypeName, DB_TYPE_NAME_LEN, &cbRet) != SQL_SUCCESS)
return(DispAllErrors(henv, hdbc, hstmt));
// BJO 20000503: no more needed with new GetColumns...
#if OLD_GETCOLUMNS
@@ -1933,8 +1933,14 @@ wxDbColInf *wxDb::GetColumns(char *tableName, int *numCols, const char *userID)
BJO 20000503
These are tentative new GetColumns members which should be more database independant and
which always returns the columns in the order they were created.
These are tentative new GetColumns members:
- The first one (wxDbColInf *wxDb::GetColumns(char *tableName[], const char* userID)) calls
the second implementation for each separate table before merging the results. This makes the
code easier to maintain as only one member (the second) makes the real work
- wxDbColInf *wxDb::GetColumns(char *tableName, int *numCols, const char *userID) is a little bit improved
- It doesn't anymore rely on the type-name to find out which database-type each column has
- It ends by sorting the columns, so that they are returned in the same order they were created
*/
@@ -1944,8 +1950,9 @@ typedef struct
wxDbColInf *colInf;
} _TableColumns;
wxDbColInf *wxDb::GetColumns(char *tableName[], const char* WXUNUSED(userID))
wxDbColInf *wxDb::GetColumns(char *tableName[], const char* userID)
{
int i, j;
// The last array element of the tableName[] argument must be zero (null).
// This is how the end of the array is detected.
@@ -1959,10 +1966,10 @@ wxDbColInf *wxDb::GetColumns(char *tableName[], const char* WXUNUSED(userID))
_TableColumns *TableColumns = new _TableColumns[tbl];
// Fill the table
int i;
for (i = 0 ; i < tbl ; i++)
{
TableColumns[i].colInf = GetColumns(tableName[i], &TableColumns[i].noCols);
TableColumns[i].colInf = GetColumns(tableName[i], &TableColumns[i].noCols, userID);
if (TableColumns[i].colInf == NULL) return NULL;
noCols += TableColumns[i].noCols;
}
@@ -1977,7 +1984,7 @@ wxDbColInf *wxDb::GetColumns(char *tableName[], const char* WXUNUSED(userID))
// Merge ...
int offset = 0;
int j;
for (i = 0 ; i < tbl ; i++)
{
for (j = 0 ; j < TableColumns[i].noCols ; j++)
@@ -1985,24 +1992,218 @@ wxDbColInf *wxDb::GetColumns(char *tableName[], const char* WXUNUSED(userID))
colInf[offset++] = TableColumns[i].colInf[j];
}
}
delete [] TableColumns;
return colInf;
}
/********** wxDb::GetColumns() **********/
wxDbColInf *wxDb::GetColumns(char *tableName, int *numCols, const char* WXUNUSED(userID))
wxDbColInf *wxDb::GetColumns(char *tableName, int *numCols, const char *userID)
//
// Same as the above GetColumns() function except this one gets columns
// only for a single table, and if 'numCols' is not NULL, the number of
// columns stored in the returned wxDbColInf is set in '*numCols'
//
// userID is evaluated in the following manner:
// userID == NULL ... UserID is ignored
// userID == "" ... UserID set equal to 'this->uid'
// userID != "" ... UserID set equal to 'userID'
//
// NOTE: ALL column bindings associated with this wxDb instance are unbound
// by this function. This function should use its own wxDb instance
// to avoid undesired unbinding of columns.
{
SWORD noCols = 0;
int colNo = 0;
wxDbColInf *colInf = 0;
short noCols;
RETCODE retcode;
SDWORD cb;
wxString UserID;
wxString TableName;
if (userID)
{
if (!wxStrlen(userID))
UserID = uid;
else
UserID = userID;
}
else
UserID = "";
// dBase does not use user names, and some drivers fail if you try to pass one
if (Dbms() == dbmsDBASE)
UserID = "";
// Oracle user names may only be in uppercase, so force
// the name to uppercase
if (Dbms() == dbmsORACLE)
UserID = UserID.Upper();
// Pass 1 - Determine how many columns there are.
// Pass 2 - Allocate the wxDbColInf array and fill in
// the array with the column information.
int pass;
for (pass = 1; pass <= 2; pass++)
{
if (pass == 2)
{
if (noCols == 0) // Probably a bogus table name(s)
break;
// Allocate n wxDbColInf objects to hold the column information
colInf = new wxDbColInf[noCols+1];
if (!colInf)
break;
// Mark the end of the array
wxStrcpy(colInf[noCols].tableName, wxT(""));
wxStrcpy(colInf[noCols].colName, wxT(""));
colInf[noCols].sqlDataType = 0;
}
TableName = tableName;
// Oracle table names are uppercase only, so force
// the name to uppercase just in case programmer forgot to do this
if (Dbms() == dbmsORACLE)
TableName = TableName.Upper();
SQLFreeStmt(hstmt, SQL_CLOSE);
// MySQL and Access cannot accept a user name when looking up column names, so we
// use the call below that leaves out the user name
if (wxStrcmp(UserID.c_str(),wxT("")) &&
Dbms() != dbmsMY_SQL &&
Dbms() != dbmsACCESS)
{
retcode = SQLColumns(hstmt,
NULL, 0, // All qualifiers
(UCHAR *) UserID.c_str(), SQL_NTS, // Owner
(UCHAR *) TableName.c_str(), SQL_NTS,
NULL, 0); // All columns
}
else
{
retcode = SQLColumns(hstmt,
NULL, 0, // All qualifiers
NULL, 0, // Owner
(UCHAR *) TableName.c_str(), SQL_NTS,
NULL, 0); // All columns
}
if (retcode != SQL_SUCCESS)
{ // Error occured, abort
DispAllErrors(henv, hdbc, hstmt);
if (colInf)
delete [] colInf;
SQLFreeStmt(hstmt, SQL_CLOSE);
if (numCols)
*numCols = 0;
return(0);
}
while ((retcode = SQLFetch(hstmt)) == SQL_SUCCESS)
{
if (pass == 1) // First pass, just add up the number of columns
noCols++;
else // Pass 2; Fill in the array of structures
{
if (colNo < noCols) // Some extra error checking to prevent memory overwrites
{
// NOTE: Only the ODBC 1.x fields are retrieved
GetData( 1, SQL_C_CHAR, (UCHAR*) colInf[colNo].catalog, 128+1, &cb);
GetData( 2, SQL_C_CHAR, (UCHAR*) colInf[colNo].schema, 128+1, &cb);
GetData( 3, SQL_C_CHAR, (UCHAR*) colInf[colNo].tableName, DB_MAX_TABLE_NAME_LEN+1, &cb);
GetData( 4, SQL_C_CHAR, (UCHAR*) colInf[colNo].colName, DB_MAX_COLUMN_NAME_LEN+1, &cb);
GetData( 5, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].sqlDataType, 0, &cb);
GetData( 6, SQL_C_CHAR, (UCHAR*) colInf[colNo].typeName, 128+1, &cb);
GetData( 7, SQL_C_SLONG, (UCHAR*) &colInf[colNo].columnSize, 0, &cb);
GetData( 8, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].bufferLength, 0, &cb);
GetData( 9, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].decimalDigits,0, &cb);
GetData(10, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].numPrecRadix, 0, &cb);
GetData(11, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].nullable, 0, &cb);
GetData(12, SQL_C_CHAR, (UCHAR*) colInf[colNo].remarks, 254+1, &cb);
// Start Values for Primary/Foriegn Key (=No)
colInf[colNo].PkCol = 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
colInf[colNo].PkTableName[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
colInf[colNo].FkCol = 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
colInf[colNo].FkTableName[0] = 0; // Foreign key table name
#ifdef _IODBC_
// IODBC returns the columnSize in bufferLength.. (bug)
colInf[colNo].columnSize = colInf[colNo].bufferLength;
#endif
// Determine the wxDb data type that is used to represent the native data type of this data source
colInf[colNo].dbDataType = 0;
// Get the intern datatype
switch (colInf[colNo].sqlDataType)
{
case SQL_VARCHAR:
case SQL_CHAR:
colInf[colNo].dbDataType = DB_DATA_TYPE_VARCHAR;
break;
case SQL_TINYINT:
case SQL_SMALLINT:
case SQL_INTEGER:
colInf[colNo].dbDataType = DB_DATA_TYPE_INTEGER;
break;
case SQL_DOUBLE:
case SQL_DECIMAL:
case SQL_NUMERIC:
case SQL_FLOAT:
case SQL_REAL:
colInf[colNo].dbDataType = DB_DATA_TYPE_FLOAT;
break;
case SQL_DATE:
colInf[colNo].dbDataType = DB_DATA_TYPE_DATE;
break;
#ifdef __WXDEBUG__
default:
wxString errMsg;
errMsg.sprintf("SQL Data type %d currently not supported by wxWindows", colInf[colNo].sqlDataType);
wxLogDebug(errMsg,wxT("ODBC DEBUG MESSAGE"));
#endif
}
colNo++;
}
}
}
if (retcode != SQL_NO_DATA_FOUND)
{ // Error occured, abort
DispAllErrors(henv, hdbc, hstmt);
if (colInf)
delete [] colInf;
SQLFreeStmt(hstmt, SQL_CLOSE);
if (numCols)
*numCols = 0;
return(0);
}
}
SQLFreeStmt(hstmt, SQL_CLOSE);
// Store Primary and Foreign Keys
GetKeyFields(tableName,colInf,noCols);
///////////////////////////////////////////////////////////////////////////
// Now sort the the columns in order to make them appear in the right order
///////////////////////////////////////////////////////////////////////////
// Build a generic SELECT statement which returns 0 rows
wxString Stmt;
Stmt.sprintf("select * from %s where 0=1", tableName);
// Execute query
if (SQLExecDirect(hstmt, (UCHAR FAR *) Stmt.c_str(), SQL_NTS) != SQL_SUCCESS)
{
@@ -2016,159 +2217,62 @@ wxDbColInf *wxDb::GetColumns(char *tableName, int *numCols, const char* WXUNUSED
DispAllErrors(henv, hdbc, hstmt);
return NULL;
}
if (noCols == 0) // Probably a bogus table name
return NULL;
// Allocate noCols wxDbColInf objects to hold the column information
colInf = new wxDbColInf[noCols+1];
if (!colInf) return NULL;
// Mark the end of the array
wxStrcpy(colInf[noCols].tableName, wxT(""));
wxStrcpy(colInf[noCols].colName, wxT(""));
colInf[noCols].sqlDataType = 0;
// Get the name and other type information
for (short colNum = 0; colNum < noCols; colNum++)
{
if (SQLDescribeCol (hstmt,
colNum+1,
(UCHAR*) colInf[colNum].colName,DB_MAX_COLUMN_NAME_LEN+1,
&colInf[colNum].bufferLength,
&colInf[colNum].sqlDataType,
(UDWORD*) &colInf[colNum].columnSize,
&colInf[colNum].decimalDigits,
&colInf[colNum].nullable) != SQL_SUCCESS)
// Get the name
int i;
short colNum;
UCHAR name[100];
SWORD Sword;
SDWORD Sdword;
for (colNum = 0; colNum < noCols; colNum++)
{
if (SQLColAttributes(hstmt,colNum+1, SQL_COLUMN_NAME,
name, sizeof(name),
&Sword, &Sdword) != SQL_SUCCESS)
{
DispAllErrors(henv, hdbc, hstmt);
return NULL;
}
}
/*
More info can be found with SQLColAttributes.
wxString Name1 = name;
Name1 = Name1.Upper();
columnName and the bufferLength are fetched
again because they are not always properly fetched by SQLDescribeCol (I observed this only with MS odbc drivers
for Access and SQLServer).
*/
SWORD cb;
SDWORD value;
// Column name
if (SQLColAttributes(hstmt,colNum+1, SQL_COLUMN_NAME, (UCHAR*) colInf[colNum].colName, sizeof(colInf[colNum].colName), &cb, &value) != SQL_SUCCESS)
{
DispAllErrors(henv, hdbc, hstmt);
return NULL;
}
// Buffer length
if (SQLColAttributes(hstmt,colNum+1, SQL_COLUMN_LENGTH, NULL,0, &cb, &value) != SQL_SUCCESS)
// Where is this name in the array ?
for (i = colNum ; i < noCols ; i++)
{
DispAllErrors(henv, hdbc, hstmt);
return NULL;
}
colInf[colNum].bufferLength = value;
// Column type name
if (SQLColAttributes(hstmt,colNum+1, SQL_COLUMN_TYPE_NAME, (UCHAR*) colInf[colNum].typeName, sizeof(colInf[colNum].typeName), &cb, &value) != SQL_SUCCESS)
{
DispAllErrors(henv, hdbc, hstmt);
return NULL;
}
// table name
if (SQLColAttributes(hstmt,colNum+1, SQL_COLUMN_TABLE_NAME, (UCHAR*) colInf[colNum].tableName, sizeof(colInf[colNum].tableName), &cb, &value) != SQL_SUCCESS)
{
DispAllErrors(henv, hdbc, hstmt);
return NULL;
}
// remarks (replaced here by the label)
if (SQLColAttributes(hstmt,colNum+1, SQL_COLUMN_LABEL, (UCHAR*) colInf[colNum].remarks, sizeof(colInf[colNum].remarks), &cb, &value) != SQL_SUCCESS)
{
DispAllErrors(henv, hdbc, hstmt);
return NULL;
}
// schema
if (SQLColAttributes(hstmt,colNum+1, SQL_COLUMN_OWNER_NAME, (UCHAR*) colInf[colNum].schema, sizeof(colInf[colNum].schema), &cb, &value) != SQL_SUCCESS)
{
DispAllErrors(henv, hdbc, hstmt);
return NULL;
}
// catalog
if (SQLColAttributes(hstmt,colNum+1, SQL_COLUMN_QUALIFIER_NAME, (UCHAR*) colInf[colNum].catalog, sizeof(colInf[colNum].catalog), &cb, &value) != SQL_SUCCESS)
{
DispAllErrors(henv, hdbc, hstmt);
return NULL;
}
// Get the intern datatype
switch (colInf[colNum].sqlDataType)
{
case SQL_VARCHAR:
case SQL_CHAR:
colInf[colNum].dbDataType = DB_DATA_TYPE_VARCHAR;
//wxStrcpy(colInf[colNum].typeName, typeInfVarchar.TypeName);
break;
case SQL_TINYINT:
case SQL_SMALLINT:
case SQL_INTEGER:
colInf[colNum].dbDataType = DB_DATA_TYPE_INTEGER;
//wxStrcpy(colInf[colNum].typeName, typeInfInteger.TypeName);
break;
case SQL_DOUBLE:
case SQL_DECIMAL:
case SQL_NUMERIC:
case SQL_FLOAT:
case SQL_REAL:
colInf[colNum].dbDataType = DB_DATA_TYPE_FLOAT;
//wxStrcpy(colInf[colNum].typeName, typeInfFloat.TypeName);
break;
case SQL_DATE:
colInf[colNum].dbDataType = DB_DATA_TYPE_DATE;
// wxStrcpy(colInf[colNum].typeName, typeInfDate.TypeName);
break;
default:
#ifdef __WXDEBUG__
wxString errMsg;
errMsg.sprintf("SQL Data type %d currently not supported by wxWindows", colInf[colNum].sqlDataType);
wxLogDebug(errMsg,wxT("ODBC DEBUG MESSAGE"));
#endif
return NULL;
}
}
wxString Name2 = colInf[i].colName;
Name2 = Name2.Upper();
if (Name2 == Name1)
{
if (colNum != i) // swap to sort
{
wxDbColInf tmpColInf = colInf[colNum];
colInf[colNum] = colInf[i];
colInf[i] = tmpColInf;
}
break;
}
}
}
SQLFreeStmt(hstmt, SQL_CLOSE);
// Store Primary and Foreign Keys
GetKeyFields(tableName,colInf,noCols);
*numCols = (int) noCols;
return colInf;
///////////////////////////////////////////////////////////////////////////
// End sorting
///////////////////////////////////////////////////////////////////////////
if (numCols)
*numCols = noCols;
return colInf;
} // wxDb::GetColumns()
#endif
@@ -2574,17 +2678,18 @@ bool wxDb::TableExists(const char *tableName, const char *userID, const char *ta
assert(tableName && wxStrlen(tableName));
if (Dbms() == dbmsDBASE)
if (Dbms() == dbmsDBASE)
{
wxString dbName;
if (tablePath && wxStrlen(tablePath))
dbName.sprintf("%s/%s.dbf",tablePath,tableName);
else
dbName.sprintf("%s.dbf",tableName);
bool exists;
exists = wxFileExists(dbName.c_str());
return exists;
wxString dbName;
if (tablePath && wxStrlen(tablePath))
dbName.sprintf("%s\\%s.dbf",tablePath,tableName);
else
dbName.sprintf("%s.dbf",tableName);
bool exists;
exists = wxFileExists(dbName.c_str());
return exists;
}
if (userID)