pgadmin3/dlg/dlgTrigger.cpp
2020-07-07 22:19:12 +05:00

551 lines
16 KiB
C++

//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// dlgTrigger.cpp - PostgreSQL Trigger Property
//
//////////////////////////////////////////////////////////////////////////
// wxWindows headers
#include <wx/wx.h>
// App headers
#include "pgAdmin3.h"
#include "utils/misc.h"
#include "frm/frmMain.h"
#include "utils/pgDefs.h"
#include "dlg/dlgTrigger.h"
#include "schema/pgTrigger.h"
#include "schema/pgTable.h"
#include "schema/pgSchema.h"
// pointer to controls
#define chkRow CTRL_CHECKBOX("chkRow")
#define chkConstraint CTRL_CHECKBOX("chkConstraint")
#define chkDeferrable CTRL_CHECKBOX("chkDeferrable")
#define chkDeferred CTRL_CHECKBOX("chkDeferred")
#define cbFunction CTRL_COMBOBOX2("cbFunction")
#define txtArguments CTRL_TEXT("txtArguments")
#define rdbFires CTRL_RADIOBOX("rdbFires")
#define chkInsert CTRL_CHECKBOX("chkInsert")
#define chkUpdate CTRL_CHECKBOX("chkUpdate")
#define chkDelete CTRL_CHECKBOX("chkDelete")
#define chkTruncate CTRL_CHECKBOX("chkTruncate")
#define txtWhen CTRL_TEXT("txtWhen")
#define txtBody CTRL_SQLBOX("txtBody")
#define btnAddCol CTRL_BUTTON("btnAddCol")
#define btnRemoveCol CTRL_BUTTON("btnRemoveCol")
#define lstColumns CTRL_LISTVIEW("lstColumns")
BEGIN_EVENT_TABLE(dlgTrigger, dlgCollistProperty)
EVT_RADIOBOX(XRCID("rdbFires"), dlgProperty::OnChange)
EVT_CHECKBOX(XRCID("chkConstraint"), dlgTrigger::OnChangeConstraint)
EVT_CHECKBOX(XRCID("chkDeferrable"), dlgProperty::OnChange)
EVT_CHECKBOX(XRCID("chkRow"), dlgProperty::OnChange)
EVT_CHECKBOX(XRCID("chkInsert"), dlgProperty::OnChange)
EVT_CHECKBOX(XRCID("chkUpdate"), dlgTrigger::OnChange)
EVT_CHECKBOX(XRCID("chkDelete"), dlgProperty::OnChange)
EVT_CHECKBOX(XRCID("chkTruncate"), dlgProperty::OnChange)
EVT_TEXT(XRCID("cbFunction"), dlgTrigger::OnChangeFunc)
EVT_COMBOBOX(XRCID("cbFunction"), dlgProperty::OnChange)
EVT_TEXT(XRCID("txtArguments"), dlgProperty::OnChange)
EVT_TEXT(XRCID("txtWhen"), dlgProperty::OnChange)
EVT_STC_MODIFIED(XRCID("txtBody"), dlgProperty::OnChangeStc)
EVT_LIST_ITEM_SELECTED(XRCID("lstColumns"), dlgTrigger::OnSelectListCol)
EVT_COMBOBOX(XRCID("cbColumns"), dlgTrigger::OnSelectComboCol)
EVT_BUTTON(XRCID("btnAddCol"), dlgTrigger::OnAddCol)
EVT_BUTTON(XRCID("btnRemoveCol"), dlgTrigger::OnRemoveCol)
END_EVENT_TABLE();
dlgProperty *pgTriggerFactory::CreateDialog(frmMain *frame, pgObject *node, pgObject *parent)
{
return new dlgTrigger(this, frame, (pgTrigger *)node, (pgTable *)parent);
}
dlgTrigger::dlgTrigger(pgaFactory *f, frmMain *frame, pgTrigger *node, pgTable *parentNode)
: dlgCollistProperty(f, frame, wxT("dlgTrigger"), parentNode)
{
trigger = node;
table = parentNode;
wxASSERT(!table || table->GetMetaType() == PGM_TABLE || table->GetMetaType() == PGM_VIEW
|| table->GetMetaType() == GP_PARTITION);
bool bVal;
settings->Read(wxT("frmQuery/ShowLineNumber"), &bVal, false);
if (!bVal)
{
txtBody->SetMarginType(1, wxSTC_MARGIN_NUMBER);
txtBody->SetMarginWidth(1, ConvertDialogToPixels(wxPoint(16, 0)).x);
}
lstColumns->AddColumn(_("Column name"));
}
pgObject *dlgTrigger::GetObject()
{
return trigger;
}
wxString dlgTrigger::GetColumns()
{
wxString sql;
int pos;
// iterate cols
for (pos = 0 ; pos < lstColumns->GetItemCount() ; pos++)
{
if (pos)
sql += wxT(", ");
sql += qtIdent(lstColumns->GetItemText(pos));
}
return sql;
}
int dlgTrigger::Go(bool modal)
{
if (trigger)
{
// edit mode
chkRow->SetValue((trigger->GetTriggerType() & TRIGGER_TYPE_ROW) != 0);
chkInsert->SetValue((trigger->GetTriggerType() & TRIGGER_TYPE_INSERT) != 0);
chkUpdate->SetValue((trigger->GetTriggerType() & TRIGGER_TYPE_UPDATE) != 0);
chkDelete->SetValue((trigger->GetTriggerType() & TRIGGER_TYPE_DELETE) != 0);
chkTruncate->SetValue((trigger->GetTriggerType() & TRIGGER_TYPE_TRUNCATE) != 0);
if (trigger->GetTriggerType() & TRIGGER_TYPE_BEFORE)
rdbFires->SetSelection(0);
else if (trigger->GetTriggerType() & TRIGGER_TYPE_INSTEAD)
rdbFires->SetSelection(2);
else
rdbFires->SetSelection(1);
txtArguments->SetValue(trigger->GetArguments());
txtWhen->SetValue(trigger->GetWhen());
if (!connection->BackendMinimumVersion(7, 4))
txtName->Disable();
if (trigger->GetLanguage() == wxT("edbspl"))
{
cbFunction->Append(wxString::Format(wxT("<%s>"), _("Inline EDB-SPL")));
txtBody->SetText(trigger->GetSource());
}
else
{
cbFunction->Append(trigger->GetFunction());
txtBody->Disable();
}
cbFunction->SetSelection(0);
txtArguments->Disable();
cbFunction->Disable();
if (!connection->EdbMinimumVersion(8, 0))
{
chkRow->Disable();
rdbFires->Disable();
chkInsert->Disable();
chkUpdate->Disable();
chkDelete->Disable();
chkTruncate->Disable();
}
else if (!connection->BackendMinimumVersion(8, 4))
chkTruncate->Disable();
else if (!connection->BackendMinimumVersion(8, 5))
txtWhen->Disable();
wxArrayString colsArr = trigger->GetColumnList();
for (int colIdx = 0, colsCount = colsArr.Count(); colIdx < colsCount; colIdx++)
{
lstColumns->InsertItem(colIdx, colsArr.Item(colIdx));
}
if (connection->BackendMinimumVersion(8, 2))
{
chkConstraint->SetValue(trigger->GetIsConstraint());
chkDeferrable->SetValue(trigger->GetDeferrable());
chkDeferred->SetValue(trigger->GetDeferred());
}
chkConstraint->Enable(false);
chkDeferrable->Enable(false);
chkDeferred->Enable(false);
}
else
{
// create mode
if (connection->EdbMinimumVersion(8, 0))
cbFunction->Append(wxString::Format(wxT("<%s>"), _("Inline EDB-SPL")));
wxString sysRestr;
if (!settings->GetShowSystemObjects())
sysRestr = wxT(" AND ") + connection->SystemNamespaceRestriction(wxT("nspname"));
pgSet *set = connection->ExecuteSet(
wxT("SELECT quote_ident(nspname) || '.' || quote_ident(proname)\n")
wxT(" FROM pg_proc p, pg_namespace n, pg_language l\n")
wxT(" WHERE p.pronamespace = n.oid AND p.prolang = l.oid AND l.lanname != 'edbspl' AND prorettype=") + NumToStr(PGOID_TYPE_TRIGGER) + sysRestr +
wxT(" ORDER BY nspname ASC, proname ASC "));
if (set)
{
while (!set->Eof())
{
cbFunction->Append(set->GetVal(0));
set->MoveNext();
}
delete set;
}
if (!connection->BackendMinimumVersion(7, 4))
{
chkRow->SetValue(true);
chkRow->Disable();
}
txtBody->Disable();
if (!connection->BackendMinimumVersion(8, 4))
chkTruncate->Disable();
if (!connection->BackendMinimumVersion(9, 1) || table->GetMetaType() != PGM_VIEW)
rdbFires->Enable(2, false);
chkConstraint->Enable(connection->BackendMinimumVersion(8, 2));
chkDeferrable->Disable();
chkDeferred->Disable();
}
cbColumns->Disable();
btnAddCol->Disable();
btnRemoveCol->Disable();
// Reset the radio box rbxEvent item values with the keywords to resolve the locale issues.
rdbFires->SetString(0, wxT("BEFORE"));
rdbFires->SetString(1, wxT("AFTER"));
rdbFires->SetString(2, wxT("INSTEAD OF"));
chkTruncate->SetLabel(wxT("TRUNCATE"));
chkDelete->SetLabel(wxT("DELETE"));
chkInsert->SetLabel(wxT("INSERT"));
chkUpdate->SetLabel(wxT("UPDATE"));
return dlgCollistProperty::Go(modal);
}
void dlgTrigger::OnAddCol(wxCommandEvent &ev)
{
wxString colName = cbColumns->GetValue();
if (!colName.IsEmpty())
{
lstColumns->InsertItem(lstColumns->GetItemCount(), colName);
cbColumns->Delete(cbColumns->GetCurrentSelection());
if (cbColumns->GetCount())
cbColumns->SetSelection(0);
CheckChange();
if (!cbColumns->GetCount())
btnAddCol->Disable();
}
}
void dlgTrigger::OnRemoveCol(wxCommandEvent &ev)
{
long pos = lstColumns->GetSelection();
if (pos >= 0)
{
wxString colName = lstColumns->GetItemText(pos);
lstColumns->DeleteItem(pos);
cbColumns->Append(colName);
CheckChange();
btnRemoveCol->Disable();
}
}
wxString dlgTrigger::GetSql()
{
wxString sql;
wxString name = GetName();
if (trigger)
{
if (name != trigger->GetName())
sql = wxT("ALTER TRIGGER ") + trigger->GetQuotedIdentifier() + wxT(" ON ") + table->GetQuotedFullIdentifier()
+ wxT("\n RENAME TO ") + qtIdent(name) + wxT(";\n\n");
}
if (!trigger ||
(cbFunction->GetValue() == wxString::Format(wxT("<%s>"), _("Inline EDB-SPL"))
&& (
txtBody->GetText() != trigger->GetSource() ||
chkRow->GetValue() != (trigger->GetTriggerType() & TRIGGER_TYPE_ROW) ||
chkInsert->GetValue() != (trigger->GetTriggerType() & TRIGGER_TYPE_INSERT ? true : false) ||
chkUpdate->GetValue() != (trigger->GetTriggerType() & TRIGGER_TYPE_UPDATE ? true : false) ||
chkDelete->GetValue() != (trigger->GetTriggerType() & TRIGGER_TYPE_DELETE ? true : false) ||
chkTruncate->GetValue() != (trigger->GetTriggerType() & TRIGGER_TYPE_TRUNCATE ? true : false) ||
rdbFires->GetSelection() != (trigger->GetTriggerType() & TRIGGER_TYPE_BEFORE ? 0 : TRIGGER_TYPE_INSTEAD ? 2 : 1)
)
)
)
{
if (cbFunction->GetValue() == wxString::Format(wxT("<%s>"), _("Inline EDB-SPL")))
sql += wxT("CREATE OR REPLACE TRIGGER ");
else if (chkConstraint->GetValue())
sql += wxT("CREATE CONSTRAINT TRIGGER ");
else
sql += wxT("CREATE TRIGGER ");
sql += qtIdent(name);
if (rdbFires->GetSelection() == 1)
sql += wxT(" AFTER");
else if (rdbFires->GetSelection() == 2)
sql += wxT(" INSTEAD OF");
else
sql += wxT(" BEFORE");
int actionCount = 0;
if (chkInsert->GetValue())
{
if (actionCount++)
sql += wxT(" OR");
sql += wxT(" INSERT");
}
if (chkUpdate->GetValue())
{
if (actionCount++)
sql += wxT(" OR");
sql += wxT(" UPDATE");
if (lstColumns->GetItemCount() > 0)
sql += wxT(" OF ") + GetColumns();
}
if (chkDelete->GetValue())
{
if (actionCount++)
sql += wxT(" OR");
sql += wxT(" DELETE");
}
if (chkTruncate->GetValue())
{
if (actionCount++)
sql += wxT(" OR");
sql += wxT(" TRUNCATE");
}
sql += wxT("\n ON ") + table->GetQuotedFullIdentifier();
if (chkDeferrable->GetValue())
{
sql += wxT(" DEFERRABLE");
if (chkDeferred->GetValue())
sql += wxT(" INITIALLY DEFERRED");
}
sql += wxT(" FOR EACH ");
if (chkRow->GetValue())
sql += wxT("ROW");
else
sql += wxT("STATEMENT");
if (connection->BackendMinimumVersion(8, 5) && !txtWhen->GetValue().IsEmpty())
sql += wxT("\n WHEN (") + txtWhen->GetValue() + wxT(")");
if (cbFunction->GetValue() != wxString::Format(wxT("<%s>"), _("Inline EDB-SPL")))
{
sql += wxT("\n EXECUTE PROCEDURE ") + cbFunction->GetValue()
+ wxT("(") + txtArguments->GetValue()
+ wxT(");\n");
}
else
{
sql += wxT("\n") + txtBody->GetText();
if (!sql.Trim().EndsWith(wxT(";")))
sql = sql.Trim() + wxT(";");
sql += wxT("\n");
}
}
AppendComment(sql, wxT("TRIGGER ") + qtIdent(GetName())
+ wxT(" ON ") + table->GetQuotedFullIdentifier(), trigger);
return sql;
}
pgObject *dlgTrigger::CreateObject(pgCollection *collection)
{
pgObject *obj = triggerFactory.CreateObjects(collection, 0,
wxT("\n AND tgname=") + qtDbString(GetName()) +
wxT("\n AND tgrelid=") + table->GetOidStr() +
wxT("\n AND relnamespace=") + table->GetSchema()->GetOidStr());
return obj;
}
void dlgTrigger::OnChange(wxCommandEvent &ev)
{
if (chkUpdate->GetValue())
{
cbColumns->Enable();
}
else
{
if (lstColumns->GetItemCount() > 0)
{
if (wxMessageBox(_("Removing the UPDATE event will cause the column list to be cleared. Do you wish to continue?"), _("Remove UPDATE event?"), wxYES_NO) != wxYES)
{
chkUpdate->SetValue(true);
return;
}
// Move all the columns back to the combo
for (int pos = lstColumns->GetItemCount(); pos > 0; pos--)
{
wxString colName = lstColumns->GetItemText(pos - 1);
lstColumns->DeleteItem(pos - 1);
cbColumns->Append(colName);
}
}
cbColumns->Disable();
btnAddCol->Disable();
btnRemoveCol->Disable();
}
CheckChange();
}
void dlgTrigger::OnChangeFunc(wxCommandEvent &ev)
{
cbFunction->GuessSelection(ev);
if (cbFunction->GetValue() == wxString::Format(wxT("<%s>"), _("Inline EDB-SPL")))
{
txtArguments->Disable();
txtBody->Enable();
}
else
{
txtArguments->Enable();
txtBody->Disable();
}
CheckChange();
}
void dlgTrigger::OnChangeConstraint(wxCommandEvent &ev)
{
if (chkConstraint->GetValue())
{
rdbFires->SetSelection(1);
rdbFires->Disable();
chkRow->SetValue(true);
chkRow->Disable();
chkDeferrable->Enable();
chkDeferred->Enable();
}
else
{
rdbFires->Enable();
chkRow->Enable();
chkDeferrable->Disable();
chkDeferred->Disable();
}
CheckChange();
}
void dlgTrigger::CheckChange()
{
bool enable = true;
wxString function = cbFunction->GetValue();
wxString name = GetName();
// We can only have per-statement TRUNCATE triggers
if (trigger || connection->BackendMinimumVersion(8, 4))
{
if (chkRow->GetValue())
{
chkTruncate->Disable();
chkTruncate->SetValue(false);
}
else if (connection->EdbMinimumVersion(8, 0))
chkTruncate->Enable();
}
CheckValid(enable, !name.IsEmpty(), _("Please specify name."));
CheckValid(enable, !function.IsEmpty(), _("Please specify trigger function."));
CheckValid(enable, chkInsert->GetValue() || chkUpdate->GetValue() || chkDelete->GetValue() || chkTruncate->GetValue(),
_("Please specify at least one action."));
if (cbFunction->GetValue() == wxString::Format(wxT("<%s>"), _("Inline EDB-SPL")))
CheckValid(enable, !txtBody->GetText().IsEmpty(), _("Please specify trigger body."));
if (trigger)
{
EnableOK(enable &&
(txtComment->GetValue() != trigger->GetComment() ||
txtName->GetValue() != trigger->GetName() ||
(txtBody->GetText() != trigger->GetSource() && cbFunction->GetValue() == wxString::Format(wxT("<%s>"), _("Inline EDB-SPL"))) ||
txtWhen->GetValue() != trigger->GetWhen() ||
chkRow->GetValue() != (trigger->GetTriggerType() & TRIGGER_TYPE_ROW ? true : false) ||
chkInsert->GetValue() != (trigger->GetTriggerType() & TRIGGER_TYPE_INSERT ? true : false) ||
chkUpdate->GetValue() != (trigger->GetTriggerType() & TRIGGER_TYPE_UPDATE ? true : false) ||
chkDelete->GetValue() != (trigger->GetTriggerType() & TRIGGER_TYPE_DELETE ? true : false) ||
chkTruncate->GetValue() != (trigger->GetTriggerType() & TRIGGER_TYPE_TRUNCATE ? true : false) ||
rdbFires->GetSelection() != (trigger->GetTriggerType() & TRIGGER_TYPE_BEFORE ? 0 : (trigger->GetTriggerType() & TRIGGER_TYPE_INSTEAD ? 2 : 1))));
}
else
{
EnableOK(enable);
}
}
bool dlgTrigger::IsUpToDate()
{
if (trigger && !trigger->IsUpToDate())
return false;
else
return true;
}
void dlgTrigger::OnSelectListCol(wxListEvent &ev)
{
OnSelectCol();
}
void dlgTrigger::OnSelectComboCol(wxCommandEvent &ev)
{
OnSelectCol();
}
void dlgTrigger::OnSelectCol()
{
// Can't change the columns on an existing index.
if (trigger)
return;
if (lstColumns->GetSelection() != wxNOT_FOUND)
btnRemoveCol->Enable(true);
else
btnRemoveCol->Enable(false);
if (cbColumns->GetSelection() != wxNOT_FOUND && !cbColumns->GetValue().IsEmpty())
btnAddCol->Enable(true);
else
btnAddCol->Enable(false);
}