+ cout << endl << sqlStmt.c_str() << endl;
+#endif
+
+ if (SQLExecDirect(hstmt, WXSQLCAST(sqlStmt), SQL_NTS) != SQL_SUCCESS)
+ {
+ // Check for "Base table not found" error and ignore
+ GetNextError(henv, hdbc, hstmt);
+ if (wxStrcmp(sqlState,wxT("S0002"))) // "Base table not found"
+ {
+ // Check for product specific error codes
+ if (!((Dbms() == dbmsSYBASE_ASA && !wxStrcmp(sqlState,wxT("42000"))))) // 5.x (and lower?)
+ {
+ DispNextError();
+ DispAllErrors(henv, hdbc, hstmt);
+ RollbackTrans();
+ return false;
+ }
+ }
+ }
+
+ // Commit the transaction
+ if (!CommitTrans())
+ return false;
+
+ return true;
+
+} // wxDb::DropView()
+
+
+/********** wxDb::ExecSql() **********/
+bool wxDb::ExecSql(const wxString &pSqlStmt)
+{
+ RETCODE retcode;
+
+ SQLFreeStmt(hstmt, SQL_CLOSE);
+
+ retcode = SQLExecDirect(hstmt, WXSQLCAST(pSqlStmt), SQL_NTS);
+ if (retcode == SQL_SUCCESS ||
+ (Dbms() == dbmsDB2 && (retcode == SQL_SUCCESS_WITH_INFO || retcode == SQL_NO_DATA_FOUND)))
+ {
+ return true;
+ }
+ else
+ {
+ DispAllErrors(henv, hdbc, hstmt);
+ return false;
+ }
+
+} // wxDb::ExecSql()
+
+
+/********** wxDb::ExecSql() with column info **********/
+bool wxDb::ExecSql(const wxString &pSqlStmt, wxDbColInf** columns, short& numcols)
+{
+ //execute the statement first
+ if (!ExecSql(pSqlStmt))
+ return false;
+
+ SWORD noCols;
+ if (SQLNumResultCols(hstmt, &noCols) != SQL_SUCCESS)
+ {
+ DispAllErrors(henv, hdbc, hstmt);
+ return false;
+ }
+
+ if (noCols == 0)
+ return false;
+ else
+ numcols = noCols;
+
+ // Get column information
+ short colNum;
+ wxChar name[DB_MAX_COLUMN_NAME_LEN+1];
+ SWORD Sword;
+ SQLLEN Sqllen;
+ wxDbColInf* pColInf = new wxDbColInf[noCols];
+
+ // Fill in column information (name, datatype)
+ for (colNum = 0; colNum < noCols; colNum++)
+ {
+ if (SQLColAttributes(hstmt, (UWORD)(colNum+1), SQL_COLUMN_NAME,
+ name, sizeof(name),
+ &Sword, &Sqllen) != SQL_SUCCESS)
+ {
+ DispAllErrors(henv, hdbc, hstmt);
+ delete[] pColInf;
+ return false;
+ }
+
+ wxStrncpy(pColInf[colNum].colName, name, DB_MAX_COLUMN_NAME_LEN);
+ pColInf[colNum].colName[DB_MAX_COLUMN_NAME_LEN] = 0; // Prevent buffer overrun
+
+ if (SQLColAttributes(hstmt, (UWORD)(colNum+1), SQL_COLUMN_TYPE,
+ NULL, 0, &Sword, &Sqllen) != SQL_SUCCESS)
+ {
+ DispAllErrors(henv, hdbc, hstmt);
+ delete[] pColInf;
+ return false;
+ }
+
+ switch (Sqllen)
+ {
+#if wxUSE_UNICODE
+ #if defined(SQL_WCHAR)
+ case SQL_WCHAR:
+ #endif
+ #if defined(SQL_WVARCHAR)
+ case SQL_WVARCHAR:
+ #endif
+#endif
+ case SQL_VARCHAR:
+ case SQL_CHAR:
+ pColInf[colNum].dbDataType = DB_DATA_TYPE_VARCHAR;
+ break;
+ case SQL_LONGVARCHAR:
+ pColInf[colNum].dbDataType = DB_DATA_TYPE_MEMO;
+ break;
+ case SQL_TINYINT:
+ case SQL_SMALLINT:
+ case SQL_INTEGER:
+ case SQL_BIT:
+ pColInf[colNum].dbDataType = DB_DATA_TYPE_INTEGER;
+ break;
+ case SQL_DOUBLE:
+ case SQL_DECIMAL:
+ case SQL_NUMERIC:
+ case SQL_FLOAT:
+ case SQL_REAL:
+ pColInf[colNum].dbDataType = DB_DATA_TYPE_FLOAT;
+ break;
+ case SQL_DATE:
+ case SQL_TIMESTAMP:
+ pColInf[colNum].dbDataType = DB_DATA_TYPE_DATE;
+ break;
+ case SQL_BINARY:
+ pColInf[colNum].dbDataType = DB_DATA_TYPE_BLOB;
+ break;
+#ifdef __WXDEBUG__
+ default:
+ wxString errMsg;
+ errMsg.Printf(wxT("SQL Data type %ld currently not supported by wxWidgets"), (long)Sqllen);
+ wxLogDebug(errMsg,wxT("ODBC DEBUG MESSAGE"));
+#endif
+ }
+ }
+
+ *columns = pColInf;
+ return true;
+} // wxDb::ExecSql()
+
+/********** wxDb::GetNext() **********/
+bool wxDb::GetNext(void)
+{
+ if (SQLFetch(hstmt) == SQL_SUCCESS)
+ return true;
+ else
+ {
+ DispAllErrors(henv, hdbc, hstmt);
+ return false;
+ }
+
+} // wxDb::GetNext()
+
+
+/********** wxDb::GetData() **********/
+bool wxDb::GetData(UWORD colNo, SWORD cType, PTR pData, SDWORD maxLen, SQLLEN FAR *cbReturned)
+{
+ wxASSERT(pData);
+ wxASSERT(cbReturned);
+
+ long bufferSize = maxLen;
+
+ if (cType == SQL_C_WXCHAR)
+ bufferSize = maxLen * sizeof(wxChar);
+
+ if (SQLGetData(hstmt, colNo, cType, pData, bufferSize, cbReturned) == SQL_SUCCESS)
+ return true;
+ else
+ {
+ DispAllErrors(henv, hdbc, hstmt);
+ return false;
+ }
+
+} // wxDb::GetData()
+
+
+/********** wxDb::GetKeyFields() **********/
+int wxDb::GetKeyFields(const wxString &tableName, wxDbColInf* colInf, UWORD noCols)
+{
+ wxChar szPkTable[DB_MAX_TABLE_NAME_LEN+1]; /* Primary key table name */
+ wxChar szFkTable[DB_MAX_TABLE_NAME_LEN+1]; /* Foreign key table name */
+ SWORD iKeySeq;
+ wxChar szPkCol[DB_MAX_COLUMN_NAME_LEN+1]; /* Primary key column */
+ wxChar szFkCol[DB_MAX_COLUMN_NAME_LEN+1]; /* Foreign key column */
+ SQLRETURN retcode;
+ SQLLEN cb;
+ SWORD i;
+ wxString tempStr;
+ /*
+ * -----------------------------------------------------------------------
+ * -- 19991224 : mj10777 : Create ------
+ * -- : Three things are done and stored here : ------
+ * -- : 1) which Column(s) is/are Primary Key(s) ------
+ * -- : 2) which tables use this Key as a Foreign Key ------
+ * -- : 3) which columns are Foreign Key and the name ------
+ * -- : of the Table where the Key is the Primary Key -----
+ * -- : Called from GetColumns(const wxString &tableName, ------
+ * -- int *numCols,const wxChar *userID ) ------
+ * -----------------------------------------------------------------------
+ */
+
+ /*---------------------------------------------------------------------*/
+ /* Get the names of the columns in the primary key. */
+ /*---------------------------------------------------------------------*/
+ retcode = SQLPrimaryKeys(hstmt,
+ NULL, 0, /* Catalog name */
+ NULL, 0, /* Schema name */
+ WXSQLCAST(tableName), SQL_NTS); /* Table name */
+
+ /*---------------------------------------------------------------------*/
+ /* Fetch and display the result set. This will be a list of the */
+ /* columns in the primary key of the tableName table. */
+ /*---------------------------------------------------------------------*/
+ while ((retcode == SQL_SUCCESS) || (retcode == SQL_SUCCESS_WITH_INFO))
+ {
+ retcode = SQLFetch(hstmt);
+ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ {
+ GetData( 4, SQL_C_WXCHAR, szPkCol, DB_MAX_COLUMN_NAME_LEN+1, &cb);
+ GetData( 5, SQL_C_SSHORT, &iKeySeq, 0, &cb);
+ //-------
+ for (i=0;i<noCols;i++) // Find the Column name
+ if (!wxStrcmp(colInf[i].colName,szPkCol)) // We have found the Column
+ colInf[i].PkCol = iKeySeq; // Which Primary Key is this (first, second usw.) ?
+ } // if
+ } // while
+ SQLFreeStmt(hstmt, SQL_CLOSE); /* Close the cursor (the hstmt is still allocated). */
+
+ /*---------------------------------------------------------------------*/
+ /* Get all the foreign keys that refer to tableName primary key. */
+ /*---------------------------------------------------------------------*/
+ retcode = SQLForeignKeys(hstmt,
+ NULL, 0, /* Primary catalog */
+ NULL, 0, /* Primary schema */
+ WXSQLCAST(tableName), SQL_NTS,/* Primary table */
+ NULL, 0, /* Foreign catalog */
+ NULL, 0, /* Foreign schema */
+ NULL, 0); /* Foreign table */
+
+ /*---------------------------------------------------------------------*/
+ /* Fetch and display the result set. This will be all of the foreign */
+ /* keys in other tables that refer to the tableName primary key. */
+ /*---------------------------------------------------------------------*/
+ tempStr.Empty();
+ szPkCol[0] = 0;
+ while ((retcode == SQL_SUCCESS) || (retcode == SQL_SUCCESS_WITH_INFO))
+ {
+ retcode = SQLFetch(hstmt);
+ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ {
+ GetData( 3, SQL_C_WXCHAR, szPkTable, DB_MAX_TABLE_NAME_LEN+1, &cb);
+ GetData( 4, SQL_C_WXCHAR, szPkCol, DB_MAX_COLUMN_NAME_LEN+1, &cb);
+ GetData( 5, SQL_C_SSHORT, &iKeySeq, 0, &cb);
+ GetData( 7, SQL_C_WXCHAR, szFkTable, DB_MAX_TABLE_NAME_LEN+1, &cb);
+ GetData( 8, SQL_C_WXCHAR, szFkCol, DB_MAX_COLUMN_NAME_LEN+1, &cb);
+ tempStr << _T('[') << szFkTable << _T(']'); // [ ] in case there is a blank in the Table name
+ } // if
+ } // while
+
+ tempStr.Trim(); // Get rid of any unneeded blanks
+ if (!tempStr.empty())
+ {
+ for (i=0; i<noCols; i++)
+ { // Find the Column name
+ if (!wxStrcmp(colInf[i].colName, szPkCol)) // We have found the Column, store the Information
+ {
+ wxStrncpy(colInf[i].PkTableName, tempStr.c_str(), DB_MAX_TABLE_NAME_LEN); // Name of the Tables where this Primary Key is used as a Foreign Key
+ colInf[i].PkTableName[DB_MAX_TABLE_NAME_LEN] = 0; // Prevent buffer overrun
+ }
+ }
+ } // if
+
+ SQLFreeStmt(hstmt, SQL_CLOSE); /* Close the cursor (the hstmt is still allocated). */
+
+ /*---------------------------------------------------------------------*/
+ /* Get all the foreign keys in the tablename table. */
+ /*---------------------------------------------------------------------*/
+ retcode = SQLForeignKeys(hstmt,
+ NULL, 0, /* Primary catalog */
+ NULL, 0, /* Primary schema */
+ NULL, 0, /* Primary table */
+ NULL, 0, /* Foreign catalog */
+ NULL, 0, /* Foreign schema */
+ WXSQLCAST(tableName), SQL_NTS);/* Foreign table */
+
+ /*---------------------------------------------------------------------*/
+ /* Fetch and display the result set. This will be all of the */
+ /* primary keys in other tables that are referred to by foreign */
+ /* keys in the tableName table. */
+ /*---------------------------------------------------------------------*/
+ while ((retcode == SQL_SUCCESS) || (retcode == SQL_SUCCESS_WITH_INFO))
+ {
+ retcode = SQLFetch(hstmt);
+ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ {
+ GetData( 3, SQL_C_WXCHAR, szPkTable, DB_MAX_TABLE_NAME_LEN+1, &cb);
+ GetData( 5, SQL_C_SSHORT, &iKeySeq, 0, &cb);
+ GetData( 8, SQL_C_WXCHAR, szFkCol, DB_MAX_COLUMN_NAME_LEN+1, &cb);
+ //-------
+ for (i=0; i<noCols; i++) // Find the Column name
+ {
+ if (!wxStrcmp(colInf[i].colName,szFkCol)) // We have found the (Foreign Key) Column
+ {
+ colInf[i].FkCol = iKeySeq; // Which Foreign Key is this (first, second usw.) ?
+ wxStrncpy(colInf[i].FkTableName, szFkTable, DB_MAX_TABLE_NAME_LEN); // Name of the Table where this Foriegn is the Primary Key
+ colInf[i].FkTableName[DB_MAX_TABLE_NAME_LEN] = 0; // Prevent buffer overrun
+ } // if
+ } // for
+ } // if
+ } // while
+ SQLFreeStmt(hstmt, SQL_CLOSE); /* Close the cursor (the hstmt is still allocated). */
+
+ return TRUE;
+
+} // wxDb::GetKeyFields()
+
+
+#if OLD_GETCOLUMNS
+/********** wxDb::GetColumns() **********/
+wxDbColInf *wxDb::GetColumns(wxChar *tableName[], const wxChar *userID)
+/*
+ * 1) The last array element of the tableName[] argument must be zero (null).
+ * This is how the end of the array is detected.
+ * 2) This function returns an array of wxDbColInf structures. If no columns
+ * were found, or an error occurred, this pointer will be zero (null). THE
+ * CALLING FUNCTION IS RESPONSIBLE FOR DELETING THE MEMORY RETURNED WHEN IT
+ * IS FINISHED WITH IT. i.e.
+ *
+ * wxDbColInf *colInf = pDb->GetColumns(tableList, userID);
+ * if (colInf)
+ * {
+ * // Use the column inf
+ * .......
+ * // Destroy the memory
+ * delete [] colInf;
+ * }
+ *
+ * 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.
+ */
+{
+ UWORD noCols = 0;
+ UWORD colNo = 0;
+ wxDbColInf *colInf = 0;
+
+ RETCODE retcode;
+ SQLLEN cb;
+
+ wxString TableName;
+
+ wxString UserID;
+ convertUserID(userID,UserID);
+
+ // 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, wxEmptyString);
+ wxStrcpy(colInf[noCols].colName, wxEmptyString);
+ colInf[noCols].sqlDataType = 0;
+ }
+ // Loop through each table name
+ int tbl;
+ for (tbl = 0; tableName[tbl]; tbl++)
+ {
+ TableName = tableName[tbl];
+ // Oracle and Interbase table names are uppercase only, so force
+ // the name to uppercase just in case programmer forgot to do this
+ if ((Dbms() == dbmsORACLE) ||
+ (Dbms() == dbmsFIREBIRD) ||
+ (Dbms() == dbmsINTERBASE))
+ TableName = TableName.Upper();
+
+ SQLFreeStmt(hstmt, SQL_CLOSE);
+
+ // MySQL, SQLServer, 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 (!UserID.empty() &&
+ Dbms() != dbmsMY_SQL &&
+ Dbms() != dbmsACCESS &&
+ Dbms() != dbmsMS_SQL_SERVER)
+ {
+ retcode = SQLColumns(hstmt,
+ NULL, 0, // All qualifiers
+ WXSQLCAST(UserID), SQL_NTS, // Owner
+ WXSQLCAST(TableName), SQL_NTS,
+ NULL, 0); // All columns
+ }
+ else
+ {
+ retcode = SQLColumns(hstmt,
+ NULL, 0, // All qualifiers
+ NULL, 0, // Owner
+ WXSQLCAST(TableName), SQL_NTS,
+ NULL, 0); // All columns
+ }
+ if (retcode != SQL_SUCCESS)
+ { // Error occurred, abort
+ DispAllErrors(henv, hdbc, hstmt);
+ if (colInf)
+ delete [] colInf;
+ SQLFreeStmt(hstmt, SQL_CLOSE);
+ 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_WXCHAR, (UCHAR*) colInf[colNo].catalog, 128+1, &cb);
+ GetData( 2, SQL_C_WXCHAR, (UCHAR*) colInf[colNo].schema, 128+1, &cb);
+ GetData( 3, SQL_C_WXCHAR, (UCHAR*) colInf[colNo].tableName, DB_MAX_TABLE_NAME_LEN+1, &cb);
+ GetData( 4, SQL_C_WXCHAR, (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_WXCHAR, (UCHAR*) colInf[colNo].typeName, 128+1, &cb);
+ GetData( 7, SQL_C_SLONG, (UCHAR*) &colInf[colNo].columnLength, 0, &cb);
+ GetData( 8, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].bufferSize, 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_WXCHAR, (UCHAR*) colInf[colNo].remarks, 254+1, &cb);
+
+ // Determine the wxDb data type that is used to represent the native data type of this data source
+ colInf[colNo].dbDataType = 0;
+ if (!wxStricmp(typeInfVarchar.TypeName,colInf[colNo].typeName))
+ {
+#ifdef _IODBC_
+ // IODBC does not return a correct columnLength, so we set
+ // columnLength = bufferSize if no column length was returned
+ // IODBC returns the columnLength in bufferSize. (bug)
+ if (colInf[colNo].columnLength < 1)
+ {
+ colInf[colNo].columnLength = colInf[colNo].bufferSize;
+ }
+#endif
+ colInf[colNo].dbDataType = DB_DATA_TYPE_VARCHAR;
+ }
+ else if (!wxStricmp(typeInfInteger.TypeName, colInf[colNo].typeName))
+ colInf[colNo].dbDataType = DB_DATA_TYPE_INTEGER;
+ else if (!wxStricmp(typeInfFloat.TypeName, colInf[colNo].typeName))
+ colInf[colNo].dbDataType = DB_DATA_TYPE_FLOAT;
+ else if (!wxStricmp(typeInfDate.TypeName, colInf[colNo].typeName))
+ colInf[colNo].dbDataType = DB_DATA_TYPE_DATE;
+ else if (!wxStricmp(typeInfBlob.TypeName, colInf[colNo].typeName))
+ colInf[colNo].dbDataType = DB_DATA_TYPE_BLOB;
+ colNo++;
+ }
+ }
+ }
+ if (retcode != SQL_NO_DATA_FOUND)
+ { // Error occurred, abort
+ DispAllErrors(henv, hdbc, hstmt);
+ if (colInf)
+ delete [] colInf;
+ SQLFreeStmt(hstmt, SQL_CLOSE);
+ return(0);
+ }
+ }
+ }
+
+ SQLFreeStmt(hstmt, SQL_CLOSE);
+ return colInf;
+
+} // wxDb::GetColumns()
+
+
+/********** wxDb::GetColumns() **********/
+
+wxDbColInf *wxDb::GetColumns(const wxString &tableName, UWORD *numCols, const wxChar *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.
+
+{
+ UWORD noCols = 0;
+ UWORD colNo = 0;
+ wxDbColInf *colInf = 0;
+
+ RETCODE retcode;
+ SQLLEN cb;
+
+ wxString TableName;
+
+ wxString UserID;
+ convertUserID(userID,UserID);
+
+ // 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, wxEmptyString);
+ wxStrcpy(colInf[noCols].colName, wxEmptyString);
+ colInf[noCols].sqlDataType = 0;
+ }
+
+ TableName = tableName;
+ // Oracle and Interbase table names are uppercase only, so force
+ // the name to uppercase just in case programmer forgot to do this
+ if ((Dbms() == dbmsORACLE) ||
+ (Dbms() == dbmsFIREBIRD) ||
+ (Dbms() == dbmsINTERBASE))
+ TableName = TableName.Upper();
+
+ SQLFreeStmt(hstmt, SQL_CLOSE);
+
+ // MySQL, SQLServer, 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 (!UserID.empty() &&
+ Dbms() != dbmsMY_SQL &&
+ Dbms() != dbmsACCESS &&
+ Dbms() != dbmsMS_SQL_SERVER)
+ {
+ retcode = SQLColumns(hstmt,
+ NULL, 0, // All qualifiers
+ WXSQLCAST(UserID), SQL_NTS, // Owner
+ WXSQLCAST(TableName), SQL_NTS,
+ NULL, 0); // All columns
+ }
+ else
+ {
+ retcode = SQLColumns(hstmt,
+ NULL, 0, // All qualifiers
+ NULL, 0, // Owner
+ WXSQLCAST(TableName), SQL_NTS,
+ NULL, 0); // All columns
+ }
+ if (retcode != SQL_SUCCESS)
+ { // Error occurred, 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_WXCHAR, (UCHAR*) colInf[colNo].catalog, 128+1, &cb);
+ GetData( 2, SQL_C_WXCHAR, (UCHAR*) colInf[colNo].schema, 128+1, &cb);
+ GetData( 3, SQL_C_WXCHAR, (UCHAR*) colInf[colNo].tableName, DB_MAX_TABLE_NAME_LEN+1, &cb);
+ GetData( 4, SQL_C_WXCHAR, (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_WXCHAR, (UCHAR*) colInf[colNo].typeName, 128+1, &cb);
+ GetData( 7, SQL_C_SLONG, (UCHAR*) &colInf[colNo].columnLength, 0, &cb);
+ // BJO 991214 : SQL_C_SSHORT instead of SQL_C_SLONG, otherwise fails on Sparc (probably all 64 bit architectures)
+ GetData( 8, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].bufferSize, 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_WXCHAR, (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
+
+ // BJO 20000428 : Virtuoso returns type names with upper cases!
+ if (Dbms() == dbmsVIRTUOSO)
+ {
+ wxString s = colInf[colNo].typeName;
+ s = s.MakeLower();
+ wxStrcmp(colInf[colNo].typeName, s.c_str());
+ }
+
+ // Determine the wxDb data type that is used to represent the native data type of this data source
+ colInf[colNo].dbDataType = 0;
+ if (!wxStricmp(typeInfVarchar.TypeName, colInf[colNo].typeName))
+ {
+#ifdef _IODBC_
+ // IODBC does not return a correct columnLength, so we set
+ // columnLength = bufferSize if no column length was returned
+ // IODBC returns the columnLength in bufferSize. (bug)
+ if (colInf[colNo].columnLength < 1)
+ {
+ colInf[colNo].columnLength = colInf[colNo].bufferSize;
+ }
+#endif
+
+ colInf[colNo].dbDataType = DB_DATA_TYPE_VARCHAR;
+ }
+ else if (!wxStricmp(typeInfInteger.TypeName, colInf[colNo].typeName))
+ colInf[colNo].dbDataType = DB_DATA_TYPE_INTEGER;
+ else if (!wxStricmp(typeInfFloat.TypeName, colInf[colNo].typeName))
+ colInf[colNo].dbDataType = DB_DATA_TYPE_FLOAT;
+ else if (!wxStricmp(typeInfDate.TypeName, colInf[colNo].typeName))
+ colInf[colNo].dbDataType = DB_DATA_TYPE_DATE;
+ else if (!wxStricmp(typeInfBlob.TypeName, colInf[colNo].typeName))
+ colInf[colNo].dbDataType = DB_DATA_TYPE_BLOB;
+
+ colNo++;
+ }
+ }
+ }
+ if (retcode != SQL_NO_DATA_FOUND)
+ { // Error occurred, 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 Foriegn Keys
+ GetKeyFields(tableName,colInf,noCols);
+
+ if (numCols)
+ *numCols = noCols;
+ return colInf;
+
+} // wxDb::GetColumns()
+
+
+#else // New GetColumns
+
+
+/*
+ BJO 20000503
+ These are tentative new GetColumns members which should be more database
+ independent and which always returns the columns in the order they were
+ created.
+
+ - The first one (wxDbColInf *wxDb::GetColumns(wxChar *tableName[], const
+ wxChar* 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(const wxString &tableName, int *numCols, const
+ wxChar *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
+*/
+
+typedef struct
+{
+ UWORD noCols;
+ wxDbColInf *colInf;
+} _TableColumns;
+
+
+wxDbColInf *wxDb::GetColumns(wxChar *tableName[], const wxChar *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.
+
+ UWORD noCols = 0;
+
+ // How many tables ?
+ int tbl;
+ for (tbl = 0 ; tableName[tbl]; tbl++);
+
+ // Create a table to maintain the columns for each separate table
+ _TableColumns *TableColumns = new _TableColumns[tbl];
+
+ // Fill the table
+ for (i = 0 ; i < tbl ; i++)
+
+ {
+ TableColumns[i].colInf = GetColumns(tableName[i], &TableColumns[i].noCols, userID);
+ if (TableColumns[i].colInf == NULL)
+ return NULL;
+ noCols += TableColumns[i].noCols;
+ }
+
+ // Now merge all the separate table infos
+ wxDbColInf *colInf = new wxDbColInf[noCols+1];
+
+ // Mark the end of the array
+ wxStrcpy(colInf[noCols].tableName, wxEmptyString);
+ wxStrcpy(colInf[noCols].colName, wxEmptyString);
+ colInf[noCols].sqlDataType = 0;
+
+ // Merge ...
+ int offset = 0;
+
+ for (i = 0 ; i < tbl ; i++)
+ {
+ for (j = 0 ; j < TableColumns[i].noCols ; j++)
+ {
+ colInf[offset++] = TableColumns[i].colInf[j];
+ }
+ }
+
+ delete [] TableColumns;
+
+ return colInf;
+} // wxDb::GetColumns() -- NEW
+
+
+wxDbColInf *wxDb::GetColumns(const wxString &tableName, int *numCols, const wxChar *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.
+{
+ UWORD noCols = 0;
+ UWORD colNo = 0;
+ wxDbColInf *colInf = 0;
+
+ RETCODE retcode;
+ SDWORD cb;
+
+ wxString TableName;
+
+ wxString UserID;
+ convertUserID(userID,UserID);
+
+ // 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, wxEmptyString);
+ wxStrcpy(colInf[noCols].colName, wxEmptyString);
+ colInf[noCols].sqlDataType = 0;
+ }
+
+ TableName = tableName;
+ // Oracle and Interbase table names are uppercase only, so force
+ // the name to uppercase just in case programmer forgot to do this
+ if ((Dbms() == dbmsORACLE) ||
+ (Dbms() == dbmsFIREBIRD) ||
+ (Dbms() == dbmsINTERBASE))
+ TableName = TableName.Upper();
+
+ SQLFreeStmt(hstmt, SQL_CLOSE);
+
+ // MySQL, SQLServer, 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 (!UserID.empty() &&
+ Dbms() != dbmsMY_SQL &&
+ Dbms() != dbmsACCESS &&
+ Dbms() != dbmsMS_SQL_SERVER)
+ {
+ 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 occurred, 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_WXCHAR, (UCHAR*) colInf[colNo].catalog, 128+1, &cb);
+ GetData( 2, SQL_C_WXCHAR, (UCHAR*) colInf[colNo].schema, 128+1, &cb);
+ GetData( 3, SQL_C_WXCHAR, (UCHAR*) colInf[colNo].tableName, DB_MAX_TABLE_NAME_LEN+1, &cb);
+ GetData( 4, SQL_C_WXCHAR, (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_WXCHAR, (UCHAR*) colInf[colNo].typeName, 128+1, &cb);
+ GetData( 7, SQL_C_SLONG, (UCHAR*) &colInf[colNo].columnLength, 0, &cb);
+ GetData( 8, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].bufferSize, 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_WXCHAR, (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 does not return a correct columnLength, so we set
+ // columnLength = bufferSize if no column length was returned
+ // IODBC returns the columnLength in bufferSize. (bug)
+ if (colInf[colNo].columnLength < 1)
+ {
+ colInf[colNo].columnLength = colInf[colNo].bufferSize;
+ }