From 049977d042858eaef70b0720ab101b1b40c5e308 Mon Sep 17 00:00:00 2001 From: George Tasker Date: Sat, 3 Feb 2001 17:52:58 +0000 Subject: [PATCH] Massive cleanup of the code. More Unicode support added (though untested). Added TONS of comments Changed code to demonstrate the new wxDbConnectInf class ODBC environment handle now managed via the wxDbConnectInf class Fixed bug where editing a displayed record would not save if the user name was changed. Other minor bug fixes git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@9276 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- samples/db/dbtest.cpp | 334 ++++++++++++++++++++++-------------------- samples/db/dbtest.h | 215 +++++++++++++++++++++------ samples/db/listdb.cpp | 52 ++++--- samples/db/listdb.h | 25 ++-- 4 files changed, 385 insertions(+), 241 deletions(-) diff --git a/samples/db/dbtest.cpp b/samples/db/dbtest.cpp index 5f217eaeb6..9a28a5ddad 100644 --- a/samples/db/dbtest.cpp +++ b/samples/db/dbtest.cpp @@ -51,33 +51,8 @@ extern wxDbList WXDLLEXPORT *PtrBegDbList; /* from db.cpp, used in getting ba IMPLEMENT_APP(DatabaseDemoApp) -extern wxChar ListDB_Selection[]; /* Used to return the first column value for the selected line from the listDB routines */ -extern wxChar ListDB_Selection2[]; /* Used to return the second column value for the selected line from the listDB routines */ - -DatabaseDemoFrame *DemoFrame; /* Pointer to the main frame */ - -/* Pointer to the main database connection used in the program. This - * pointer would normally be used for doing things as database lookups - * for user login names and passwords, getting workstation settings, etc. - * ---> IMPORTANT <--- - * - * For each database object created which uses this wxDb pointer - * connection to the database, when a CommitTrans() or RollBackTrans() - * will commit or rollback EVERY object which uses this wxDb pointer. - * - * To allow each table object (those derived from wxDbTable) to be - * individually committed or rolled back, you MUST use a different - * instance of wxDb in the constructor of the table. Doing so creates - * more overhead, and will use more database connections (some DBs have - * connection limits...), so use connections sparringly. - * - * It is recommended that one "main" database connection be created for - * the entire program to use for READ-ONLY database accesses, but for each - * table object which will do a CommitTrans() or RollbackTrans() that a - * new wxDb object be created and used for it. - */ - -wxDb *READONLY_DB; +extern wxChar ListDB_Selection[]; /* Used to return the first column value for the selected line from the listDB routines */ +extern wxChar ListDB_Selection2[]; /* Used to return the second column value for the selected line from the listDB routines */ const char *GetExtendedDBErrorMsg(wxDb *pDb, char *ErrFile, int ErrLine) { @@ -121,6 +96,8 @@ const char *GetExtendedDBErrorMsg(wxDb *pDb, char *ErrFile, int ErrLine) bool DatabaseDemoApp::OnInit() { + DbConnectInf = NULL; + // Create the main frame window DemoFrame = new DatabaseDemoFrame(NULL, wxT("wxWindows Database Demo"), wxPoint(50, 50), wxSize(537, 480)); @@ -146,13 +123,6 @@ bool DatabaseDemoApp::OnInit() menu_bar->Append(about_menu, wxT("&About")); DemoFrame->SetMenuBar(menu_bar); - // Initialize the ODBC Environment for Database Operations - if (SQLAllocEnv(&DbConnectInf.Henv) != SQL_SUCCESS) - { - wxMessageBox(wxT("A problem occured while trying to get a connection to the data source"),wxT("DB CONNECTION ERROR"),wxOK | wxICON_EXCLAMATION); - return NULL; - } - params.ODBCSource[0] = 0; params.UserName[0] = 0; params.Password[0] = 0; @@ -161,15 +131,57 @@ bool DatabaseDemoApp::OnInit() // Show the frame DemoFrame->Show(TRUE); + ReadParamFile(params); + + // Passing NULL for the SQL environment handle causes + // the wxDbConnectInf constructor to obtain a handle + // for you. + // + // WARNING: Be certain that you do not free this handle + // directly with SQLFreeEnv(). Use either the + // method ::FreeHenv() or delete the DbConnectInf. + DbConnectInf = new wxDbConnectInf(NULL, params.ODBCSource, params.UserName, + params.Password, params.DirPath); + + if (!DbConnectInf || !DbConnectInf->GetHenv()) + { + wxMessageBox(wxT("Unable to define data source connection info."), wxT("DB CONNECTION ERROR..."),wxOK | wxICON_EXCLAMATION); + delete DbConnectInf; + } + + + READONLY_DB = wxDbGetConnection(DbConnectInf); + if (READONLY_DB == 0) + { + wxMessageBox(wxT("Unable to connect to the data source.\n\nCheck the name of your data source to verify it has been correctly entered/spelled.\n\nWith some databases, the user name and password must\nbe created with full rights to the CONTACT table prior to making a connection\n(using tools provided by the database manufacturer)"), wxT("DB CONNECTION ERROR..."),wxOK | wxICON_EXCLAMATION); + DemoFrame->BuildParameterDialog(NULL); + DbConnectInf->SetDsn(""); + DbConnectInf->SetUid(""); + DbConnectInf->SetPassword(""); + wxMessageBox(wxT("Now exiting program.\n\nRestart program to try any new settings."),wxT("Notice..."),wxOK | wxICON_INFORMATION); + return(FALSE); + } + + DemoFrame->BuildEditorDialog(); + + // Show the frame + DemoFrame->Refresh(); + + return TRUE; +} // DatabaseDemoApp::OnInit() + + +bool DatabaseDemoApp::ReadParamFile(Cparameters ¶ms) +{ FILE *paramFile; - if ((paramFile = fopen(paramFilename, wxT("r"))) == NULL) + if ((paramFile = fopen(PARAM_FILENAME, wxT("r"))) == NULL) { wxString tStr; - tStr.Printf(wxT("Unable to open the parameter file '%s' for reading.\n\nYou must specify the data source, user name, and\npassword that will be used and save those settings."),paramFilename); + tStr.Printf(wxT("Unable to open the parameter file '%s' for reading.\n\nYou must specify the data source, user name, and\npassword that will be used and save those settings."),PARAM_FILENAME); wxMessageBox(tStr,wxT("File I/O Error..."),wxOK | wxICON_EXCLAMATION); DemoFrame->BuildParameterDialog(NULL); - if ((paramFile = fopen(paramFilename, wxT("r"))) == NULL) + if ((paramFile = fopen(PARAM_FILENAME, wxT("r"))) == NULL) return FALSE; } @@ -192,31 +204,89 @@ bool DatabaseDemoApp::OnInit() fclose(paramFile); - // Connect to datasource - DbConnectInf.Dsn = params.ODBCSource; // ODBC data source name (created with ODBC Administrator under Win95/NT) - DbConnectInf.Uid = params.UserName; // database username - must already exist in the data source - DbConnectInf.AuthStr = params.Password; // password database username - DbConnectInf.defaultDir = params.DirPath; // path where the table exists (needed for dBase) + return TRUE; +} // DatabaseDemoApp::ReadParamFile() - READONLY_DB = wxDbGetConnection(&DbConnectInf); - if (READONLY_DB == 0) + +bool DatabaseDemoApp::WriteParamFile(Cparameters ¶ms) +{ + FILE *paramFile; + if ((paramFile = fopen(PARAM_FILENAME, wxT("wt"))) == NULL) { - wxMessageBox(wxT("Unable to connect to the data source.\n\nCheck the name of your data source to verify it has been correctly entered/spelled.\n\nWith some databases, the user name and password must\nbe created with full rights to the CONTACT table prior to making a connection\n(using tools provided by the database manufacturer)"), wxT("DB CONNECTION ERROR..."),wxOK | wxICON_EXCLAMATION); - DemoFrame->BuildParameterDialog(NULL); - DbConnectInf.Dsn.Empty(); - DbConnectInf.Uid.Empty(); - DbConnectInf.AuthStr.Empty(); - wxMessageBox(wxT("Now exiting program.\n\nRestart program to try any new settings."),wxT("Notice..."),wxOK | wxICON_INFORMATION); - return(FALSE); + wxString tStr; + tStr.Printf(wxT("Unable to write/overwrite '%s'."),PARAM_FILENAME); + wxMessageBox(tStr,wxT("File I/O Error..."),wxOK | wxICON_EXCLAMATION); + return FALSE; } - DemoFrame->BuildEditorDialog(); - - // Show the frame - DemoFrame->Refresh(); + fputs(wxGetApp().params.ODBCSource, paramFile); + fputc(wxT('\n'), paramFile); + fputs(wxGetApp().params.UserName, paramFile); + fputc(wxT('\n'), paramFile); + fputs(wxGetApp().params.Password, paramFile); + fputc(wxT('\n'), paramFile); + fputs(wxGetApp().params.DirPath, paramFile); + fputc(wxT('\n'), paramFile); + fclose(paramFile); return TRUE; -} // DatabaseDemoApp::OnInit() +} // DatabaseDemoApp::WriteParamFile() + + +void DatabaseDemoApp::CreateDataTable(bool recreate) +{ + bool Ok = TRUE; + if (recreate) + Ok = (wxMessageBox(wxT("Any data currently residing in the table will be erased.\n\nAre you sure?"),wxT("Confirm"),wxYES_NO|wxICON_QUESTION) == wxYES); + + if (!Ok) + return; + + wxBeginBusyCursor(); + + bool success = TRUE; + + // Use a temporary instance of a new Ccontact table object + // for creating the table within the datasource. + Ccontact *Contact = new Ccontact(); + + if (!Contact) + { + wxEndBusyCursor(); + wxMessageBox(wxT("Error allocating memory for 'Ccontact'object.\n\nTable was not created."),wxT("Error..."),wxOK | wxICON_EXCLAMATION); + return; + } + + if (!Contact->CreateTable(recreate)) + { + wxEndBusyCursor(); + wxString tStr; + tStr = wxT("Error creating CONTACTS table.\nTable was not created.\n\n"); + tStr += GetExtendedDBErrorMsg(Contact->GetDb(),__FILE__,__LINE__); + wxMessageBox(tStr,wxT("ODBC Error..."),wxOK | wxICON_EXCLAMATION); + success = FALSE; + } + else + { + if (!Contact->CreateIndexes()) + { + wxEndBusyCursor(); + wxString tStr; + tStr = wxT("Error creating CONTACTS indexes.\nIndexes will be unavailable.\n\n"); + tStr += GetExtendedDBErrorMsg(Contact->GetDb(),__FILE__,__LINE__); + wxMessageBox(tStr,wxT("ODBC Error..."),wxOK | wxICON_EXCLAMATION); + success = FALSE; + } + } + while (wxIsBusy()) + wxEndBusyCursor(); + + delete Contact; + Contact = NULL; + + if (success) + wxMessageBox(wxT("Table and index(es) were successfully created."),wxT("Notice..."),wxOK | wxICON_INFORMATION); +} // DatabaseDemoApp::CreateDataTable() BEGIN_EVENT_TABLE(DatabaseDemoFrame, wxFrame) @@ -243,13 +313,13 @@ DatabaseDemoFrame::DatabaseDemoFrame(wxFrame *frame, const wxString& title, void DatabaseDemoFrame::OnCreate(wxCommandEvent& event) { - CreateDataTable(FALSE); + wxGetApp().CreateDataTable(FALSE); } // DatabaseDemoFrame::OnCreate() void DatabaseDemoFrame::OnRecreateTable(wxCommandEvent& event) { - CreateDataTable(TRUE); + wxGetApp().CreateDataTable(TRUE); } // DatabaseDemoFrame::OnRecreate() @@ -300,11 +370,10 @@ void DatabaseDemoFrame::OnAbout(wxCommandEvent& event) } // DatabaseDemoFrame::OnAbout() +// Put any additional checking necessary to make certain it is alright +// to close the program here that is not done elsewhere void DatabaseDemoFrame::OnCloseWindow(wxCloseEvent& event) { - // Put any additional checking necessary to make certain it is alright - // to close the program here that is not done elsewhere - // Clean up time if (pEditorDlg && pEditorDlg->Close()) pEditorDlg = NULL; @@ -321,67 +390,17 @@ void DatabaseDemoFrame::OnCloseWindow(wxCloseEvent& event) // previously cached. wxDbCloseConnections(); - // Cleans up the environment space allocated for the SQL/ODBC connection handle - SQLFreeEnv(DbConnectInf.Henv); + // Deletion of the wxDbConnectInf instance must be the LAST thing done that + // has anything to do with the database. Deleting this before disconnecting, + // freeing/closing connections, etc will result in a crash! + delete wxGetApp().DbConnectInf; + wxGetApp().DbConnectInf = NULL; this->Destroy(); } // DatabaseDemoFrame::OnCloseWindow() -void DatabaseDemoFrame::CreateDataTable(bool recreate) -{ - bool Ok = TRUE; - if (recreate) - Ok = (wxMessageBox(wxT("Any data currently residing in the table will be erased.\n\nAre you sure?"),wxT("Confirm"),wxYES_NO|wxICON_QUESTION) == wxYES); - - if (!Ok) - return; - - wxBeginBusyCursor(); - - bool success = TRUE; - - Ccontact *Contact = new Ccontact(); - if (!Contact) - { - wxEndBusyCursor(); - wxMessageBox(wxT("Error allocating memory for 'Ccontact'object.\n\nTable was not created."),wxT("Error..."),wxOK | wxICON_EXCLAMATION); - return; - } - - if (!Contact->CreateTable(recreate)) - { - wxEndBusyCursor(); - wxString tStr; - tStr = wxT("Error creating CONTACTS table.\nTable was not created.\n\n"); - tStr += GetExtendedDBErrorMsg(Contact->GetDb(),__FILE__,__LINE__); - wxMessageBox(tStr,wxT("ODBC Error..."),wxOK | wxICON_EXCLAMATION); - success = FALSE; - } - else - { - if (!Contact->CreateIndexes()) - { - wxEndBusyCursor(); - wxString tStr; - tStr = wxT("Error creating CONTACTS indexes.\nIndexes will be unavailable.\n\n"); - tStr += GetExtendedDBErrorMsg(Contact->GetDb(),__FILE__,__LINE__); - wxMessageBox(tStr,wxT("ODBC Error..."),wxOK | wxICON_EXCLAMATION); - success = FALSE; - } - } - while (wxIsBusy()) - wxEndBusyCursor(); - - delete Contact; - Contact = NULL; - - if (success) - wxMessageBox(wxT("Table and index(es) were successfully created."),wxT("Notice..."),wxOK | wxICON_INFORMATION); -} // DatabaseDemoFrame::CreateDataTable() - - void DatabaseDemoFrame::BuildEditorDialog() { pEditorDlg = NULL; @@ -394,13 +413,13 @@ void DatabaseDemoFrame::BuildEditorDialog() pEditorDlg->Close(); pEditorDlg = NULL; wxMessageBox(wxT("Unable to initialize the editor dialog for some reason"),wxT("Error..."),wxOK | wxICON_EXCLAMATION); - DemoFrame->Close(); + Close(); } } else { wxMessageBox(wxT("Unable to create the editor dialog for some reason"),wxT("Error..."),wxOK | wxICON_EXCLAMATION); - DemoFrame->Close(); + Close(); } } // DatabaseDemoFrame::BuildEditorDialog() @@ -426,7 +445,9 @@ void DatabaseDemoFrame::BuildParameterDialog(wxWindow *parent) * or creating a table objects which use the same pDb, know that all the objects * will be committed or rolled back when any of the objects has this function call made. */ -Ccontact::Ccontact (wxDb *pwxDb) : wxDbTable(pwxDb ? pwxDb : wxDbGetConnection(&DbConnectInf), CONTACT_TABLE_NAME,CONTACT_NO_COLS, wxT(""), !wxDB_QUERY_ONLY, DbConnectInf.defaultDir) +Ccontact::Ccontact (wxDb *pwxDb) : wxDbTable(pwxDb ? pwxDb : wxDbGetConnection(wxGetApp().DbConnectInf), + CONTACT_TABLE_NAME, CONTACT_NO_COLS, wxT(""), + !wxDB_QUERY_ONLY, wxGetApp().DbConnectInf->GetDefaultDir()) { // This is used to represent whether the database connection should be released // when this instance of the object is deleted. If using the same connection @@ -529,7 +550,7 @@ bool Ccontact::CreateIndexes(void) * very efficient and tighter coding so that it is available where ever the object * is. Great for use with multiple tables when not using views or outer joins */ -bool Ccontact::FetchByName(wxChar *name) +bool Ccontact::FetchByName(const wxString &name) { whereStr.Printf(wxT("NAME = '%s'"),name); SetWhereClause(whereStr.c_str()); @@ -705,7 +726,7 @@ void CeditorDlg::OnCommand(wxWindow& win, wxCommandEvent& event) else { // Requery previous record - if (Contact->FetchByName((wxChar*) (const wxChar*) saveName)) + if (Contact->FetchByName(saveName)) { PutData(); SetMode(mView); @@ -821,9 +842,9 @@ void CeditorDlg::OnCommand(wxWindow& win, wxCommandEvent& event) if (Contact->GetDb()->Dbms() != dbmsPOSTGRES && Contact->GetDb()->Dbms() != dbmsMY_SQL) { - Contact->whereStr = wxT("NAME = (SELECT MIN(NAME) FROM "); - Contact->whereStr += CONTACT_TABLE_NAME; - Contact->whereStr += wxT(")"); + Contact->whereStr = wxT("NAME = (SELECT MIN(NAME) FROM "); + Contact->whereStr += CONTACT_TABLE_NAME; + Contact->whereStr += wxT(")"); } Contact->SetWhereClause(Contact->whereStr.c_str()); @@ -846,21 +867,23 @@ void CeditorDlg::OnCommand(wxWindow& win, wxCommandEvent& event) if (widgetName == pNameListBtn->GetName()) { - new ClookUpDlg(/* wxWindow *parent */ this, + new ClookUpDlg(/* wxWindow *parent */ this, /* wxChar *windowTitle */ wxT("Select contact name"), /* wxChar *tableName */ (wxChar *) CONTACT_TABLE_NAME, /* wxChar *dispCol1 */ wxT("NAME"), /* wxChar *dispCol2 */ wxT("JOINDATE"), /* wxChar *where */ wxT(""), /* wxChar *orderBy */ wxT("NAME"), - /* bool distinctValues */ TRUE); + /* wxDb *pDb */ wxGetApp().READONLY_DB, + /* const wxString &defDir */ wxGetApp().DbConnectInf->GetDefaultDir(), + /* bool distinctValues */ TRUE); if (ListDB_Selection && wxStrlen(ListDB_Selection)) { wxString w = wxT("NAME = '"); w += ListDB_Selection; w += wxT("'"); - GetRec((wxChar*) (const wxChar*) w); + GetRec(w); } return; @@ -883,7 +906,9 @@ bool CeditorDlg::Initialize() // Check if the table exists or not. If it doesn't, ask the user if they want to // create the table. Continue trying to create the table until it exists, or user aborts - while (!Contact->GetDb()->TableExists((wxChar *)CONTACT_TABLE_NAME,DbConnectInf.Uid,DbConnectInf.defaultDir)) + while (!Contact->GetDb()->TableExists((wxChar *)CONTACT_TABLE_NAME, + wxGetApp().DbConnectInf->GetUserID(), + wxGetApp().DbConnectInf->GetDefaultDir())) { wxString tStr; tStr.Printf(wxT("Unable to open the table '%s'.\n\nTable may need to be created...?\n\n"),CONTACT_TABLE_NAME); @@ -898,7 +923,7 @@ bool CeditorDlg::Initialize() return FALSE; } else - DemoFrame->CreateDataTable(TRUE); + wxGetApp().CreateDataTable(TRUE); } // Tables must be "opened" before anything other than creating/deleting table can be done @@ -912,7 +937,7 @@ bool CeditorDlg::Initialize() // in the 2.4 release. This check will determine whether the open failing was due // to the table not existing, or the users privileges being insufficient to // open the table. - if (!Contact->GetDb()->TablePrivileges(CONTACT_TABLE_NAME,wxT("SELECT"),Contact->GetDb()->GetUsername(),Contact->GetDb()->GetUsername(),DbConnectInf.defaultDir)) + if (!Contact->GetDb()->TablePrivileges(CONTACT_TABLE_NAME,wxT("SELECT"),Contact->GetDb()->GetUsername(),Contact->GetDb()->GetUsername(),DbConnectInf->GetDefaultDir())) { wxString tStr; tStr.Printf(wxT("Unable to open the table '%s'.\n\n"),CONTACT_TABLE_NAME); @@ -921,7 +946,8 @@ bool CeditorDlg::Initialize() } else #endif - if (Contact->GetDb()->TableExists(CONTACT_TABLE_NAME,Contact->GetDb()->GetUsername(),DbConnectInf.defaultDir)) + if (Contact->GetDb()->TableExists(CONTACT_TABLE_NAME, Contact->GetDb()->GetUsername(), + wxGetApp().DbConnectInf->GetDefaultDir())) { wxString tStr; tStr.Printf(wxT("Unable to open the table '%s'.\n\n"),CONTACT_TABLE_NAME); @@ -1004,9 +1030,9 @@ bool CeditorDlg::Initialize() if (Contact->GetDb()->Dbms() != dbmsPOSTGRES && Contact->GetDb()->Dbms() != dbmsMY_SQL) { - Contact->whereStr.sprintf(wxT("NAME = (SELECT MIN(NAME) FROM %s)"),Contact->GetTableName()); + Contact->whereStr.Printf(wxT("NAME = (SELECT MIN(NAME) FROM %s)"),Contact->GetTableName()); // NOTE: (const wxChar*) returns a pointer which may not be valid later, so this is short term use only - Contact->SetWhereClause(Contact->whereStr.c_str()); + Contact->SetWhereClause(Contact->whereStr); } else Contact->SetWhereClause(wxT("")); @@ -1147,7 +1173,7 @@ bool CeditorDlg::GetData() return FALSE; } - bool invalid = FALSE; + bool invalid = FALSE; int mm,dd,yyyy; int first, second; @@ -1281,7 +1307,8 @@ bool CeditorDlg::Save() } else // mode == mEdit { - if (!Contact->Update()) + Contact->whereStr.Printf("NAME = '%s'",saveName.c_str()); + if (!Contact->UpdateWhere(Contact->whereStr)) { wxString tStr; tStr = wxT("Database update failed\n\n"); @@ -1336,7 +1363,7 @@ bool CeditorDlg::GetNextRec() } w += wxT(")"); - return(GetRec((wxChar*) (const wxChar*) w)); + return(GetRec(w)); } // CeditorDlg::GetNextRec() @@ -1372,7 +1399,7 @@ bool CeditorDlg::GetPrevRec() w += wxT(")"); - return(GetRec((wxChar*) (const wxChar*)w)); + return(GetRec(w)); } // CeditorDlg::GetPrevRec() @@ -1381,7 +1408,7 @@ bool CeditorDlg::GetPrevRec() * This function is here to avoid duplicating this same code in both the * GetPrevRec() and GetNextRec() functions */ -bool CeditorDlg::GetRec(wxChar *whereStr) +bool CeditorDlg::GetRec(const wxString &whereStr) { Contact->SetWhereClause(whereStr); Contact->SetOrderByClause(wxT("NAME")); @@ -1586,31 +1613,15 @@ bool CparameterDlg::GetData() bool CparameterDlg::Save() { - Cparameters saveParams = wxGetApp().params; + // Copy the current params in case user cancels changing + // the params, so that we can reset them. if (!GetData()) { - wxGetApp().params = saveParams; - return FALSE; - } - - FILE *paramFile; - if ((paramFile = fopen(paramFilename, wxT("wt"))) == NULL) - { - wxString tStr; - tStr.Printf(wxT("Unable to write/overwrite '%s'."),paramFilename); - wxMessageBox(tStr,wxT("File I/O Error..."),wxOK | wxICON_EXCLAMATION); + wxGetApp().params = savedParamSettings; return FALSE; } - fputs(wxGetApp().params.ODBCSource, paramFile); - fputc(wxT('\n'), paramFile); - fputs(wxGetApp().params.UserName, paramFile); - fputc(wxT('\n'), paramFile); - fputs(wxGetApp().params.Password, paramFile); - fputc(wxT('\n'), paramFile); - fputs(wxGetApp().params.DirPath, paramFile); - fputc(wxT('\n'), paramFile); - fclose(paramFile); + wxGetApp().WriteParamFile(wxGetApp().params); return TRUE; } // CparameterDlg::Save() @@ -1622,7 +1633,8 @@ void CparameterDlg::FillDataSourceList() wxChar DsDesc[255]; wxStringList strList; - while (wxDbGetDataSource(DbConnectInf.Henv, Dsn, SQL_MAX_DSN_LENGTH+1, DsDesc, 255)) + while (wxDbGetDataSource(wxGetApp().DbConnectInf->GetHenv(), Dsn, + SQL_MAX_DSN_LENGTH+1, DsDesc, 255)) strList.Add(Dsn); strList.Sort(); @@ -2112,7 +2124,9 @@ void CqueryDlg::ProcessCountBtn() if (dbTable == 0) // wxDbTable object needs to be created and opened { - if (!(dbTable = new wxDbTable(pDB, masterTableName, 0, wxT(""), !wxDB_QUERY_ONLY, DbConnectInf.defaultDir))) + if (!(dbTable = new wxDbTable(pDB, masterTableName, 0, wxT(""), + !wxDB_QUERY_ONLY, + wxGetApp().DbConnectInf->GetDefaultDir()))) { wxMessageBox(wxT("Memory allocation failed creating a wxDbTable object."),wxT("Error..."),wxOK | wxICON_EXCLAMATION); return; diff --git a/samples/db/dbtest.h b/samples/db/dbtest.h index d840502770..59d56bbdf0 100644 --- a/samples/db/dbtest.h +++ b/samples/db/dbtest.h @@ -29,17 +29,21 @@ enum DialogModes {mView,mCreate,mEdit,mSearch}; // this seems to be missing, Robert Roebling (?) #ifndef MAX_PATH -#define MAX_PATH 200 + #if defined(__WXMAC__) + #define MAX_PATH 260 /* max. length of full pathname */ + #else /* _MAC */ + #define MAX_PATH 256 /* max. length of full pathname */ + #endif #endif // Name of the table to be created/opened -const wxChar CONTACT_TABLE_NAME[] = "contacts"; +const wxChar CONTACT_TABLE_NAME[] = "contacts"; -// Nuber of columns in the above table -const int CONTACT_NO_COLS = 12; // 0-11 +// Number of columns in the CONTACT table +const int CONTACT_NO_COLS = 12; // 0-11 + +const wxChar PARAM_FILENAME[] = "dbtest.cfg"; -// Global structure for holding ODBC connection information -struct wxDbConnectInf DbConnectInf; enum Language {langENGLISH, langFRENCH, langGERMAN, langSPANISH, langOTHER}; @@ -47,26 +51,23 @@ enum Language {langENGLISH, langFRENCH, langGERMAN, langSPANISH, langOTHER}; class CeditorDlg; class CparameterDlg; -const wxChar paramFilename[] = "dbtest.cfg"; - - -/* - * This class contains the actual data members that are used for transferring - * data back and forth from the database to the program. - * - * NOTE: The object described in this class is just for example purposes, and has no - * real meaning other than to show each type of field being used by the database - */ +// +// This class contains the actual data members that are used for transferring +// data back and forth from the database to the program. +// +// NOTE: The object described in this class is just for example purposes, and has no +// real meaning other than to show each type of field being used by the database +// class CstructContact : public wxObject { public: - wxChar Name[50+1]; // Contact's name - wxChar Addr1[50+1]; - wxChar Addr2[50+1]; - wxChar City[25+1]; - wxChar State[25+1]; - wxChar PostalCode[15+1]; - wxChar Country[20+1]; + wxChar Name[50+1]; // Contact's name + wxChar Addr1[50+1]; + wxChar Addr2[50+1]; + wxChar City[25+1]; + wxChar State[25+1]; + wxChar PostalCode[15+1]; + wxChar Country[20+1]; TIMESTAMP_STRUCT JoinDate; // Date on which this person joined the wxWindows project Language NativeLanguage; // Enumerated type indicating person's native language bool IsDeveloper; // Is this person a developer for wxWindows, or just a subscriber @@ -76,49 +77,62 @@ class CstructContact : public wxObject // -// NOTE: Ccontact inherits wxDbTable, which gives access to all the database functionality +// The Ccontact class derives from wxDbTable, so we have access to all +// of the database table functions and the local memory variables that +// the database classes will store the data into (and read the data from) +// all combined in this one class. // class Ccontact : public wxDbTable, public CstructContact { private: + // Used to keep track of whether this class had a wxDb instance + // passed in to it or not. If an existing wxDb instance was not + // passed in at Ccontact creation time, then when the Ccontact + // instance is deleted, the connection will be freed as Ccontact + // created its own connection when it was created. bool freeDbConn; + + // Calls wxDbTable::SetColDefs() once for each column that is + // to be associated with some member variable for use with + // this database object. void SetupColumns(); public: + // Used in places where we need to construct a WHERE clause to + // be passed to the SetWhereClause() function. From example, + // where building the WHERE clause requires using ::Printf() + // to build the string. wxString whereStr; - wxString qryWhereStr; // Where string returned from the query dialog + + // WHERE string returned from the query dialog + wxString qryWhereStr; Ccontact(wxDb *pwxDb=NULL); ~Ccontact(); void Initialize(); + + // Contains all the index definitions and calls to wxDbTable::CreateIndex() + // required to create all the indexes we wish to define for this table. bool CreateIndexes(void); - bool FetchByName(wxChar *name); + + // Since we do not wish to have duplicate code blocks all over our program + // for a common query/fetch that we will need to do in many places, we + // include this member function that does it all for us in one place. + bool FetchByName(const wxString &name); }; // Ccontact class definition typedef struct Cparameters { - // The length of these strings were arbitrarily picked, and are - // dependent on the OS and database engine you will be using. - wxChar ODBCSource[100+1]; - wxChar UserName[25+1]; - wxChar Password[25+1]; + wxChar ODBCSource[SQL_MAX_DSN_LENGTH+1]; + wxChar UserName[SQL_MAX_USER_NAME_LEN+1]; + wxChar Password[SQL_MAX_AUTHSTR_LEN+1]; wxChar DirPath[MAX_PATH+1]; } Cparameters; -// Define a new application type -class DatabaseDemoApp: public wxApp -{ - public: - Cparameters params; - bool OnInit(); -}; // DatabaseDemoApp - -DECLARE_APP(DatabaseDemoApp) - // Define a new frame type class DatabaseDemoFrame: public wxFrame { @@ -137,7 +151,6 @@ class DatabaseDemoFrame: public wxFrame void OnEditParameters(wxCommandEvent& event); void OnAbout(wxCommandEvent& event); - void CreateDataTable(bool recreate); void BuildEditorDialog(); void BuildParameterDialog(wxWindow *parent); @@ -145,13 +158,73 @@ DECLARE_EVENT_TABLE() }; // DatabaseDemoFrame +// Define a new application type +class DatabaseDemoApp: public wxApp +{ + public: + // These are the parameters that are stored in the "PARAM_FILENAME" file + // that are read in at startup of the program that indicate the connection + // parameters to be used for connecting to the ODBC data source. + Cparameters params; + + // Pointer to the main frame used by the App + DatabaseDemoFrame *DemoFrame; + + // Pointer to the main database connection used in the program. This + // pointer would normally be used for doing things as database lookups + // for user login names and passwords, getting workstation settings, etc. + // + // ---> IMPORTANT <--- + // + // For each database object created which uses this wxDb pointer + // connection to the database, when a CommitTrans() or RollBackTrans() + // will commit or rollback EVERY object which uses this wxDb pointer. + // + // To allow each table object (those derived from wxDbTable) to be + // individually committed or rolled back, you MUST use a different + // instance of wxDb in the constructor of the table. Doing so creates + // more overhead, and will use more database connections (some DBs have + // connection limits...), so use connections sparringly. + // + // It is recommended that one "main" database connection be created for + // the entire program to use for READ-ONLY database accesses, but for each + // table object which will do a CommitTrans() or RollbackTrans() that a + // new wxDb object be created and used for it. + wxDb *READONLY_DB; + + // Contains the ODBC connection information used by + // all database connections + wxDbConnectInf *DbConnectInf; + + bool OnInit(); + + // Read/Write ODBC connection parameters to the "PARAM_FILENAME" file + bool ReadParamFile(Cparameters ¶ms); + bool WriteParamFile(Cparameters ¶ms); + + void CreateDataTable(bool recreate); +}; // DatabaseDemoApp + + +DECLARE_APP(DatabaseDemoApp) + // *************************** CeditorDlg *************************** class CeditorDlg : public wxPanel { private: + // Used to indicate whether all of the widget pointers (defined + // below) have been initialized to point to the memory for + // the named widget. Used as a safeguard from using the widget + // before it has been initialized. bool widgetPtrsSet; + + // Used when the EDIT button has been pressed to maintain the + // original name that was displayed in the editor before the + // EDIT button was pressed, so that if CANCEL is pressed, a + // FetchByName() can be done to retrieve the original data + // to repopulate the dialog. wxString saveName; // Pointers to all widgets on the dialog @@ -167,9 +240,16 @@ class CeditorDlg : public wxPanel wxStaticText *pNativeLangMsg; public: + // Indicates if the editor dialog has been initialized yet (used to + // help trap if the Initialize() function failed to load all required + // resources or not. bool initialized; + enum DialogModes mode; - Ccontact *Contact; // this is the table object that will be being manipulated + + // Pointer to the wxDbTable instance that is used to manipulate + // the data in memory and in the database + Ccontact *Contact; CeditorDlg(wxWindow *parent); @@ -179,14 +259,32 @@ class CeditorDlg : public wxPanel void OnActivate(bool) {}; // necessary for hot keys bool Initialize(); + + // Sets wxStaticText fields to be editable or not depending + // on the current value of 'mode' void FieldsEditable(); + + // Sets the editor mode, determining what state widgets + // on the dialog are to be in based on the operation + // being performed. void SetMode(enum DialogModes m); + + // Update/Retrieve data from the widgets on the dialog bool PutData(); bool GetData(); + + // Inserts/updates the database with the current data + // retrieved from the editor dialog bool Save(); + + // Database functions for changing the data that is to + // be displayed on the dialog. GetNextRec()/GetPrevRec() + // provide database independent methods that do not require + // backward scrolling cursors to obtain the record that + // is prior to the current record in the search sequence. bool GetNextRec(); bool GetPrevRec(); - bool GetRec(wxChar *whereStr); + bool GetRec(const wxString &whereStr); DECLARE_EVENT_TABLE() }; // CeditorDlg @@ -238,9 +336,19 @@ DECLARE_EVENT_TABLE() class CparameterDlg : public wxDialog { private: + // Used to indicate whether all of the widget pointers (defined + // below) have been initialized to point to the memory for + // the named widget. Used as a safeguard from using the widget + // before it has been initialized. bool widgetPtrsSet; + enum DialogModes mode; + + // Have the parameters been saved yet, or do they + // need to be saved to update the params on disk bool saved; + + // Original params Cparameters savedParamSettings; // Pointers to all widgets on the dialog @@ -258,9 +366,16 @@ class CparameterDlg : public wxDialog void OnCommand(wxWindow& win, wxCommandEvent& event); void OnActivate(bool) {}; // necessary for hot keys + // Update/Retrieve data from the widgets on the dialog bool PutData(); bool GetData(); + + // Stores the defined parameter for connecting to the selected ODBC + // data source to the config file name in "PARAM_FILENAME" bool Save(); + + // Populates the 'pParamODBCSourceList' listbox with the names of all + // ODBC datasource defined for use at the current workstation void FillDataSourceList(); DECLARE_EVENT_TABLE() @@ -313,13 +428,17 @@ wxChar * const langQRY_BETWEEN = "column BETWEEN value AND value"; class CqueryDlg : public wxDialog { private: - wxDbColInf *colInf; // Column inf. returned by db->GetColumns() - wxDbTable *dbTable; - wxChar *masterTableName; - wxChar *pWhere; // A pointer to the storage for the resulting where clause + wxDbColInf *colInf; // Column inf. returned by db->GetColumns() + wxDbTable *dbTable; // generic wxDbTable object for attaching to the table to query + wxChar *masterTableName; // Name of the table that 'dbTable' will be associated with + wxChar *pWhere; // A pointer to the storage for the resulting where clause wxDb *pDB; public: + // Used to indicate whether all of the widget pointers (defined + // below) have been initialized to point to the memory for + // the named widget. Used as a safeguard from using the widget + // before it has been initialized. bool widgetPtrsSet; // Widget pointers diff --git a/samples/db/listdb.cpp b/samples/db/listdb.cpp index 7e6ff1b6c4..e5959ab255 100644 --- a/samples/db/listdb.cpp +++ b/samples/db/listdb.cpp @@ -59,13 +59,7 @@ extern wxDbList WXDLLEXPORT *PtrBegDbList; /* from db.cpp, used in getting back error results from db connections */ #include "listdb.h" - -// Global structure for holding ODBC connection information -extern wxDbConnectInf DbConnectInf; - -// Global database connection -extern wxDb *READONLY_DB; - +//#include "dbtest.h" // Used for passing the selected listbox selection back to the calling // routine. This variable must be declared as 'extern' in the calling @@ -80,6 +74,8 @@ wxChar ListDB_Selection2[LOOKUP_COL_LEN+1]; const int LISTDB_NO_SPACES_BETWEEN_COLS = 3; +extern wxApp *DatabaseDemoApp; + /* * This function will return the exact string(s) from the database engine @@ -143,8 +139,9 @@ const wxChar *GetExtendedDBErrorMsg2(wxChar *ErrFile, int ErrLine) // Clookup constructor -Clookup::Clookup(wxChar *tblName, wxChar *colName) : - wxDbTable(READONLY_DB, tblName, 1, wxT(""), !wxDB_QUERY_ONLY, DbConnectInf.defaultDir) +Clookup::Clookup(wxChar *tblName, wxChar *colName, wxDb *pDb, const wxString &defDir) + : wxDbTable(pDb, tblName, 1, wxT(""), !wxDB_QUERY_ONLY, + defDir) { SetColDefs (0, colName, DB_DATA_TYPE_VARCHAR, lookupCol, SQL_C_CHAR, LOOKUP_COL_LEN+1, FALSE, FALSE); @@ -153,9 +150,16 @@ Clookup::Clookup(wxChar *tblName, wxChar *colName) : // Clookup2 constructor -Clookup2::Clookup2(wxChar *tblName, wxChar *colName1, wxChar *colName2, wxDb *pDb) - : wxDbTable(pDb, tblName, (1 + (wxStrlen(colName2) > 0)), NULL, !wxDB_QUERY_ONLY, DbConnectInf.defaultDir) +Clookup2::Clookup2(wxChar *tblName, wxChar *colName1, wxChar *colName2, + wxDb *pDb, const wxString &defDir) + : wxDbTable(pDb, tblName, (1 + (wxStrlen(colName2) > 0)), wxT(""), + !wxDB_QUERY_ONLY, defDir) { + wxASSERT(pDb); + wxASSERT(tblName); + wxASSERT(colName1); + wxASSERT(colName2); + int i = 0; SetColDefs (i, colName1, DB_DATA_TYPE_VARCHAR, lookupCol1, SQL_C_CHAR, LOOKUP_COL_LEN+1, FALSE, FALSE); @@ -167,15 +171,17 @@ Clookup2::Clookup2(wxChar *tblName, wxChar *colName1, wxChar *colName2, wxDb *pD BEGIN_EVENT_TABLE(ClookUpDlg, wxDialog) -// EVT_LISTBOX(LOOKUP_DIALOG_SELECT, ClookUpDlg::SelectCallback) EVT_BUTTON(LOOKUP_DIALOG_OK, ClookUpDlg::OnButton) EVT_BUTTON(LOOKUP_DIALOG_CANCEL, ClookUpDlg::OnButton) EVT_CLOSE(ClookUpDlg::OnClose) END_EVENT_TABLE() + // This is a generic lookup constructor that will work with any table and any column -ClookUpDlg::ClookUpDlg(wxWindow *parent, wxChar *windowTitle, wxChar *tableName, wxChar *colName, - wxChar *where, wxChar *orderBy) : wxDialog (parent, LOOKUP_DIALOG, wxT("Select..."), wxPoint(-1, -1), wxSize(400, 290)) +ClookUpDlg::ClookUpDlg(wxWindow *parent, wxChar *windowTitle, wxChar *tableName, + wxChar *colName, wxChar *where, wxChar *orderBy, + wxDb *pDb, const wxString &defDir) + : wxDialog (parent, LOOKUP_DIALOG, wxT("Select..."), wxPoint(-1, -1), wxSize(400, 290)) { wxBeginBusyCursor(); @@ -186,14 +192,14 @@ ClookUpDlg::ClookUpDlg(wxWindow *parent, wxChar *windowTitle, wxChar *tableName, noDisplayCols = 1; col1Len = 0; - pLookUpSelectList = new wxListBox(this, LOOKUP_DIALOG_SELECT, wxPoint( 5, 15), wxSize(384, 195), 0, 0, wxLB_SINGLE|wxLB_ALWAYS_SB, wxDefaultValidator, wxT("LookUpSelectList")); - pLookUpOkBtn = new wxButton(this, LOOKUP_DIALOG_OK, wxT("&Ok"), wxPoint(113, 222), wxSize( 70, 35), 0, wxDefaultValidator, wxT("LookUpOkBtn")); - pLookUpCancelBtn = new wxButton(this, LOOKUP_DIALOG_CANCEL, wxT("C&ancel"), wxPoint(212, 222), wxSize( 70, 35), 0, wxDefaultValidator, wxT("LookUpCancelBtn")); + pLookUpSelectList = new wxListBox(this, LOOKUP_DIALOG_SELECT, wxPoint( 5, 15), wxSize(384, 195), 0, 0, wxLB_SINGLE|wxLB_ALWAYS_SB, wxDefaultValidator, wxT("LookUpSelectList")); + pLookUpOkBtn = new wxButton(this, LOOKUP_DIALOG_OK, wxT("&Ok"), wxPoint(113, 222), wxSize( 70, 35), 0, wxDefaultValidator, wxT("LookUpOkBtn")); + pLookUpCancelBtn = new wxButton(this, LOOKUP_DIALOG_CANCEL, wxT("C&ancel"), wxPoint(212, 222), wxSize( 70, 35), 0, wxDefaultValidator, wxT("LookUpCancelBtn")); widgetPtrsSet = TRUE; // Query the lookup table and display the result set - if (!(lookup = new Clookup(tableName, colName))) + if (!(lookup = new Clookup(tableName, colName, pDb, defDir))) { wxMessageBox(wxT("Error allocating memory for 'Clookup'object."),wxT("Error...")); Close(); @@ -263,13 +269,15 @@ ClookUpDlg::ClookUpDlg(wxWindow *parent, wxChar *windowTitle, wxChar *tableName, // since it cannot be derived when you query using your own sql statement. // // The optional database connection can be used if you'd like the lookup class -// to use a database pointer other than the global READONLY_DB. This is necessary if +// to use a database pointer other than wxGetApp().READONLY_DB. This is necessary if // records are being saved, but not committed to the db, yet should be included // in the lookup window. // ClookUpDlg::ClookUpDlg(wxWindow *parent, wxChar *windowTitle, wxChar *tableName, - wxChar *dispCol1, wxChar *dispCol2, wxChar *where, wxChar *orderBy, bool distinctValues, - wxChar *selectStmt, int maxLenCol1, wxDb *pDb, bool allowOk) : wxDialog (parent, LOOKUP_DIALOG, wxT("Select..."), wxPoint(-1, -1), wxSize(400, 290)) + wxChar *dispCol1, wxChar *dispCol2, wxChar *where, wxChar *orderBy, + wxDb *pDb, const wxString &defDir, bool distinctValues, + wxChar *selectStmt, int maxLenCol1, bool allowOk) + : wxDialog (parent, LOOKUP_DIALOG, wxT("Select..."), wxPoint(-1, -1), wxSize(400, 290)) { wxBeginBusyCursor(); @@ -295,7 +303,7 @@ ClookUpDlg::ClookUpDlg(wxWindow *parent, wxChar *windowTitle, wxChar *tableName, widgetPtrsSet = TRUE; // Query the lookup table and display the result set - if (!(lookup2 = new Clookup2(tableName, dispCol1, dispCol2, pDb))) + if (!(lookup2 = new Clookup2(tableName, dispCol1, dispCol2, pDb, defDir))) { wxMessageBox(wxT("Error allocating memory for 'Clookup2' object."),wxT("Error...")); Close(); diff --git a/samples/db/listdb.h b/samples/db/listdb.h index d3c472498f..82897c2bb2 100644 --- a/samples/db/listdb.h +++ b/samples/db/listdb.h @@ -25,9 +25,6 @@ const int LOOKUP_COL_LEN = 250; -// Global database connection -extern wxDb *READONLY_DB; - // Clookup class class Clookup : public wxDbTable { @@ -35,10 +32,11 @@ class Clookup : public wxDbTable wxChar lookupCol[LOOKUP_COL_LEN+1]; - Clookup(wxChar *tblName, wxChar *colName); + Clookup(wxChar *tblName, wxChar *colName, wxDb *pDb, const wxString &defDir=""); }; // Clookup + // Clookup2 class class Clookup2 : public wxDbTable { @@ -47,10 +45,12 @@ class Clookup2 : public wxDbTable wxChar lookupCol1[LOOKUP_COL_LEN+1]; wxChar lookupCol2[LOOKUP_COL_LEN+1]; - Clookup2(wxChar *tblName, wxChar *colName1, wxChar *colName2, wxDb *pDb); + Clookup2(wxChar *tblName, wxChar *colName1, wxChar *colName2, wxDb *pDb, const wxString &defDir=""); }; // Clookup2 + +// ClookUpDlg class class ClookUpDlg : public wxDialog { private: @@ -73,14 +73,16 @@ class ClookUpDlg : public wxDialog wxChar *tableName, wxChar *colName, wxChar *where, - wxChar *orderBy); + wxChar *orderBy, + wxDb *pDb, + const wxString &defDir); // // This is a generic lookup constructor that will work with any table and any column. // It extends the capabilites of the lookup dialog in the following ways: // // 1) 2 columns rather than one - // 2) The ability to select DISTINCT column values + // 2) The ability to select DISTINCT column values // // Only set distinctValues equal to true if necessary. In many cases, the constraints // of the index(es) will enforce this uniqueness. Selecting DISTINCT does require @@ -95,8 +97,8 @@ class ClookUpDlg : public wxDialog // since it cannot be derived when you query using your own sql statement. // // The optional database connection can be used if you'd like the lookup class - // to use a database pointer other than the global READONLY_DB. This is necessary if - // records are being saved, but not committed to the db, yet should be included + // to use a database pointer other than the READONLY_DB of the app. This is necessary + // if records are being saved, but not committed to the db, yet should be included // in the lookup window. // ClookUpDlg(wxWindow *parent, @@ -106,10 +108,11 @@ class ClookUpDlg : public wxDialog wxChar *dispCol2, // Optional wxChar *where, wxChar *orderBy, + wxDb *pDb, // Database connection pointer + const wxString &defDir, bool distinctValues, // e.g. SELECT DISTINCT ... wxChar *selectStmt = 0, // If you wish to query by SQLstmt (complicated lookups) int maxLenCol1 = 0, // Mandatory if querying by SQLstmt - wxDb *pDb = READONLY_DB, // Database connection pointer bool allowOk = TRUE); // is the OK button enabled void OnButton( wxCommandEvent &event ); @@ -118,7 +121,7 @@ class ClookUpDlg : public wxDialog void OnActivate(bool) {}; // necessary for hot keys DECLARE_EVENT_TABLE() -}; +}; // class ClookUpDlg #define LOOKUP_DIALOG 500 -- 2.45.2