From fcb01c7ae63e5eafc15c51101a9e41351b2d3b34 Mon Sep 17 00:00:00 2001 From: Lucas Rubian Schatz Date: Wed, 8 Nov 2023 12:43:04 -0300 Subject: [PATCH 1/9] new Volatile SQL Editor TODO: allow user to open as many editors as wish TODO: fix character set loading error TODO: disable loading of every single database object --- src/frutils.cpp | 11 +++++ src/frutils.h | 2 + src/gui/CommandIds.h | 1 + src/gui/ExecuteSqlFrame.cpp | 62 +++++++++++++++++++++++++--- src/gui/ExecuteSqlFrame.h | 1 + src/gui/MainFrame.cpp | 23 +++++++++++ src/gui/MainFrame.h | 4 ++ src/metadata/database.cpp | 12 +++++- src/metadata/database.h | 3 ++ src/sql/SqlStatement.cpp | 81 +++++++++++++++++++++++++++++++++++-- src/sql/SqlStatement.h | 6 ++- 11 files changed, 195 insertions(+), 11 deletions(-) diff --git a/src/frutils.cpp b/src/frutils.cpp index eff857db..7008efa0 100644 --- a/src/frutils.cpp +++ b/src/frutils.cpp @@ -218,6 +218,17 @@ bool getService(Server* s, IBPP::Service& svc, ProgressIndicator* p, return true; } +wxString unquote(const wxString& input, const wxString& quoteChar) +{ + wxString result = input; + + if (result.StartsWith(quoteChar) && result.EndsWith(quoteChar) && result.length() >= 2) { + result = result.Mid(1, result.length() - 2); + } + + return result; +} + wxString getClientLibrary() { /*Todo: Implement FB library per conexion */ diff --git a/src/frutils.h b/src/frutils.h index afd79de1..2bdeff97 100644 --- a/src/frutils.h +++ b/src/frutils.h @@ -55,6 +55,8 @@ bool connectDatabase(Database *db, wxWindow* parent, bool getService(Server* s, IBPP::Service& svc, ProgressIndicator* p, bool sysdba); +wxString unquote(const wxString& input, const wxString& quoteChar = "\""); + wxString getClientLibrary(); #endif // FRUTILS_H diff --git a/src/gui/CommandIds.h b/src/gui/CommandIds.h index 48320d7a..03626163 100644 --- a/src/gui/CommandIds.h +++ b/src/gui/CommandIds.h @@ -86,6 +86,7 @@ enum { Menu_URLBugReport, Menu_NewObject, Menu_DatabaseRegistrationInfo, + Menu_NewVolatileSQLEditor, Menu_RegisterDatabase, Menu_CreateDatabase, Menu_ManageUsers, diff --git a/src/gui/ExecuteSqlFrame.cpp b/src/gui/ExecuteSqlFrame.cpp index 05e3a3a9..7f6311fe 100644 --- a/src/gui/ExecuteSqlFrame.cpp +++ b/src/gui/ExecuteSqlFrame.cpp @@ -526,6 +526,8 @@ ExecuteSqlFrame::ExecuteSqlFrame(wxWindow* WXUNUSED(parent), int id, loadingM = true; updateEditorCaretPosM = true; updateFrameTitleM = true; + if (db->getIsVolative()) + prepareVolatileDatabase(); transactionIsolationLevelM = static_cast(config().get("transactionIsolationLevel", 0)); transactionLockResolutionM = config().get("transactionLockResolution", true) ? IBPP::lrWait : IBPP::lrNoWait; @@ -574,11 +576,11 @@ ExecuteSqlFrame::ExecuteSqlFrame(wxWindow* WXUNUSED(parent), int id, do_layout(); // observe database object to close on disconnect / destruction - databaseM->attachObserver(this, false); + if (!db->getIsVolative()) databaseM->attachObserver(this, false); executedStatementsM.clear(); inTransaction(false); // enable/disable controls - setKeywords(); // set words for autocomplete feature + if (!db->getIsVolative()) setKeywords(); // set words for autocomplete feature historyPositionM = StatementHistory::get(databaseM).size(); @@ -803,7 +805,8 @@ void ExecuteSqlFrame::set_properties() int statusbar_widths[] = { -2, 100, 60, -1 }; statusbar_1->SetStatusWidths(4, statusbar_widths); - statusbar_1->SetStatusText(databaseM->getConnectionInfoString(), 0); + if ( ! databaseM->getIsVolative() ) + statusbar_1->SetStatusText(databaseM->getConnectionInfoString(), 0); statusbar_1->SetStatusText("Rows fetched", 1); statusbar_1->SetStatusText("Cursor position", 2); statusbar_1->SetStatusText("Transaction status", 3); @@ -1609,8 +1612,8 @@ void ExecuteSqlFrame::OnMenuUpdateHistoryNext(wxUpdateUIEvent& event) void ExecuteSqlFrame::OnMenuUpdateHistoryPrev(wxUpdateUIEvent& event) { - StatementHistory& sh = StatementHistory::get(databaseM); - event.Enable(historyPositionM > 0 && sh.size() > 0); + //StatementHistory& sh = StatementHistory::get(databaseM); + //event.Enable(historyPositionM > 0 && sh.size() > 0); } void ExecuteSqlFrame::OnMenuExecute(wxCommandEvent& WXUNUSED(event)) @@ -2105,6 +2108,35 @@ void ExecuteSqlFrame::clearLogBeforeExecution() styled_text_ctrl_stats->ClearAll(); } +void ExecuteSqlFrame::prepareVolatileDatabase(wxString hostname, wxString port, wxString path, wxString user, wxString password, wxString role, wxString charset) +{ + /*DatabasePtr dbM = std::make_shared(); + ServerPtr serverPtrM = std::make_shared(); + dbM->setServer(serverPtrM); + databaseM = dbM.get();*/ + //databaseM = new Database(); + //databaseM = std::make_shared(); + //databaseM->setId(UINT_MAX-30); + //this->serverM = new Server(); + //serverPtrM = ServerPtr(serverM); + ServerPtr serverPtrM = databaseM->getServer(); + if (!serverPtrM->getHostname().compare(hostname) || !serverPtrM->getPort().compare(port) || !databaseM->getPath().compare(path) || !databaseM->getUsername().compare(user) || !databaseM->getRawPassword().compare(password) || !databaseM->getRole().compare(role) || !databaseM->getDatabaseCharset().compare(charset)) + databaseM->disconnect(); + else + return; + + serverPtrM->setHostname(hostname); + serverPtrM->setPort(port); + + databaseM->setPath(path); + databaseM->setUsername(user); + databaseM->setRawPassword(password); + databaseM->setRole(role); + databaseM->setConnectionCharset(charset); + //databaseM->setServer(serverPtrM); + +} + void ExecuteSqlFrame::prepareAndExecute(bool prepareOnly) { bool hasSelection = styled_text_ctrl_sql->GetSelectionStart() @@ -2362,7 +2394,26 @@ bool ExecuteSqlFrame::execute(wxString sql, const wxString& terminator, log(_("Empty statement detected, bailing out...")); return true; } + + SqlStatement stm(sql, databaseM, terminator); + + if (stm.getAction() == actCONNECT) + { + if (!databaseM->getIsVolative()) + { + + log(_("Cannot use 'connect' statement in a regular SQL Script")); + return true; + } + wxString connHostM, connDatabasePortM, connPathM, connUsernameM, connPasswordM, connRoleM, connCharsetM; + stm.getCONNECTION(connHostM, connDatabasePortM, connPathM, connUsernameM, connPasswordM, connRoleM, connCharsetM); + prepareVolatileDatabase(connHostM, connDatabasePortM, connPathM, connUsernameM, connPasswordM, connRoleM, connCharsetM); + log(wxString::Format("Connecting to host: %s, port: %s, database: %s, user: %s, password: %s, role: %s, charset: %s", connHostM, connDatabasePortM, connPathM, connUsernameM, connPasswordM, connRoleM, connCharsetM)); + databaseM->connect(databaseM->getRawPassword()); + return databaseM->isConnected(); + + } if (styled_text_ctrl_sql->AutoCompActive()) styled_text_ctrl_sql->AutoCompCancel(); // remove the list if needed notebook_1->SetSelection(0); @@ -2545,7 +2596,6 @@ bool ExecuteSqlFrame::execute(wxString sql, const wxString& terminator, { } } - SqlStatement stm(sql, databaseM, terminator); if (stm.isDDL()) type = IBPP::stDDL; executedStatementsM.push_back(stm); diff --git a/src/gui/ExecuteSqlFrame.h b/src/gui/ExecuteSqlFrame.h index fdcc01a6..72a2def4 100644 --- a/src/gui/ExecuteSqlFrame.h +++ b/src/gui/ExecuteSqlFrame.h @@ -107,6 +107,7 @@ class ExecuteSqlFrame: public BaseFrame, public Observer typedef enum { ttNormal, ttSql, ttError } TextType; void log(wxString s, TextType type = ttNormal); // write messages to textbox void clearLogBeforeExecution(); + void prepareVolatileDatabase(wxString server = "", wxString port = "3050", wxString db = "", wxString user = "", wxString password = "", wxString role = "", wxString charset = "NONE"); void splitScreen(); Database* databaseM; diff --git a/src/gui/MainFrame.cpp b/src/gui/MainFrame.cpp index 036e2b31..45ec1b61 100644 --- a/src/gui/MainFrame.cpp +++ b/src/gui/MainFrame.cpp @@ -190,6 +190,7 @@ void MainFrame::buildMainMenu() menuBarM = new wxMenuBar(); databaseMenuM = new wxMenu(); + databaseMenuM->Append(Cmds::Menu_NewVolatileSQLEditor, _("Open new Volatile &SQL Editor...")); databaseMenuM->Append(Cmds::Menu_RegisterDatabase, _("R&egister existing database...")); databaseMenuM->Append(Cmds::Menu_CreateDatabase, _("Create &new database...")); databaseMenuM->Append(Cmds::Menu_RestoreIntoNew, _("Restore bac&kup into new database...")); @@ -373,6 +374,7 @@ EVT_MENU(Cmds::Menu_URLFeatureRequest, MainFrame::OnMenuURLFeatureRequest) EVT_MENU(Cmds::Menu_URLBugReport, MainFrame::OnMenuURLBugReport) EVT_MENU(wxID_PREFERENCES, MainFrame::OnMenuConfigure) +EVT_MENU(Cmds::Menu_NewVolatileSQLEditor, MainFrame::OnMenuNewVolatileSQLEditor) EVT_MENU(Cmds::Menu_RegisterDatabase, MainFrame::OnMenuRegisterDatabase) EVT_UPDATE_UI(Cmds::Menu_RegisterDatabase, MainFrame::OnMenuUpdateIfServerSelected) EVT_MENU(Cmds::Menu_CreateDatabase, MainFrame::OnMenuCreateDatabase) @@ -730,6 +732,8 @@ void MainFrame::doBeforeDestroy() // Firebird packagers for various distros figure out how to properly use NPTL treeMainM->Freeze(); rootM->disconnectAllDatabases(); + db=0; + serverPtrM=0; wxSafeYield(); treeMainM->Thaw(); @@ -960,6 +964,25 @@ void MainFrame::OnMenuBrowseData(wxCommandEvent& WXUNUSED(event)) treeMainM->getSelectedMetadataItem(), this); } +void MainFrame::OnMenuNewVolatileSQLEditor(wxCommandEvent& WXUNUSED(event)) +{ + if (db) + { + showInformationDialog(wxGetTopLevelParent(wxGetActiveWindow()), + _("Alert"), "For now, it's only possible to open one agnostic SQL Editor per run. If you are good at C++ and want help me fix the memory managment error I'm getting, let me know. I need 2 things: release a DatabasePtr and create a list of databaseM", + AdvancedMessageDialogButtonsOk()); + return; + } + + db = std::make_shared(); + serverPtrM = std::make_shared(); + db->setServer(serverPtrM); + db->setId(UINT_MAX-30); + db->setIsVolatile(true); + + ExecuteSqlFrame* eff = new ExecuteSqlFrame(this, -1, _("Omni SQL Editor"), db); + eff->Show(); +} void MainFrame::OnMenuRegisterDatabase(wxCommandEvent& WXUNUSED(event)) { ServerPtr s = getServer(treeMainM->getSelectedMetadataItem()); diff --git a/src/gui/MainFrame.h b/src/gui/MainFrame.h index f87ffb63..62369d6f 100644 --- a/src/gui/MainFrame.h +++ b/src/gui/MainFrame.h @@ -57,6 +57,7 @@ class MainFrame: public BaseFrame, private URIHandler, void OnMenuURLFeatureRequest(wxCommandEvent& event); void OnMenuURLBugReport(wxCommandEvent& event); void OnMenuConfigure(wxCommandEvent& event); + void OnMenuNewVolatileSQLEditor(wxCommandEvent& event); void OnMenuRegisterDatabase(wxCommandEvent& event); void OnMenuCloneDatabase(wxCommandEvent& event); void OnMenuDatabaseRegistrationInfo(wxCommandEvent& event); @@ -163,6 +164,9 @@ class MainFrame: public BaseFrame, private URIHandler, bool handleURI(URI& uri); private: RootPtr rootM; + //Volatile SQL Editor + DatabasePtr db; + ServerPtr serverPtrM; virtual bool doCanClose(); virtual void doBeforeDestroy(); diff --git a/src/metadata/database.cpp b/src/metadata/database.cpp index f63d2a23..40d479b6 100644 --- a/src/metadata/database.cpp +++ b/src/metadata/database.cpp @@ -316,7 +316,7 @@ bool DatabaseAuthenticationMode::getUseEncryptedPassword() const // Database class Database::Database() : MetadataItem(ntDatabase), metadataLoaderM(0), connectedM(false), - connectionCredentialsM(0), dialectM(3), idM(0) + connectionCredentialsM(0), dialectM(3), idM(0), volatileM(false) { defaultTimezoneM.name = ""; defaultTimezoneM.id = 0; @@ -415,6 +415,11 @@ void Database::getDatabaseTriggers(std::vector& list) std::back_inserter(list), std::mem_fn(&DBTriggerPtr::get)); } +bool Database::getIsVolative() +{ + return volatileM; +} + CharacterSet Database::getCharsetById(int id) { // if it contains both charset and collation as 2 bytes @@ -1834,6 +1839,11 @@ IBPP::Database& Database::getIBPPDatabase() return databaseM; } +void Database::setIsVolatile(const bool isVolatile) +{ + volatileM = isVolatile; +} + void Database::setPath(const wxString& value) { pathM = value; diff --git a/src/metadata/database.h b/src/metadata/database.h index 2da993b7..d6a294a1 100644 --- a/src/metadata/database.h +++ b/src/metadata/database.h @@ -162,6 +162,7 @@ class Database: public MetadataItem, MetadataLoader* metadataLoaderM; bool connectedM; + bool volatileM; wxString databaseCharsetM; wxString connectionUserM; wxString connectionRoleM; @@ -312,6 +313,7 @@ class Database: public MetadataItem, //! gets the database triggers (FB2.1+) void getDatabaseTriggers(std::vector& list); + bool getIsVolative(); wxString getPath() const; wxString getClientLibrary() const; int getSqlDialect() const; @@ -325,6 +327,7 @@ class Database: public MetadataItem, DatabaseAuthenticationMode& getAuthenticationMode(); wxString getRole() const; IBPP::Database& getIBPPDatabase(); + void setIsVolatile(const bool isVolatile); void setPath(const wxString& value); void setClientLibrary(const wxString& value); void setConnectionCharset(const wxString& value); diff --git a/src/sql/SqlStatement.cpp b/src/sql/SqlStatement.cpp index 1262a73d..b0f82926 100644 --- a/src/sql/SqlStatement.cpp +++ b/src/sql/SqlStatement.cpp @@ -36,6 +36,7 @@ #include "metadata/procedure.h" #include "metadata/relation.h" #include "sql/SqlStatement.h" +#include "frutils.h" // TOKEN LIST - a helper class @@ -145,6 +146,9 @@ SqlStatement::SqlStatement(const wxString& sql, Database *db, const wxString& actionM = actUPDATE; objectTypeM = ntTable; break; + case kwCONNECT: + actionM = actCONNECT; + break; default: return; // true; } @@ -238,7 +242,7 @@ SqlStatement::SqlStatement(const wxString& sql, Database *db, const wxString& } // get object type - while (objectTypeM == ntUnknown && typeTokenIndex < tokensM.size()) + while (objectTypeM == ntUnknown && typeTokenIndex < tokensM.size() && actionM != actCONNECT) { switch (tokensM[typeTokenIndex]) { @@ -346,10 +350,11 @@ SqlStatement::SqlStatement(const wxString& sql, Database *db, const wxString& } - if (objectTypeM == ntUnknown || !databaseM) + if ((objectTypeM == ntUnknown || !databaseM) && (actionM != actCONNECT)) return; // false; - objectM = databaseM->findByNameAndType(objectTypeM, nameM.get()); + if ( actionM != actCONNECT ) + objectM = databaseM->findByNameAndType(objectTypeM, nameM.get()); // map "CREATE OR ALTER" and "RECREATE" to correct action @@ -430,6 +435,63 @@ SqlStatement::SqlStatement(const wxString& sql, Database *db, const wxString& } } } + + // CONNECT "[database]" user '[username]' password '[password]' role '[role=]' character set '[charset=NONE]'; + if (actionM == actCONNECT) + { + //wxString database, user, password, role, charset; + size_t idx = 1; + connPathM = unquote(tokenStringsM[idx++], "'"); + + + if (connPathM.Contains(":")) //split "server/port:database" + { + + // find ':' pos + int colonPos = connPathM.Find(':'); + + + connServerPort = "3050"; + connHostM = connPathM.Left(colonPos); + // find '/' pos + int slashPos = connHostM.Find('/'); + + if (slashPos >= 0) + { + connHostM = connPathM.Left(slashPos); + connServerPort = connPathM.Mid(slashPos + 1, colonPos - slashPos - 1); + } + connPathM = connPathM.Mid(colonPos + 1); + } + connUsernameM = ""; + connRoleM = ""; + connCharsetM = "NONE"; + while (idx < tokensM.size()) + { + if (tokensM[idx] == kwUSER) + { + idx++; + connUsernameM = unquote(tokenStringsM[idx++],"'"); + } + if (tokensM[idx] == kwPASSWORD) + { + idx++; + connPasswordM = unquote(tokenStringsM[idx++], "'"); + } + if (tokensM[idx] == kwROLE) + { + idx++; + connRoleM = unquote(tokenStringsM[idx++], "'"); + } + if ((tokensM[idx] == kwCHARACTER) && (tokensM[idx+1] == kwSET)) + { + idx++; + connCharsetM = unquote(tokenStringsM[idx++], "'"); + } + idx++; + } + return; + } } wxString SqlStatement::getStatement() const @@ -465,6 +527,19 @@ bool SqlStatement::isDDL() const && actionM != actUPDATE); } +bool SqlStatement::getCONNECTION(wxString& host,wxString& port, wxString& path, wxString& user, wxString& password, wxString& role, wxString& charset) +{ + if (actionM != actCONNECT) return false; + host = connHostM; + port = connServerPort; + path = connPathM; + user = connUsernameM; + password = connPasswordM; + role = connRoleM; + charset = connCharsetM; + return true; +} + bool SqlStatement::isAlterColumn() const { return isAlterColumnM; diff --git a/src/sql/SqlStatement.h b/src/sql/SqlStatement.h index 96737c10..99c9ca4b 100644 --- a/src/sql/SqlStatement.h +++ b/src/sql/SqlStatement.h @@ -36,7 +36,7 @@ class Relation; typedef enum { actNONE, actALTER, actCREATE, actCREATE_OR_ALTER, actDECLARE, actDROP, - actRECREATE, actSET, actUPDATE, actGRANT, actCOMMENT + actRECREATE, actSET, actUPDATE, actGRANT, actCOMMENT, actCONNECT } SqlAction; class TokenList @@ -56,6 +56,7 @@ class SqlStatement const wxString& terminator = ";"); bool isDDL() const; + bool getCONNECTION(wxString& host, wxString& port, wxString& db, wxString& user, wxString& password, wxString& role, wxString& charset); SqlAction getAction() const; bool actionIs(const SqlAction& act, NodeType nt = ntUnknown) const; NodeType getObjectType() const; @@ -84,6 +85,9 @@ class SqlStatement bool isDatatypeM; wxString terminatorM; wxString statementM; + //CONNECT action + wxString connHostM, connServerPort, connPathM, connUsernameM, connPasswordM, connRoleM, connCharsetM; + }; #endif From 6856f0146ff5df540bab22452cc96a97cc8ec42d Mon Sep 17 00:00:00 2001 From: Lucas Rubian Schatz Date: Wed, 8 Nov 2023 16:55:52 -0300 Subject: [PATCH 2/9] Fix character set loading TODO: validade connect parameters --- src/gui/ExecuteSqlFrame.cpp | 4 ++-- src/gui/MainFrame.cpp | 2 +- src/sql/SqlStatement.cpp | 21 +++++++++++---------- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/gui/ExecuteSqlFrame.cpp b/src/gui/ExecuteSqlFrame.cpp index 7f6311fe..c5cbf28d 100644 --- a/src/gui/ExecuteSqlFrame.cpp +++ b/src/gui/ExecuteSqlFrame.cpp @@ -1612,8 +1612,8 @@ void ExecuteSqlFrame::OnMenuUpdateHistoryNext(wxUpdateUIEvent& event) void ExecuteSqlFrame::OnMenuUpdateHistoryPrev(wxUpdateUIEvent& event) { - //StatementHistory& sh = StatementHistory::get(databaseM); - //event.Enable(historyPositionM > 0 && sh.size() > 0); + StatementHistory& sh = StatementHistory::get(databaseM); + event.Enable(historyPositionM > 0 && sh.size() > 0); } void ExecuteSqlFrame::OnMenuExecute(wxCommandEvent& WXUNUSED(event)) diff --git a/src/gui/MainFrame.cpp b/src/gui/MainFrame.cpp index 45ec1b61..4e364be9 100644 --- a/src/gui/MainFrame.cpp +++ b/src/gui/MainFrame.cpp @@ -969,7 +969,7 @@ void MainFrame::OnMenuNewVolatileSQLEditor(wxCommandEvent& WXUNUSED(event)) if (db) { showInformationDialog(wxGetTopLevelParent(wxGetActiveWindow()), - _("Alert"), "For now, it's only possible to open one agnostic SQL Editor per run. If you are good at C++ and want help me fix the memory managment error I'm getting, let me know. I need 2 things: release a DatabasePtr and create a list of databaseM", + _("Alert"), "For now, it's only possible to open one Volatile SQL Editor per run. If you are good at C++ and want help me fix the memory managment error I'm getting, let me know. I need 2 things: declare locally the object or release the DatabasePtr when closing the editor and/or create a list of databaseM", AdvancedMessageDialogButtonsOk()); return; } diff --git a/src/sql/SqlStatement.cpp b/src/sql/SqlStatement.cpp index b0f82926..9203ec33 100644 --- a/src/sql/SqlStatement.cpp +++ b/src/sql/SqlStatement.cpp @@ -371,7 +371,7 @@ SqlStatement::SqlStatement(const wxString& sql, Database *db, const wxString& if (stt != tkCOMMENT && stt != tkWHITESPACE) { tokensM.add(stt); - if (stt == tkIDENTIFIER || stt == tkSTRING) + //if (stt == tkIDENTIFIER || stt == tkSTRING) //Wy does this if exists? { wxString ts(tokenizer.getCurrentTokenString()); tokenStringsM[tokensM.size() - 1] = ts; @@ -436,7 +436,7 @@ SqlStatement::SqlStatement(const wxString& sql, Database *db, const wxString& } } - // CONNECT "[database]" user '[username]' password '[password]' role '[role=]' character set '[charset=NONE]'; + // CONNECT "[database]" password '[password]' user '[username]' role '[role=]' character set '[charset=NONE]'; if (actionM == actCONNECT) { //wxString database, user, password, role, charset; @@ -464,6 +464,7 @@ SqlStatement::SqlStatement(const wxString& sql, Database *db, const wxString& connPathM = connPathM.Mid(colonPos + 1); } connUsernameM = ""; + connPasswordM = ""; connRoleM = ""; connCharsetM = "NONE"; while (idx < tokensM.size()) @@ -471,22 +472,22 @@ SqlStatement::SqlStatement(const wxString& sql, Database *db, const wxString& if (tokensM[idx] == kwUSER) { idx++; - connUsernameM = unquote(tokenStringsM[idx++],"'"); + connUsernameM = unquote(tokenStringsM[idx],"'"); } - if (tokensM[idx] == kwPASSWORD) + else if (tokensM[idx] == kwPASSWORD) { idx++; - connPasswordM = unquote(tokenStringsM[idx++], "'"); + connPasswordM = unquote(tokenStringsM[idx], "'"); } - if (tokensM[idx] == kwROLE) + else if (tokensM[idx] == kwROLE) { idx++; - connRoleM = unquote(tokenStringsM[idx++], "'"); + connRoleM = unquote(tokenStringsM[idx], "'"); } - if ((tokensM[idx] == kwCHARACTER) && (tokensM[idx+1] == kwSET)) + else if ((tokensM[idx] == kwCHARACTER) && (tokensM[idx + 1] == kwSET) && (tokensM[idx + 2] == tkSTRING)) { - idx++; - connCharsetM = unquote(tokenStringsM[idx++], "'"); + idx++; idx++; //for character and set + connCharsetM = unquote(tokenStringsM[idx], "'"); } idx++; } From af50d6654579132ebbc097307e38fac3d2d4560d Mon Sep 17 00:00:00 2001 From: Lucas Rubian Schatz Date: Thu, 9 Nov 2023 16:01:47 -0300 Subject: [PATCH 3/9] Fix memory managment for Volatile SQL Editor --- src/gui/ExecuteSqlFrame.cpp | 9 ++++++++- src/gui/ExecuteSqlFrame.h | 2 ++ src/gui/MainFrame.cpp | 11 ++--------- src/gui/MainFrame.h | 3 --- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/gui/ExecuteSqlFrame.cpp b/src/gui/ExecuteSqlFrame.cpp index 57c603ac..e345f9f9 100644 --- a/src/gui/ExecuteSqlFrame.cpp +++ b/src/gui/ExecuteSqlFrame.cpp @@ -520,9 +520,12 @@ ExecuteSqlFrame::ExecuteSqlFrame(wxWindow* WXUNUSED(parent), int id, wxString title, DatabasePtr db, const wxPoint& pos, const wxSize& size, long style) : BaseFrame(wxTheApp->GetTopWindow(), id, title, pos, size, style), - Observer(), databaseM(db.get()) + Observer(), databasePtrM(db) //changed for volatile SQL Editor, as we only have a fake server and database { wxASSERT(db); + //Need this 2 PtrM lines especifically to keep a reference for both original objects in volatile SQL Editor else the serverptr is released as soon as exists MainFrame event call + databaseM = databasePtrM.get(); + serverPtrM = databasePtrM->getServer(); loadingM = true; updateEditorCaretPosM = true; @@ -882,7 +885,11 @@ void ExecuteSqlFrame::doBeforeDestroy() if (grid_data->IsCellEditControlEnabled()) grid_data->EnableCellEditControl(false); // make sure that further calls to update() will not call Close() again + if (databaseM->getIsVolative() && databaseM->isConnected()) + databaseM->disconnect(); databaseM = 0; + databasePtrM = 0; + serverPtrM = 0; } void ExecuteSqlFrame::showProperties(wxString objectName) diff --git a/src/gui/ExecuteSqlFrame.h b/src/gui/ExecuteSqlFrame.h index 72a2def4..02a165fc 100644 --- a/src/gui/ExecuteSqlFrame.h +++ b/src/gui/ExecuteSqlFrame.h @@ -111,6 +111,8 @@ class ExecuteSqlFrame: public BaseFrame, public Observer void splitScreen(); Database* databaseM; + DatabasePtr databasePtrM; + ServerPtr serverPtrM; StatementHistory::Position historyPositionM; wxString localBuffer; diff --git a/src/gui/MainFrame.cpp b/src/gui/MainFrame.cpp index 816f4f13..c6df8376 100644 --- a/src/gui/MainFrame.cpp +++ b/src/gui/MainFrame.cpp @@ -737,8 +737,6 @@ void MainFrame::doBeforeDestroy() // Firebird packagers for various distros figure out how to properly use NPTL treeMainM->Freeze(); rootM->disconnectAllDatabases(); - db=0; - serverPtrM=0; wxSafeYield(); treeMainM->Thaw(); @@ -971,13 +969,8 @@ void MainFrame::OnMenuBrowseData(wxCommandEvent& WXUNUSED(event)) void MainFrame::OnMenuNewVolatileSQLEditor(wxCommandEvent& WXUNUSED(event)) { - if (db) - { - showInformationDialog(wxGetTopLevelParent(wxGetActiveWindow()), - _("Alert"), "For now, it's only possible to open one Volatile SQL Editor per run. If you are good at C++ and want help me fix the memory managment error I'm getting, let me know. I need 2 things: declare locally the object or release the DatabasePtr when closing the editor and/or create a list of databaseM", - AdvancedMessageDialogButtonsOk()); - return; - } + DatabasePtr db; + ServerPtr serverPtrM; db = std::make_shared(); serverPtrM = std::make_shared(); diff --git a/src/gui/MainFrame.h b/src/gui/MainFrame.h index 0712b1f8..f9cbb898 100644 --- a/src/gui/MainFrame.h +++ b/src/gui/MainFrame.h @@ -165,9 +165,6 @@ class MainFrame: public BaseFrame, private URIHandler, bool handleURI(URI& uri); private: RootPtr rootM; - //Volatile SQL Editor - DatabasePtr db; - ServerPtr serverPtrM; virtual bool doCanClose(); virtual void doBeforeDestroy(); From f3229f6aa85c59ebb5c7143ea026b9569e662d14 Mon Sep 17 00:00:00 2001 From: Lucas Rubian Schatz Date: Fri, 10 Nov 2023 14:35:30 -0300 Subject: [PATCH 4/9] Fix for 64 builds --- src/metadata/database.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/metadata/database.cpp b/src/metadata/database.cpp index d118fcc2..86b2c350 100644 --- a/src/metadata/database.cpp +++ b/src/metadata/database.cpp @@ -2197,7 +2197,7 @@ void Database::getConnectedUsers(wxArrayString& users) const { wxString name((*it).first); if ((*it).second > 1) - name += wxString::Format(" (%d)", (*it).second); + name += wxString::Format(" (%zu)", (*it).second); users.Add(name); } } From 9c90b985be565ed449ebfe5f45736de25e0d353d Mon Sep 17 00:00:00 2001 From: Lucas Rubian Schatz Date: Fri, 10 Nov 2023 14:37:32 -0300 Subject: [PATCH 5/9] Update CMakeLists.txt --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 54e9656f..61f4dbd3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ +cmake_minimum_required(VERSION 3.5) project(flamerobin) -cmake_minimum_required(VERSION 3.0) # Don't allow in-source builds. Keeps things nice and tidy if( "${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}" ) From 7ad3d3934a8e48f023d4db9b69256efaed243069 Mon Sep 17 00:00:00 2001 From: Lucas Rubian Schatz Date: Fri, 10 Nov 2023 17:51:49 -0300 Subject: [PATCH 6/9] Enable connect, disconnect and create database --- src/gui/ExecuteSqlFrame.cpp | 35 +++++++++++++++++++++++++++++------ src/sql/SqlStatement.cpp | 33 ++++++++++++++++++++++++++++++--- src/sql/SqlStatement.h | 6 ++++-- src/sql/SqlTokenizer.h | 2 +- 4 files changed, 64 insertions(+), 12 deletions(-) diff --git a/src/gui/ExecuteSqlFrame.cpp b/src/gui/ExecuteSqlFrame.cpp index e345f9f9..ea9c5ee6 100644 --- a/src/gui/ExecuteSqlFrame.cpp +++ b/src/gui/ExecuteSqlFrame.cpp @@ -2129,7 +2129,10 @@ void ExecuteSqlFrame::prepareVolatileDatabase(wxString hostname, wxString port, //serverPtrM = ServerPtr(serverM); ServerPtr serverPtrM = databaseM->getServer(); if (!serverPtrM->getHostname().compare(hostname) || !serverPtrM->getPort().compare(port) || !databaseM->getPath().compare(path) || !databaseM->getUsername().compare(user) || !databaseM->getRawPassword().compare(password) || !databaseM->getRole().compare(role) || !databaseM->getDatabaseCharset().compare(charset)) + { databaseM->disconnect(); + transactionM = 0; + } else return; @@ -2137,6 +2140,7 @@ void ExecuteSqlFrame::prepareVolatileDatabase(wxString hostname, wxString port, serverPtrM->setPort(port); databaseM->setPath(path); + databaseM->setName_("VOLATILECONNECTION"); databaseM->setUsername(user); databaseM->setRawPassword(password); databaseM->setRole(role); @@ -2373,22 +2377,41 @@ bool ExecuteSqlFrame::execute(wxString sql, const wxString& terminator, SqlStatement stm(sql, databaseM, terminator); - if (stm.getAction() == actCONNECT) + if ((stm.getAction() == actCONNECT) || (stm.getAction() == actCREATE_DATABASE)) { if (!databaseM->getIsVolative()) { - - log(_("Cannot use 'connect' statement in a regular SQL Script")); - return true; + log(_("Cannot use 'connect' or 'create' statement in a regular SQL Script"), ttError); + splitScreen(); + return false; } - wxString connHostM, connDatabasePortM, connPathM, connUsernameM, connPasswordM, connRoleM, connCharsetM; + wxString connHostM, connDatabasePortM, connPathM, connUsernameM, connPasswordM, connRoleM, connCharsetM; int createPageSizeM, createDialecM; stm.getCONNECTION(connHostM, connDatabasePortM, connPathM, connUsernameM, connPasswordM, connRoleM, connCharsetM); prepareVolatileDatabase(connHostM, connDatabasePortM, connPathM, connUsernameM, connPasswordM, connRoleM, connCharsetM); - log(wxString::Format("Connecting to host: %s, port: %s, database: %s, user: %s, password: %s, role: %s, charset: %s", connHostM, connDatabasePortM, connPathM, connUsernameM, connPasswordM, connRoleM, connCharsetM)); + if (stm.getAction() == actCREATE_DATABASE) { + createPageSizeM = stm.getCreatePageSize(); + createDialecM = stm.getCreateDialect(); + log(wxString::Format("Creating database: %s, port: %s, database: %s, user: %s, password: %s, role: %s, charset: %s, page size: %d, dialect %d", connHostM, connDatabasePortM, connPathM, connUsernameM, connPasswordM, connRoleM, connCharsetM, createPageSizeM, createDialecM), ttSql); + databaseM->create(createPageSizeM, createDialecM); + } + log(wxString::Format("Connecting to host: %s, port: %s, database: %s, user: %s, password: %s, role: %s, charset: %s", connHostM, connDatabasePortM, connPathM, connUsernameM, connPasswordM, connRoleM, connCharsetM), ttSql); databaseM->connect(databaseM->getRawPassword()); return databaseM->isConnected(); } + else + if (stm.getAction() == actDISCONNECT) + { + if (!databaseM->getIsVolative()) + { + log(_("Cannot use 'disconnect' statement in a regular SQL Script"), ttError); + splitScreen(); + return false; + } + databaseM->disconnect(); + transactionM = 0; + return true; + } if (styled_text_ctrl_sql->AutoCompActive()) styled_text_ctrl_sql->AutoCompCancel(); // remove the list if needed notebook_1->SetSelection(0); diff --git a/src/sql/SqlStatement.cpp b/src/sql/SqlStatement.cpp index 9203ec33..10186a18 100644 --- a/src/sql/SqlStatement.cpp +++ b/src/sql/SqlStatement.cpp @@ -123,7 +123,7 @@ SqlStatement::SqlStatement(const wxString& sql, Database *db, const wxString& actionM = actCOMMENT; break; case kwCREATE: - actionM = actCREATE; + actionM = (tokensM[1] == kwDATABASE ? actCREATE_DATABASE : actCREATE); break; case kwDECLARE: actionM = actDECLARE; @@ -149,6 +149,9 @@ SqlStatement::SqlStatement(const wxString& sql, Database *db, const wxString& case kwCONNECT: actionM = actCONNECT; break; + case kwDISCONNECT: + actionM = actDISCONNECT; + break; default: return; // true; } @@ -437,10 +440,12 @@ SqlStatement::SqlStatement(const wxString& sql, Database *db, const wxString& } // CONNECT "[database]" password '[password]' user '[username]' role '[role=]' character set '[charset=NONE]'; - if (actionM == actCONNECT) + if ((actionM == actCONNECT) || (actionM == actCREATE_DATABASE)) { //wxString database, user, password, role, charset; size_t idx = 1; + if (actionM == actCREATE_DATABASE) + idx++; connPathM = unquote(tokenStringsM[idx++], "'"); @@ -467,6 +472,8 @@ SqlStatement::SqlStatement(const wxString& sql, Database *db, const wxString& connPasswordM = ""; connRoleM = ""; connCharsetM = "NONE"; + createPageSizeM = 8096; + createDialectM = 3; while (idx < tokensM.size()) { if (tokensM[idx] == kwUSER) @@ -489,6 +496,16 @@ SqlStatement::SqlStatement(const wxString& sql, Database *db, const wxString& idx++; idx++; //for character and set connCharsetM = unquote(tokenStringsM[idx], "'"); } + else if (tokensM[idx] == kwPAGE_SIZE) + { + idx++; + tokenStringsM[idx].ToInt(&createPageSizeM); + } + else if (tokensM[idx] == kwDIALECT) + { + idx++; + tokenStringsM[idx].ToInt(&createDialectM); + } idx++; } return; @@ -530,7 +547,7 @@ bool SqlStatement::isDDL() const bool SqlStatement::getCONNECTION(wxString& host,wxString& port, wxString& path, wxString& user, wxString& password, wxString& role, wxString& charset) { - if (actionM != actCONNECT) return false; + if ((actionM != actCONNECT) && ((actionM != actCREATE_DATABASE))) return false; host = connHostM; port = connServerPort; path = connPathM; @@ -541,6 +558,16 @@ bool SqlStatement::getCONNECTION(wxString& host,wxString& port, wxString& path, return true; } +int SqlStatement::getCreatePageSize() const +{ + return createPageSizeM; +} + +int SqlStatement::getCreateDialect() const +{ + return createDialectM; +} + bool SqlStatement::isAlterColumn() const { return isAlterColumnM; diff --git a/src/sql/SqlStatement.h b/src/sql/SqlStatement.h index 99c9ca4b..62313ec1 100644 --- a/src/sql/SqlStatement.h +++ b/src/sql/SqlStatement.h @@ -36,7 +36,7 @@ class Relation; typedef enum { actNONE, actALTER, actCREATE, actCREATE_OR_ALTER, actDECLARE, actDROP, - actRECREATE, actSET, actUPDATE, actGRANT, actCOMMENT, actCONNECT + actRECREATE, actSET, actUPDATE, actGRANT, actCOMMENT, actCONNECT, actDISCONNECT, actCREATE_DATABASE } SqlAction; class TokenList @@ -57,6 +57,8 @@ class SqlStatement bool isDDL() const; bool getCONNECTION(wxString& host, wxString& port, wxString& db, wxString& user, wxString& password, wxString& role, wxString& charset); + int getCreatePageSize() const; + int getCreateDialect() const; SqlAction getAction() const; bool actionIs(const SqlAction& act, NodeType nt = ntUnknown) const; NodeType getObjectType() const; @@ -86,7 +88,7 @@ class SqlStatement wxString terminatorM; wxString statementM; //CONNECT action - wxString connHostM, connServerPort, connPathM, connUsernameM, connPasswordM, connRoleM, connCharsetM; + wxString connHostM, connServerPort, connPathM, connUsernameM, connPasswordM, connRoleM, connCharsetM; int createPageSizeM, createDialectM; }; diff --git a/src/sql/SqlTokenizer.h b/src/sql/SqlTokenizer.h index 4a42bcbd..7b67317e 100644 --- a/src/sql/SqlTokenizer.h +++ b/src/sql/SqlTokenizer.h @@ -63,7 +63,7 @@ enum SqlTokenType { kwDATA, kwDATABASE, kwDATE, kwDATEADD, kwDATEDIFF, kwDAY, kwDB_KEY, kwDDL, kwDEC, kwDECFLOAT, kwDECIMAL, kwDECLARE, kwDECODE, kwDECRYPT, kwDEFAULT, kwDEFINER, kwDELETE, kwDELETING, kwDENSE_RANK, kwDESCENDING, kwDESCRIBE, kwDESCRIPTOR, - kwDETERMINISTIC, kwDIFFERENCE, kwDISABLE, kwDISCONNECT, kwDISPLAY, kwDISTINCT, + kwDETERMINISTIC, kwDIALECT, kwDIFFERENCE, kwDISABLE, kwDISCONNECT, kwDISPLAY, kwDISTINCT, kwDO, kwDOMAIN, kwDOUBLE, kwDROP, kwECHO, kwEDIT, kwELSE, kwENABLE, kwENCRYPT, kwEND, kwENGINE, kwENTRY_POINT, From 6bddc7a36b0b159018089389853d34f171c44ad8 Mon Sep 17 00:00:00 2001 From: arvanus Date: Fri, 10 Nov 2023 17:54:17 -0300 Subject: [PATCH 7/9] Update windows-build.yml --- .github/workflows/windows-build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/windows-build.yml b/.github/workflows/windows-build.yml index 8eb257c8..5e7ec44f 100644 --- a/.github/workflows/windows-build.yml +++ b/.github/workflows/windows-build.yml @@ -5,9 +5,9 @@ env: on: push: - branches: [ "master", "dev" ] + branches: [ "**" ] pull_request: - branches: [ "master", "dev" ] + branches: [ "**" ] jobs: build: From 24987b81ad4d20a52e6c77bd1d92bc0ca16770ca Mon Sep 17 00:00:00 2001 From: Lucas Rubian Schatz Date: Sun, 12 Nov 2023 20:42:31 -0300 Subject: [PATCH 8/9] Update fr_whatsnew.html --- docs/fr_whatsnew.html | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/docs/fr_whatsnew.html b/docs/fr_whatsnew.html index b4d69530..04f55dc8 100644 --- a/docs/fr_whatsnew.html +++ b/docs/fr_whatsnew.html @@ -8,7 +8,25 @@ http://www.flamerobin.org/bugs.php or https://github.com/mariuz/flamerobin/issues


-Changes in FlameRobin 0.9.9 and 0.9.10
+Changes in FlameRobin 0.9.10
+

+New features
+- New "Volatile" SQL Editor, allows you to use CREATE TABASE, CONNECT and DISCONNECT in your script, increasing the fexibility to do maintenance across severall databases. To use this feature you need to go Database->Open new Volatile SQL Editor. Use with attention
+
+Enhancements and Bug fixes
+
+- New collation tree view
+- Many other small fixes, see commit history for more details :)
+
+For more info please read commit history log
+
+
+
+
+
+ + +Changes in FlameRobin 0.9.9


Enhancements and Bug fixes

From 4375da48de76f68a03252536a56375d3fa3578bc Mon Sep 17 00:00:00 2001 From: Lucas Rubian Schatz Date: Sun, 12 Nov 2023 20:48:28 -0300 Subject: [PATCH 9/9] Update fr_whatsnew.html --- docs/fr_whatsnew.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/fr_whatsnew.html b/docs/fr_whatsnew.html index 04f55dc8..ac25ba44 100644 --- a/docs/fr_whatsnew.html +++ b/docs/fr_whatsnew.html @@ -11,7 +11,7 @@ Changes in FlameRobin 0.9.10


New features
-- New "Volatile" SQL Editor, allows you to use CREATE TABASE, CONNECT and DISCONNECT in your script, increasing the fexibility to do maintenance across severall databases. To use this feature you need to go Database->Open new Volatile SQL Editor. Use with attention
+- New "Volatile" SQL Editor, allows you to use CREATE DATABASE, CONNECT and DISCONNECT in your script, increasing the flexibility to do maintenance across several databases. To use this feature you need to go to Database->Open new Volatile SQL Editor. Use with attention

Enhancements and Bug fixes