diff --git a/docs/en_US/options-query_tool.rst b/docs/en_US/options-query_tool.rst index 0b557e7..bb3f0af 100644 --- a/docs/en_US/options-query_tool.rst +++ b/docs/en_US/options-query_tool.rst @@ -23,6 +23,8 @@ Use the fields on the *Query editor* dialog to specify workspace preferences for * **Enable Auto ROLLBACK** - Check the box next to *Enable Auto ROLLBACK* to instruct the query tool to execute a ROLLBACK if a query fails. +* **Enable Auto COMMIT** - Check the box next to *Enable Auto COMMIT* to instruct the query tool to enable auto commit after executing the query. + * **Keywords in uppercase** - Check the box next to *Keywords in uppercase* to instruct the Query tool to convert any keywords entered to an uppercase font. .. image:: images/options-colours.png diff --git a/pgadmin/frm/frmOptions.cpp b/pgadmin/frm/frmOptions.cpp index 4b3b1f1..4081319 100644 --- a/pgadmin/frm/frmOptions.cpp +++ b/pgadmin/frm/frmOptions.cpp @@ -89,6 +89,7 @@ #define chkColumnNames CTRL_CHECKBOX("chkColumnNames") #define txtThousandsSeparator CTRL_TEXT("txtThousandsSeparator") #define chkAutoRollback CTRL_CHECKBOX("chkAutoRollback") +#define chkAutoCommit CTRL_CHECKBOX("chkAutoCommit") #define chkDoubleClickProperties CTRL_CHECKBOX("chkDoubleClickProperties") #define chkShowNotices CTRL_CHECKBOX("chkShowNotices") #define cbLanguage CTRL_COMBOBOX("cbLanguage") @@ -303,6 +304,7 @@ frmOptions::frmOptions(frmMain *parent) chkIndicateNull->SetValue(settings->GetIndicateNull()); txtThousandsSeparator->SetValue(settings->GetThousandsSeparator()); chkAutoRollback->SetValue(settings->GetAutoRollback()); + chkAutoCommit->SetValue(settings->GetAutoCommit()); chkDoubleClickProperties->SetValue(settings->GetDoubleClickProperties()); txtDecimalMark->SetValue(settings->GetDecimalMark()); chkColumnNames->SetValue(settings->GetColumnNames()); @@ -670,6 +672,7 @@ void frmOptions::OnOK(wxCommandEvent &ev) settings->SetColumnNames(chkColumnNames->GetValue()); settings->SetThousandsSeparator(txtThousandsSeparator->GetValue()); settings->SetAutoRollback(chkAutoRollback->GetValue()); + settings->SetAutoCommit(chkAutoCommit->GetValue()); settings->SetDoubleClickProperties(chkDoubleClickProperties->GetValue()); settings->SetShowNotices(chkShowNotices->GetValue()); diff --git a/pgadmin/frm/frmQuery.cpp b/pgadmin/frm/frmQuery.cpp index 23ec45b..e6827f9 100644 --- a/pgadmin/frm/frmQuery.cpp +++ b/pgadmin/frm/frmQuery.cpp @@ -121,6 +121,7 @@ BEGIN_EVENT_TABLE(frmQuery, pgFrame) EVT_MENU(MNU_EXPLAINANALYZE, frmQuery::OnExplain) EVT_MENU(MNU_CANCEL, frmQuery::OnCancel) EVT_MENU(MNU_AUTOROLLBACK, frmQuery::OnAutoRollback) + EVT_MENU(MNU_AUTOCOMMIT, frmQuery::OnAutoCommit) EVT_MENU(MNU_CONTENTS, frmQuery::OnContents) EVT_MENU(MNU_HELP, frmQuery::OnHelp) EVT_MENU(MNU_CLEARHISTORY, frmQuery::OnClearHistory) @@ -316,6 +317,7 @@ frmQuery::frmQuery(frmMain *form, const wxString &_title, pgConn *_conn, const w queryMenu->Append(MNU_CLEARHISTORY, _("Clear history"), _("Clear history window.")); queryMenu->AppendSeparator(); queryMenu->Append(MNU_AUTOROLLBACK, _("&Auto-Rollback"), _("Rollback the current transaction if an error is detected"), wxITEM_CHECK); + queryMenu->Append(MNU_AUTOCOMMIT, _("&Auto-Commit"), _("Auto commit the cuurent transaction"), wxITEM_CHECK); queryMenu->AppendSeparator(); queryMenu->Append(MNU_CANCEL, _("&Cancel\tAlt-Break"), _("Cancel query")); menuBar->Append(queryMenu, _("&Query")); @@ -559,9 +561,13 @@ frmQuery::frmQuery(frmMain *form, const wxString &_title, pgConn *_conn, const w bool bVal; // Auto-rollback - settings->Read(wxT("frmQuery/AutoRollback"), &bVal, false); + bVal = settings->GetAutoRollback(); queryMenu->Check(MNU_AUTOROLLBACK, bVal); + // Auto-commit + bVal = settings->GetAutoCommit(); + queryMenu->Check(MNU_AUTOCOMMIT, bVal); + // Auto indent settings->Read(wxT("frmQuery/AutoIndent"), &bVal, true); editMenu->Check(MNU_AUTOINDENT, bVal); @@ -823,6 +829,15 @@ void frmQuery::OnAutoRollback(wxCommandEvent &event) settings->WriteBool(wxT("frmQuery/AutoRollback"), queryMenu->IsChecked(MNU_AUTOROLLBACK)); } +void frmQuery::OnAutoCommit(wxCommandEvent &event) +{ + queryMenu->Check(MNU_AUTOCOMMIT, event.IsChecked()); + if(event.IsChecked() && conn->GetTxStatus() != PQTRANS_IDLE) + wxMessageBox( + _("The current transaction is still in progess.\n\nIn order to take the effect of AUTOCOMMIT mode, please complete the transaction by executing COMMIT, or ROLLBACK statement."), + _("Warning - Transaction in progress"), wxICON_INFORMATION | wxOK, this); + settings->WriteBool(wxT("frmQuery/AutoCommit"), queryMenu->IsChecked(MNU_AUTOCOMMIT)); +} void frmQuery::OnAutoIndent(wxCommandEvent &event) { @@ -2486,6 +2501,9 @@ void frmQuery::execQuery(const wxString &query, int resultToRetrieve, bool singl startTimeQuery = wxGetLocalTimeMillis(); timer.Start(10); + if (!queryMenu->IsChecked(MNU_AUTOCOMMIT) && conn->GetTxStatus() == PQTRANS_IDLE && !isBeginNotRequired(query)) + conn->ExecuteVoid(wxT("BEGIN;")); + if (sqlResult->Execute(query, resultToRetrieve, this, QUERY_COMPLETE, qi) >= 0) { // Return and wait for the result @@ -2495,6 +2513,177 @@ void frmQuery::execQuery(const wxString &query, int resultToRetrieve, bool singl completeQuery(false, false, false); } +bool frmQuery::isBeginNotRequired(wxString query) +{ + int wordlen = 0; + + query = query.Trim(false); + + /* + * Check word length (since "beginx" is not "begin"). + */ + while(wxIsalpha(query.GetChar(wordlen))) + wordlen++; + + /* + * Transaction control commands. These should include every keyword that + * gives rise to a TransactionStmt in the backend grammar, except for the + * savepoint-related commands. + * + * (We assume that START must be START TRANSACTION, since there is + * presently no other "START foo" command.) + */ + wxString keyword = query.SubString(0, wordlen-1); + if (wordlen == 5 && keyword.CmpNoCase(wxT("abort")) == 0) + return true; + if (wordlen == 5 && keyword.CmpNoCase(wxT("begin")) == 0) + return true; + if (wordlen == 5 && keyword.CmpNoCase(wxT("start")) == 0) + return true; + if (wordlen == 6 && keyword.CmpNoCase(wxT("commit")) == 0) + return true; + if (wordlen == 3 && keyword.CmpNoCase(wxT("end")) == 0) + return true; + if (wordlen == 8 && keyword.CmpNoCase(wxT("rollback")) == 0) + return true; + if (wordlen == 7 && keyword.CmpNoCase(wxT("prepare")) == 0) + { + /* PREPARE TRANSACTION is a TC command, PREPARE foo is not */ + query = query.SubString(keyword.Length(),query.Length()-1); + query = query.Trim(false); + + wordlen = 0; + while(wxIsalpha(query.GetChar(wordlen))) + wordlen++; + keyword = query.SubString(0, wordlen-1); + + if (wordlen == 11 && keyword.CmpNoCase(wxT("transaction")) == 0) + return true; + return false; + } + + /* + * Commands not allowed within transactions. The statements checked for + * here should be exactly those that call PreventTransactionChain() in the + * backend. + */ + if (wordlen == 6 && keyword.CmpNoCase(wxT("vacuum")) == 0) + return true; + if (wordlen == 7 && keyword.CmpNoCase(wxT("cluster")) == 0) + { + /* CLUSTER with any arguments is allowed in transactions */ + query = query.SubString(keyword.Length(),query.Length()-1); + query = query.Trim(false); + + if(wxIsalpha(((wxChar)query.at(0)))) + return false; /* has additional words */ + return true; /* it's CLUSTER without arguments */ + } + + if (wordlen == 6 && keyword.CmpNoCase(wxT("create")) == 0) + { + query = query.SubString(keyword.Length(),query.Length()-1); + query = query.Trim(false); + + wordlen = 0; + while(wxIsalpha(query.GetChar(wordlen))) + wordlen++; + keyword = query.SubString(0, wordlen-1); + + if (wordlen == 8 && keyword.CmpNoCase(wxT("database")) == 0) + return true; + if (wordlen == 10 && keyword.CmpNoCase(wxT("tablespace")) == 0) + return true; + + /* CREATE [UNIQUE] INDEX CONCURRENTLY isn't allowed in xacts */ + if (wordlen == 6 && keyword.CmpNoCase(wxT("cluster")) == 0) + { + query = query.SubString(keyword.Length(),query.Length()-1); + query = query.Trim(false); + + wordlen = 0; + while(wxIsalpha(query.GetChar(wordlen))) + wordlen++; + keyword = query.SubString(0, wordlen-1); + } + + if (wordlen == 5 && keyword.CmpNoCase(wxT("index")) == 0) + { + query = query.SubString(keyword.Length(),query.Length()-1); + query = query.Trim(false); + + wordlen = 0; + while(wxIsalpha(query.GetChar(wordlen))) + wordlen++; + keyword = query.SubString(0, wordlen-1); + + if (wordlen == 12 && keyword.CmpNoCase(wxT("concurrently")) == 0) + return true; + } + + return false; + } + + if (wordlen == 5 && keyword.CmpNoCase(wxT("alter")) == 0) + { + query = query.SubString(keyword.Length(),query.Length()-1); + query = query.Trim(false); + + wordlen = 0; + while(wxIsalpha(query.GetChar(wordlen))) + wordlen++; + keyword = query.SubString(0, wordlen-1); + + /* ALTER SYSTEM isn't allowed in xacts */ + if (wordlen == 6 && keyword.CmpNoCase(wxT("system")) == 0) + return true; + + return false; + } + + /* + * Note: these tests will match DROP SYSTEM and REINDEX TABLESPACE, which + * aren't really valid commands so we don't care much. The other four + * possible matches are correct. + */ + if ((wordlen == 4 && keyword.CmpNoCase(wxT("drop")) == 0) || + (wordlen == 7 && keyword.CmpNoCase(wxT("reindex")) == 0)) + { + query = query.SubString(keyword.Length(),query.Length()-1); + query = query.Trim(false); + + wordlen = 0; + while(wxIsalpha(query.GetChar(wordlen))) + wordlen++; + keyword = query.SubString(0, wordlen-1); + + if (wordlen == 8 && keyword.CmpNoCase(wxT("database")) == 0) + return true; + if (wordlen == 6 && keyword.CmpNoCase(wxT("system")) == 0) + return true; + if (wordlen == 10 && keyword.CmpNoCase(wxT("tablespace")) == 0) + return true; + return false; + } + + /* DISCARD ALL isn't allowed in xacts, but other variants are allowed. */ + if (wordlen == 7 && keyword.CmpNoCase(wxT("discard")) == 0) + { + query = query.SubString(keyword.Length(),query.Length()-1); + query = query.Trim(false); + + wordlen = 0; + while(wxIsalpha(query.GetChar(wordlen))) + wordlen++; + keyword = query.SubString(0, wordlen-1); + + if (wordlen == 3 && keyword.CmpNoCase(wxT("all")) == 0) + return true; + return false; + } + + return false; +} // When the query completes, it raises an event which we process here. void frmQuery::OnQueryComplete(pgQueryResultEvent &ev) @@ -2839,7 +3028,7 @@ void frmQuery::completeQuery(bool done, bool explain, bool verbose) msgHistory->ShowPosition(0); // If the transaction aborted for some reason, issue a rollback to cleanup. - if (settings->GetAutoRollback() && conn->GetTxStatus() == PGCONN_TXSTATUS_INERROR) + if (queryMenu->IsChecked(MNU_AUTOROLLBACK) && conn->GetTxStatus() == PGCONN_TXSTATUS_INERROR) conn->ExecuteVoid(wxT("ROLLBACK;")); setTools(false); diff --git a/pgadmin/include/frm/frmQuery.h b/pgadmin/include/frm/frmQuery.h index c698370..d53931b 100644 --- a/pgadmin/include/frm/frmQuery.h +++ b/pgadmin/include/frm/frmQuery.h @@ -199,6 +199,7 @@ private: void OnRedo(wxCommandEvent &event); void OnSaveHistory(wxCommandEvent &event); void OnAutoRollback(wxCommandEvent &event); + void OnAutoCommit(wxCommandEvent &event); void OnChangeConnection(wxCommandEvent &ev); void OnClearHistory(wxCommandEvent &event); void OnActivate(wxActivateEvent &event); @@ -239,6 +240,7 @@ private: void execQuery(const wxString &query, int resultToRetrieve = 0, bool singleResult = false, const int queryOffset = 0, bool toFile = false, bool explain = false, bool verbose = false); void OnQueryComplete(pgQueryResultEvent &ev); void completeQuery(bool done, bool explain, bool verbose); + bool isBeginNotRequired(wxString query); void OnScriptComplete(wxCommandEvent &ev); void setTools(const bool running); void showMessage(const wxString &msg, const wxString &msgShort = wxT("")); diff --git a/pgadmin/include/frm/menu.h b/pgadmin/include/frm/menu.h index fb20b60..4d0f757 100644 --- a/pgadmin/include/frm/menu.h +++ b/pgadmin/include/frm/menu.h @@ -71,6 +71,7 @@ enum MNU_BUFFERS, MNU_TIMING, MNU_AUTOROLLBACK, + MNU_AUTOCOMMIT, MNU_CLEARHISTORY, MNU_SAVEHISTORY, MNU_CHECKALIVE, diff --git a/pgadmin/include/utils/sysSettings.h b/pgadmin/include/utils/sysSettings.h index eb11213..cecab7f 100644 --- a/pgadmin/include/utils/sysSettings.h +++ b/pgadmin/include/utils/sysSettings.h @@ -367,13 +367,23 @@ public: bool GetAutoRollback() const { bool b; - Read(wxT("frmQuery/AutoRollback"), &b, true); + Read(wxT("frmQuery/AutoRollback"), &b, false); return b; } void SetAutoRollback(const bool newval) { WriteBool(wxT("frmQuery/AutoRollback"), newval); } + bool GetAutoCommit() const + { + bool b; + Read(wxT("frmQuery/AutoCommit"), &b, true); + return b; + } + void SetAutoCommit(const bool newval) + { + WriteBool(wxT("frmQuery/AutoCommit"), newval); + } wxString GetDecimalMark() const { wxString s; diff --git a/pgadmin/ui/frmOptions.xrc b/pgadmin/ui/frmOptions.xrc index a57b0ce..7b9dc6f 100644 --- a/pgadmin/ui/frmOptions.xrc +++ b/pgadmin/ui/frmOptions.xrc @@ -490,6 +490,21 @@ 4 + + + + wxALIGN_CENTER_VERTICAL|wxTOP|wxLEFT|wxRIGHT + 4 + + + + + 1 + + wxEXPAND|wxALIGN_CENTER_VERTICAL|wxTOP|wxLEFT|wxRIGHT + 4 + +