pgadmin3/dlg/dlgFunction.cpp
2025-09-24 12:02:10 +05:00

1148 lines
32 KiB
C++

//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// dlgFunction.cpp - PostgreSQL Function Property
//
//////////////////////////////////////////////////////////////////////////
// wxWindows headers
#include <wx/wx.h>
// App headers
#include "pgAdmin3.h"
#include "utils/misc.h"
#include "utils/pgDefs.h"
#include "ctl/ctlSQLBox.h"
#include "dlg/dlgFunction.h"
#include "schema/pgFunction.h"
#include "schema/pgSchema.h"
#include "schema/pgDatatype.h"
#include "ctl/ctlSeclabelPanel.h"
// pointer to controls
#define txtArguments CTRL_TEXT("txtArguments")
#define cbReturntype CTRL_COMBOBOX2("cbReturntype")
#define cbLanguage CTRL_COMBOBOX2("cbLanguage")
#define chkSetof CTRL_CHECKBOX("chkSetof")
#define cbVolatility CTRL_COMBOBOX("cbVolatility")
#define cbParallel CTRL_COMBOBOX("cbParallel")
#define chkStrict CTRL_CHECKBOX("chkStrict")
#define chkWindow CTRL_CHECKBOX("chkWindow")
#define chkSecureDefiner CTRL_CHECKBOX("chkSecureDefiner")
#define txtCost CTRL_TEXT("txtCost")
#define txtRows CTRL_TEXT("txtRows")
#define chkLeakProof CTRL_CHECKBOX("chkLeakProof")
#define lstArguments CTRL_LISTVIEW("lstArguments")
#define rdbIn CTRL_RADIOBUTTON("rdbIn")
#define rdbOut CTRL_RADIOBUTTON("rdbOut")
#define rdbInOut CTRL_RADIOBUTTON("rdbInOut")
#define rdbVariadic CTRL_RADIOBUTTON("rdbVariadic")
#define txtArgName CTRL_TEXT("txtArgName")
#define txtArgDefVal CTRL_TEXT("txtArgDefVal")
#define btnAdd CTRL_BUTTON("wxID_ADD")
#define btnChange CTRL_BUTTON("wxID_CHANGE")
#define btnRemove CTRL_BUTTON("wxID_REMOVE")
#define pnlParameters CTRL_PANEL("pnlParameters")
#define sbxDefinition CTRL_STATICBOX("sbxDefinition")
#define stObjectFile CTRL_STATIC("stObjectFile")
#define txtObjectFile CTRL_TEXT("txtObjectFile")
#define stLinkSymbol CTRL_STATIC("stLinkSymbol")
#define txtLinkSymbol CTRL_TEXT("txtLinkSymbol")
#define txtSqlBox CTRL_SQLBOX("txtSqlBox")
#define lstVariables CTRL_LISTVIEW("lstVariables")
#define btnAddVar CTRL_BUTTON("btnAddVar")
#define btnRemoveVar CTRL_BUTTON("btnRemoveVar")
#define cbVarname CTRL_COMBOBOX2("cbVarname")
#define txtValue CTRL_TEXT("txtValue")
#define chkValue CTRL_CHECKBOX("chkValue")
#define TXTOBJ_LIB wxT("$libdir/")
BEGIN_EVENT_TABLE(dlgFunction, dlgSecurityProperty)
EVT_TEXT(XRCID("cbVolatility"), dlgProperty::OnChange)
EVT_TEXT(XRCID("cbParallel"), dlgProperty::OnChange)
EVT_CHECKBOX(XRCID("chkStrict"), dlgProperty::OnChange)
EVT_CHECKBOX(XRCID("chkSecureDefiner"), dlgProperty::OnChange)
EVT_TEXT(XRCID("txtObjectFile"), dlgProperty::OnChange)
EVT_TEXT(XRCID("txtLinkSymbol"), dlgProperty::OnChange)
EVT_TEXT(XRCID("txtCost"), dlgProperty::OnChange)
EVT_TEXT(XRCID("txtRows"), dlgProperty::OnChange)
EVT_CHECKBOX(XRCID("chkLeakProof"), dlgProperty::OnChange)
EVT_STC_MODIFIED(XRCID("txtSqlBox"), dlgProperty::OnChangeStc)
EVT_CHECKBOX(XRCID("chkSetof"), dlgFunction::OnChangeSetof)
EVT_TEXT(XRCID("cbReturntype"), dlgFunction::OnChangeReturn)
EVT_COMBOBOX(XRCID("cbReturntype"), dlgFunction::OnChangeReturn)
EVT_TEXT(XRCID("cbDatatype"), dlgFunction::OnSelChangeType)
EVT_COMBOBOX(XRCID("cbDatatype"), dlgFunction::OnSelChangeType)
EVT_TEXT(XRCID("cbLanguage"), dlgFunction::OnSelChangeLanguage)
EVT_COMBOBOX(XRCID("cbLanguage"), dlgFunction::OnSelChangeLanguage)
EVT_LIST_ITEM_SELECTED(XRCID("lstArguments"), dlgFunction::OnSelChangeArg)
EVT_TEXT(XRCID("txtArgName"), dlgFunction::OnChangeArgName)
EVT_BUTTON(wxID_ADD, dlgFunction::OnAddArg)
EVT_BUTTON(XRCID("wxID_CHANGE"), dlgFunction::OnChangeArg)
EVT_BUTTON(wxID_REMOVE, dlgFunction::OnRemoveArg)
EVT_LIST_ITEM_SELECTED(XRCID("lstVariables"), dlgFunction::OnVarSelChange)
EVT_BUTTON(XRCID("btnAddVar"), dlgFunction::OnVarAdd)
EVT_BUTTON(XRCID("btnRemoveVar"), dlgFunction::OnVarRemove)
EVT_TEXT(XRCID("cbVarname"), dlgFunction::OnVarnameSelChange)
EVT_COMBOBOX(XRCID("cbVarname"), dlgFunction::OnVarnameSelChange)
EVT_RADIOBUTTON(XRCID("rdbIn"), dlgFunction::OnChangeArgMode)
EVT_RADIOBUTTON(XRCID("rdbOut"), dlgFunction::OnChangeArgMode)
EVT_RADIOBUTTON(XRCID("rdbInOut"), dlgFunction::OnChangeArgMode)
EVT_RADIOBUTTON(XRCID("rdbVariadic"), dlgFunction::OnChangeArgMode)
EVT_CHECKBOX(XRCID("chkWindow"), dlgFunction::OnChangeWindow)
#ifdef __WXMAC__
EVT_SIZE( dlgFunction::OnChangeSize)
#endif
END_EVENT_TABLE();
dlgProperty *pgFunctionFactory::CreateDialog(frmMain *frame, pgObject *node, pgObject *parent)
{
pgSchema *sch;
if (parent->GetMetaType() == PGM_TRIGGER)
sch = parent->GetSchema();
// Event triggers are at database level. So, we do not have a schema for an event trigger.
else if(parent->GetMetaType() == PGM_EVENTTRIGGER)
sch = 0;
else
sch = (pgSchema *)parent;
return new dlgFunction(this, frame, (pgFunction *)node, sch);
}
dlgFunction::dlgFunction(pgaFactory *f, frmMain *frame, pgFunction *node, pgSchema *sch)
: dlgSecurityProperty(f, frame, node, wxT("dlgFunction"), wxT("EXECUTE"), "X"),
isEdbWrapped( false )
{
schema = sch;
function = node;
isProcedure = false;
seclabelPage = new ctlSeclabelPanel(nbNotebook);
txtArguments->Disable();
if (!node)
{
int icon = PGICON_PUBLIC;
wxString name = wxT("public");
wxString value = wxT("X");
securityPage->lbPrivileges->AppendItem(icon, name, value);
AppendCurrentAcl(name, value);
}
bool bVal;
settings->Read(wxT("frmQuery/ShowLineNumber"), &bVal, false);
if (!bVal)
{
txtSqlBox->SetMarginType(1, wxSTC_MARGIN_NUMBER);
txtSqlBox->SetMarginWidth(1, ConvertDialogToPixels(wxPoint(16, 0)).x);
}
btnAdd->Disable();
btnRemove->Disable();
btnChange->Disable();
lstVariables->CreateColumns(0, _("Variable"), _("Value"), -1);
chkValue->Hide();
}
dlgProperty *pgProcedureFactory::CreateDialog(frmMain *frame, pgObject *node, pgObject *parent)
{
dlgProcedure *p= new dlgProcedure(this, frame, (pgFunction *)node, (pgSchema *)parent);
//p->isProcedure=true;
return p;
}
dlgProcedure::dlgProcedure(pgaFactory *f, frmMain *frame, pgFunction *node, pgSchema *sch)
: dlgFunction(f, frame, node, sch)
{
isProcedure = true;
}
pgObject *dlgFunction::GetObject()
{
return function;
}
int dlgFunction::Go(bool modal)
{
isBackendMinVer84 = connection->BackendMinimumVersion(8, 4);
if (connection->BackendMinimumVersion(9, 1))
{
seclabelPage->SetConnection(connection);
seclabelPage->SetObject(function);
this->Connect(EVT_SECLABELPANEL_CHANGE, wxCommandEventHandler(dlgFunction::OnChange));
}
else
seclabelPage->Disable();
if (function)
{
cbSchema->Enable(connection->BackendMinimumVersion(8, 1));
rdbIn->Disable();
rdbOut->Disable();
rdbInOut->Disable();
rdbVariadic->Disable();
isProcedure = function->GetIsProcedure();
}
if (!isBackendMinVer84)
txtArgDefVal->Disable();
chkLeakProof->Enable(connection->BackendMinimumVersion(9, 2));
lstArguments->AddColumn(_("Type"), 60);
lstArguments->AddColumn(_("Mode"), 40);
lstArguments->AddColumn(_("Name"), 60);
lstArguments->AddColumn(_("Default Value"), 60);
if (!connection->BackendMinimumVersion(8, 0))
cbOwner->Disable();
if (!connection->BackendMinimumVersion(8, 3))
txtCost->Disable();
txtRows->Disable();
if (!connection->BackendMinimumVersion(8, 0))
txtArgName->Disable();
// Window function can not be modified
// Disable it for editing
if (function || !isBackendMinVer84)
chkWindow->Disable();
if (isProcedure)
{
if (function && !connection->EdbMinimumVersion(8, 2))
// txtName->Disable();
//cbOwner->Disable();
//cbLanguage->Disable();
chkStrict->Disable();
chkWindow->Disable();
//chkSecureDefiner->Disable();
chkSetof->Disable();
cbVolatility->Disable();
cbParallel->Disable();
cbReturntype->Disable();
txtCost->Disable();
txtRows->Disable();
chkLeakProof->Disable();
}
else
{
if (!connection->BackendMinimumVersion(8, 1))
{
rdbIn->SetValue(true);
rdbIn->Disable();
rdbOut->Disable();
rdbInOut->Disable();
}
if (!isBackendMinVer84)
{
rdbVariadic->Disable();
}
}
pgSet *lang = connection->ExecuteSet(wxT("SELECT lanname FROM pg_language"));
if (lang)
{
while (!lang->Eof())
{
wxString language = lang->GetVal(0);
if (factory == &triggerFunctionFactory)
{
if (language.IsSameAs(wxT("SQL"), false) ||
language.IsSameAs(wxT("edbspl"), false))
{
lang->MoveNext();
continue;
}
}
cbLanguage->Append(language);
lang->MoveNext();
}
delete lang;
}
if (connection->BackendMinimumVersion(8, 3))
{
pgSet *set;
set = connection->ExecuteSet(wxT("SELECT name, vartype, min_val, max_val\n")
wxT(" FROM pg_settings WHERE context in ('user', 'superuser')"));
if (set)
{
while (!set->Eof())
{
cbVarname->Append(set->GetVal(0));
varInfo.Add(set->GetVal(wxT("vartype")) + wxT(" ") +
set->GetVal(wxT("min_val")) + wxT(" ") +
set->GetVal(wxT("max_val")));
set->MoveNext();
}
delete set;
cbVarname->SetSelection(0);
SetupVarEditor(0);
}
}
else
{
btnAddVar->Disable();
btnRemoveVar->Disable();
cbVarname->Disable();
txtValue->Disable();
chkValue->Disable();
}
if (function)
{
// edit mode
if (factory != &triggerFunctionFactory)
{
wxArrayString argTypes = function->GetArgTypesArray();
wxArrayString argNames = function->GetArgNamesArray();
wxArrayString argModes = function->GetArgModesArray();
wxArrayString argDefs = function->GetArgDefsArray();
for (unsigned int i = 0; i < argTypes.Count(); i++)
{
if (argModes[i] != wxT("TABLE"))
{
if (isBackendMinVer84)
lstArguments->AppendItem(-1, argTypes.Item(i), argModes[i], argNames[i], (argDefs.Count() > i ? argDefs[i] : wxString(wxEmptyString)));
else
lstArguments->AppendItem(-1, argTypes.Item(i), argModes[i], argNames[i]);
}
}
}
txtArguments->SetValue(function->GetArgListWithNames());
if (!isProcedure)
{
cbReturntype->Append(function->GetReturnType());
cbReturntype->SetSelection(0);
//cbReturntype->SetValue(function->GetReturnType());
}
cbLanguage->SetValue(function->GetLanguage());
cbVolatility->SetValue(function->GetVolatility());
cbParallel->SetValue(function->GetParallel());
chkSetof->SetValue(function->GetReturnAsSet());
chkStrict->SetValue(function->GetIsStrict());
if (connection->BackendMinimumVersion(8, 4))
chkWindow->SetValue(function->GetIsWindow());
chkSecureDefiner->SetValue(function->GetSecureDefiner());
chkLeakProof->SetValue(function->GetIsLeakProof());
if (function->GetLanguage().IsSameAs(wxT("C"), false))
{
txtObjectFile->SetValue(function->GetBin());
txtLinkSymbol->SetValue(function->GetSource());
}
else
txtSqlBox->SetText(function->GetSource());
if (!connection->BackendMinimumVersion(7, 4))
txtName->Disable();
if (connection->BackendMinimumVersion(8, 3))
{
txtCost->SetValue(NumToStr(function->GetCost()));
if (function->GetReturnAsSet())
{
txtRows->SetValue(NumToStr(function->GetRows()));
txtRows->Enable();
}
else
txtRows->Disable();
}
size_t index;
for (index = 0 ; index < function->GetConfigList().GetCount() ; index++)
{
wxString item = function->GetConfigList().Item(index);
lstVariables->AppendItem(0, item.BeforeFirst('='), item.AfterFirst('='));
}
cbReturntype->Disable();
chkSetof->Disable();
cbDatatype->Disable();
// Editing parameter for wrapped functions is not allowed
// It will anyway throw an error, if we try to edit the parameter list
if ( connection->GetIsEdb() &&
function->GetSource().Trim(false).StartsWith( wxT( "$__EDBwrapped__$" )))
{
isEdbWrapped = true;
cbDatatype->Disable();
rdbIn->Disable();
rdbOut->Disable();
rdbInOut->Disable();
rdbVariadic->Disable();
txtArgName->Disable();
txtArgDefVal->Disable();
btnAdd->Disable();
btnChange->Disable();
btnRemove->Disable();
}
}
else
{
wxString restrict;
// create mode
restrict = wxT("(typtype IN ('b', 'c', 'd', 'e', 'p', 'r') AND typname NOT IN ('any', 'trigger', 'language_handler', 'event_trigger'))");
if (!settings->GetShowSystemObjects())
restrict += wxT(" AND nspname NOT LIKE E'pg\\\\_toast%' AND nspname NOT LIKE E'pg\\\\_temp%'");
DatatypeReader tr(database, restrict);
cbDatatype->Freeze();
cbReturntype->Freeze();
wxArrayString arr_dt,arr_rt;
wxString prevnametype;
while (tr.HasMore())
{
if (!tr.IsPartition()) {
wxString tname=tr.GetTypename();
bool isnext=true;
if (tname.Right(2)=="[]")
if (prevnametype!=tname.substr(0,tname.Length()-2)) isnext=false;
if (isnext) {
pgDatatype dt = tr.GetDatatype();
typOids.Add(tr.GetOidStr());
wxString tmp_str=dt.GetQuotedSchemaPrefix(database) + dt.QuotedFullName();
types.Add(tmp_str);
arr_dt.Add(tmp_str);
//cbDatatype->Append(dt.GetQuotedSchemaPrefix(database) + dt.QuotedFullName());
if (factory != &triggerFunctionFactory)
arr_rt.Add(tmp_str);
}
prevnametype=tname;
}
tr.MoveNext();
}
cbDatatype->Append(arr_dt);
cbReturntype->Append(arr_rt);
cbDatatype->Thaw();
cbReturntype->Thaw();
long sel;
if (factory == &triggerFunctionFactory)
{
cbReturntype->Append(wxT("trigger"));
if (connection->BackendMinimumVersion(9, 3))
cbReturntype->Append(wxT("event_trigger"));
cbReturntype->SetSelection(0);
lstArguments->Disable();
cbDatatype->Disable();
txtArgName->Disable();
txtArgDefVal->Disable();
rdbIn->Disable();
rdbOut->Disable();
rdbInOut->Disable();
rdbVariadic->Disable();
sel = cbLanguage->FindString(wxT("c"));
}
else if (isProcedure)
sel = cbLanguage->FindString(wxT("plpgsql"));
else
sel = cbLanguage->FindString(wxT("sql"));
if (sel >= 0)
cbLanguage->SetSelection(sel);
txtObjectFile->SetValue(TXTOBJ_LIB);
}
wxNotifyEvent event;
OnSelChangeLanguage(event);
return dlgSecurityProperty::Go(modal);
}
#ifdef __WXMAC__
void dlgFunction::OnChangeSize(wxSizeEvent &ev)
{
lstArguments->SetSize(wxDefaultCoord, wxDefaultCoord,
ev.GetSize().GetWidth(), ev.GetSize().GetHeight() - 350);
lstVariables->SetSize(wxDefaultCoord, wxDefaultCoord,
ev.GetSize().GetWidth(), ev.GetSize().GetHeight() - 350);
dlgSecurityProperty::OnChangeSize(ev);
}
#endif
void dlgFunction::OnVarnameSelChange(wxCommandEvent &ev)
{
int sel = cbVarname->GuessSelection(ev);
SetupVarEditor(sel);
}
void dlgFunction::SetupVarEditor(int var)
{
if (var >= 0 && varInfo.Count() > 0)
{
wxStringTokenizer vals(varInfo.Item(var));
wxString typ = vals.GetNextToken();
if (typ == wxT("bool"))
{
txtValue->Hide();
chkValue->Show();
chkValue->GetParent()->Layout();
}
else
{
chkValue->Hide();
txtValue->Show();
txtValue->GetParent()->Layout();
if (typ == wxT("string") || typ == wxT("enum"))
txtValue->SetValidator(wxTextValidator());
else
txtValue->SetValidator(numericValidator);
}
}
}
void dlgFunction::OnVarSelChange(wxListEvent &ev)
{
long pos = lstVariables->GetSelection();
if (pos >= 0)
{
wxString value = lstVariables->GetText(pos, 1);
cbVarname->SetValue(lstVariables->GetText(pos));
// We used to raise an OnVarnameSelChange() event here, but
// at this point the combo box hasn't necessarily updated.
int sel = cbVarname->FindString(lstVariables->GetText(pos));
SetupVarEditor(sel);
txtValue->SetValue(value);
chkValue->SetValue(value == wxT("on"));
}
}
void dlgFunction::OnVarAdd(wxCommandEvent &ev)
{
wxString name = cbVarname->GetValue();
wxString value;
if (chkValue->IsShown())
value = chkValue->GetValue() ? wxT("on") : wxT("off");
else
value = txtValue->GetValue().Strip(wxString::both);
if (value.IsEmpty())
value = wxT("DEFAULT");
if (!name.IsEmpty())
{
long pos = lstVariables->FindItem(-1, name);
if (pos < 0)
{
pos = lstVariables->GetItemCount();
lstVariables->InsertItem(pos, name, 0);
}
lstVariables->SetItem(pos, 1, value);
}
CheckChange();
}
void dlgFunction::OnVarRemove(wxCommandEvent &ev)
{
if (lstVariables->GetSelection() == wxNOT_FOUND)
return;
lstVariables->DeleteCurrentItem();
CheckChange();
}
pgObject *dlgFunction::CreateObject(pgCollection *collection)
{
wxString sql = wxT(" WHERE proname=") + qtDbString(GetName()) +
wxT("\n AND pronamespace=") + schema->GetOidStr();
long argCount;
for (argCount = 0 ; argCount < (int)argOids.GetCount() ; argCount++)
sql += wxT("\n AND proargtypes[") + NumToStr(argCount) + wxT("] = ") + argOids.Item(argCount);
sql += wxT("\n AND proargtypes[") + NumToStr(argCount) + wxT("] = 0\n");
pgObject *obj = functionFactory.AppendFunctions(collection, collection->GetSchema(), 0, sql);
return obj;
}
void dlgFunction::CheckChange()
{
wxString name = GetName();
bool isC = cbLanguage->GetValue().IsSameAs(wxT("C"), false);
bool enable = true, didChange = true;
CheckValid(enable, !name.IsEmpty(), _("Please specify name."));
if (!isProcedure)
CheckValid(enable, cbReturntype->GetValue().Trim() != wxEmptyString, _("Please select return type."));
CheckValid(enable, cbLanguage->GetGuessedSelection() >= 0, _("Please select language."));
if (isC)
{
wxString objfile = txtObjectFile->GetValue();
CheckValid(enable, !objfile.IsEmpty() && objfile != TXTOBJ_LIB, _("Please specify object library."));
}
else
{
CheckValid(enable, !txtSqlBox->GetText().IsEmpty(), _("Please enter function source code."));
}
if (function && enable)
{
if (seclabelPage && connection->BackendMinimumVersion(9, 1))
enable = enable || !(seclabelPage->GetSqlForSecLabels().IsEmpty());
wxString s=GetSql();
bool noempty=true;
if (s.IsEmpty()) {
noempty=false;
}
EnableOK(enable && noempty);
}
else
{
EnableOK(enable && didChange);
}
}
bool dlgFunction::IsUpToDate()
{
if (function && !function->IsUpToDate())
return false;
else
return true;
}
void dlgFunction::OnSelChangeLanguage(wxCommandEvent &ev)
{
bool isC = (cbLanguage->GetValue().IsSameAs(wxT("C"), false));
stObjectFile->Show(isC);
txtObjectFile->Show(isC);
stLinkSymbol->Show(isC);
txtLinkSymbol->Show(isC);
txtSqlBox->Show(!isC);
txtSqlBox->GetContainingSizer()->Layout();
CheckChange();
}
void dlgFunction::OnSelChangeArg(wxListEvent &ev)
{
int row = lstArguments->GetSelection();
if (row >= 0)
{
cbDatatype->SetValue(lstArguments->GetText(row, 0));
wxString mode = lstArguments->GetText(row, 1);
if (mode == wxT("IN"))
rdbIn->SetValue(true);
else if (mode == wxT("OUT"))
rdbOut->SetValue(true);
else if (mode == wxT("IN OUT") || mode == wxT("INOUT"))
rdbInOut->SetValue(true);
else if (mode == wxT("VARIADIC"))
rdbVariadic->SetValue(true);
txtArgName->SetValue(lstArguments->GetText(row, 2));
if (isBackendMinVer84)
{
txtArgDefVal->SetValue(lstArguments->GetText(row, 3));
txtArgDefVal->Enable(mode == wxT("IN") || mode.IsEmpty());
}
wxCommandEvent ev;
OnChangeArgName(ev);
}
}
void dlgFunction::OnChangeReturn(wxCommandEvent &ev)
{
cbReturntype->GuessSelection(ev);
CheckChange();
}
void dlgFunction::OnChangeSetof(wxCommandEvent &ev)
{
if (chkSetof->GetValue() && connection->BackendMinimumVersion(8, 3) && !isProcedure)
txtRows->Enable();
else
txtRows->Disable();
CheckChange();
}
void dlgFunction::OnSelChangeType(wxCommandEvent &ev)
{
cbDatatype->GuessSelection(ev);
OnChangeArgName(ev);
}
void dlgFunction::OnChangeArgName(wxCommandEvent &ev)
{
int argNameRow = -1;
if (!txtArgName->GetValue().IsEmpty())
argNameRow = lstArguments->FindItem(-1, txtArgName->GetValue());
int pos = lstArguments->GetSelection();
bool typeValid = (function != 0 || cbDatatype->GetGuessedSelection() >= 0);
// EDBWrapped function does not allow modification in parameter list
btnChange->Enable(pos >= 0 && typeValid && !isEdbWrapped);
if (!function)
{
// EDBWrapped function does not allow modification in parameter list
btnAdd->Enable(argNameRow < 0 && typeValid && !isEdbWrapped);
btnRemove->Enable(pos >= 0 && !isEdbWrapped);
}
}
void dlgFunction::OnChangeArgMode(wxCommandEvent &ev)
{
// Do nothing, if Default value for function parameter not supported
if (!isBackendMinVer84)
return;
// Only IN parameter supports default value
if (!rdbIn->GetValue())
{
txtArgDefVal->SetValue(wxEmptyString);
txtArgDefVal->Enable(false);
}
else
{
// EDBWrapped function does not allow modification in parameter list
txtArgDefVal->Enable(true && !isEdbWrapped);
}
}
void dlgFunction::OnChangeArg(wxCommandEvent &ev)
{
if (GetSelectedDirection() == wxT("VARIADIC") &&
!cbDatatype->GetValue().EndsWith(wxT("[]")))
{
wxLogError(_("Only array types can be VARIADIC."));
return;
}
int row = lstArguments->GetSelection();
if (row >= 0)
{
lstArguments->SetItem(row, 0, cbDatatype->GetValue());
lstArguments->SetItem(row, 1, GetSelectedDirection());
lstArguments->SetItem(row, 2, txtArgName->GetValue());
if (isBackendMinVer84)
lstArguments->SetItem(row, 3, txtArgDefVal->GetValue());
if (!function)
argOids.Item(row) = typOids.Item(cbDatatype->GetGuessedSelection());
txtArguments->SetValue(GetArgs());
}
OnChangeArgName(ev);
CheckChange();
}
void dlgFunction::OnAddArg(wxCommandEvent &ev)
{
if (GetSelectedDirection() == wxT("VARIADIC") &&
!cbDatatype->GetValue().EndsWith(wxT("[]")))
{
wxLogError(_("Only array types can be VARIADIC."));
return;
}
lstArguments->AppendItem(-1, cbDatatype->GetValue(), GetSelectedDirection(), txtArgName->GetValue(), txtArgDefVal->GetValue().Trim());
if (!function)
argOids.Add(typOids.Item(cbDatatype->GetGuessedSelection()));
txtArguments->SetValue(GetArgs());
OnChangeArgName(ev);
}
void dlgFunction::OnRemoveArg(wxCommandEvent &ev)
{
unsigned int sel = lstArguments->GetSelection();
argOids.RemoveAt(sel);
lstArguments->DeleteItem(sel);
btnRemove->Disable();
txtArguments->SetValue(GetArgs());
OnChangeArgName(ev);
}
wxString dlgFunction::GetSelectedDirection()
{
if (rdbIn->GetValue())
return wxT("IN");
else if (rdbOut->GetValue())
return wxT("OUT");
else if (rdbInOut->GetValue())
{
if (isProcedure)
return wxT("INOUT");
else
return wxT("INOUT");
}
else if (rdbVariadic->GetValue())
return wxT("VARIADIC");
else
return wxEmptyString;
}
wxString dlgFunction::GetArgs(const bool withNames, const bool inOnly)
{
wxString args;
bool isEdbspl = cbLanguage->GetValue() == wxT("edbspl");
for (int i = 0; i < lstArguments->GetItemCount(); i++)
{
if (!isEdbspl && inOnly && lstArguments->GetText(i, 1) == wxT("OUT"))
continue;
if (i && !args.IsEmpty() && !args.EndsWith(wxT(", ")))
args += wxT(", ");
if (isProcedure)
{
if (withNames && lstArguments->GetText(i, 2) != wxEmptyString)
args += qtIdent(lstArguments->GetText(i, 2)) + wxT(" ");
// edbspl functions should list OUT params, but only by type.
// although this is not true for EDB AS90 onwards..
if (!inOnly || !isEdbspl || lstArguments->GetText(i, 1) != wxT("OUT") ||
connection->EdbMinimumVersion(9, 0))
{
if (lstArguments->GetText(i, 1) != wxEmptyString)
args += lstArguments->GetText(i, 1) + wxT(" ");
}
args += lstArguments->GetText(i, 0);
}
else
{
// edbspl functions should list OUT params, but only by type.
// although this is not true for EDB AS90 onwards..
if (!inOnly || !isEdbspl || lstArguments->GetText(i, 1) != wxT("OUT") ||
connection->EdbMinimumVersion(9, 0))
{
if (connection->BackendMinimumVersion(8, 1) && lstArguments->GetText(i, 1) != wxEmptyString)
args += lstArguments->GetText(i, 1) + wxT(" ");
}
if (connection->BackendMinimumVersion(8, 0) && withNames && lstArguments->GetText(i, 2) != wxEmptyString)
args += qtIdent(lstArguments->GetText(i, 2)) + wxT(" ");
args += lstArguments->GetText(i, 0);
}
if (withNames && isBackendMinVer84 && !lstArguments->GetText(i, 3).IsEmpty())
args += wxT(" DEFAULT ") + lstArguments->GetText(i, 3);
}
return args;
}
wxString dlgFunction::GetSql()
{
wxString sql;
wxString name;
wxString objType;
if (isProcedure)
objType = wxT("PROCEDURE ");
else
objType = wxT("FUNCTION ");
bool isC = cbLanguage->GetValue().IsSameAs(wxT("C"), false);
bool didChange = !function
|| cbLanguage->GetValue() != function->GetLanguage() && !isProcedure
|| cbVolatility->GetValue() != function->GetVolatility() && !isProcedure
|| cbParallel->GetValue() != function->GetParallel() && !isProcedure
|| chkSecureDefiner->GetValue() != function->GetSecureDefiner()
|| chkStrict->GetValue() != function->GetIsStrict() && !isProcedure
|| GetArgs() != function->GetArgListWithNames() && !isProcedure
|| chkLeakProof->GetValue() != function->GetIsLeakProof() && !isProcedure
|| (isC && (txtObjectFile->GetValue() != function->GetBin()&& !isProcedure || txtLinkSymbol->GetValue() != function->GetSource()))
|| (!isC && txtSqlBox->GetText() != function->GetSource());
if (connection->BackendMinimumVersion(8, 3))
{
didChange = (didChange ||
txtCost->GetValue() != NumToStr(function->GetCost())&& !isProcedure ||
(chkSetof->GetValue()&& !isProcedure && txtRows->GetValue() != NumToStr(function->GetRows())));
}
if (function)
{
name = GetName();
// edit mode
if (name != function->GetName())
{
if (!isProcedure)
AppendNameChange(sql, wxT("FUNCTION ") + function->GetQuotedFullIdentifier()
+ wxT("(") + function->GetArgSigList() + wxT(")"));
else
AppendNameChange(sql, wxT("PROCEDURE ") + function->GetQuotedFullIdentifier());
}
if (didChange)
sql += wxT("CREATE OR REPLACE ") + objType;
}
else
{
name = qtIdent(cbSchema->GetValue()) + wxT(".") + qtIdent(GetName());
// create mode
sql = wxT("CREATE " ) + objType;
}
if (didChange)
{
if (isProcedure && GetArgs().IsEmpty())
{
sql += schema->GetQuotedPrefix() + qtIdent(GetName()) + wxT("()");
}
else
{
// While creating trigger functions from the Event trigger, we may get the schema as null.
// Since, Event triggers are at database level.
if (schema)
sql += schema->GetQuotedPrefix() + qtIdent(GetName())
+ wxT("(") + GetArgs() + wxT(")");
else if(!function)
sql += qtIdent(cbSchema->GetValue()) + wxT(".") + qtIdent(GetName())
+ wxT("(") + GetArgs() + wxT(")");
else
sql += function->GetSchema()->GetQuotedPrefix() + qtIdent(GetName())
+ wxT("(") + GetArgs() + wxT(")");
}
//sql += wxT("\n LANGUAGE ") + GetLanguage() + wxT(" ");
if (!isProcedure)
{
sql += wxT(" RETURNS ");
sql += cbReturntype->GetValue();
}
sql += wxT(" AS\n");
if (isProcedure)
{
sql += qtDbStringDollar(txtSqlBox->GetText());
sql = sql.Trim(true);
sql += wxT("\nLANGUAGE ") + cbLanguage->GetValue();
if (!sql.EndsWith(wxT(";")))
sql += wxT(";\n");
else
sql += wxT("\n");
}
else
{
if (cbLanguage->GetValue().IsSameAs(wxT("C"), false))
{
sql += qtDbString(txtObjectFile->GetValue());
if (!txtLinkSymbol->GetValue().IsEmpty())
sql += wxT(", ") + qtDbString(txtLinkSymbol->GetValue());
}
else
{
if (connection->BackendMinimumVersion(7, 5))
sql += qtDbStringDollar(txtSqlBox->GetText());
else
sql += qtDbString(txtSqlBox->GetText());
}
sql += wxT("\nLANGUAGE ") + cbLanguage->GetValue();
if (chkWindow->GetValue())
sql += wxT(" WINDOW ");
else
sql += wxT(" ");
sql += cbVolatility->GetValue();
if (connection->BackendMinimumVersion(9, 2))
{
if (!chkLeakProof->GetValue())
sql += wxT(" NOT");
sql += wxT(" LEAKPROOF");
}
if (chkStrict->GetValue())
sql += wxT(" STRICT");
if (chkSecureDefiner->GetValue())
sql += wxT(" SECURITY DEFINER");
if (connection->BackendMinimumVersion(10, 0))
{
sql += wxT(" PARALLEL ");
sql += cbParallel->GetValue();
}
// PostgreSQL 8.3+ cost/row estimations
if (connection->BackendMinimumVersion(8, 3))
{
if (txtCost->GetValue().Length() > 0)
sql += wxT("\nCOST ") + txtCost->GetValue();
if (chkSetof->GetValue() && txtRows->GetValue().Length() > 0)
sql += wxT("\nROWS ") + txtRows->GetValue();
}
sql += wxT(";\n");
}
}
if (function )
{
if (schema)
name = schema->GetQuotedPrefix() + qtIdent(name) + wxT("(") + GetArgs(false, true) + wxT(")");
// Event triggers do not have it's schema definition. Hence, getting the schema from the trigger function.
else
name = function->GetSchema()->GetQuotedPrefix() + qtIdent(name)
+ wxT("(") + GetArgs(false, true) + wxT(")");
AppendOwnerChange(sql, objType + name);
AppendSchemaChange(sql, objType + name);
}
//else if (function && isProcedure)
//{
// name = schema->GetQuotedPrefix() + qtIdent(name);
// AppendOwnerChange(sql, wxT("PROCEDURE ") + name);
// AppendSchemaChange(sql, wxT("PROCEDURE ") + name);
//}
else
{
name = name + wxT("(") + GetArgs(false, true) + wxT(")");
if (cbOwner->GetCurrentSelection() > 0)
if (!isProcedure) AppendOwnerNew(sql, wxT("FUNCTION ") + name);
else AppendOwnerNew(sql, wxT("PROCEDURE ") + name);
}
if (false)
sql += GetGrant(wxT("X"), wxT("PROCEDURE ") + name);
else
{
wxArrayString vars;
size_t index;
if (isProcedure) sql += GetGrant(wxT("X"), objType + name);
if (function)
{
for (index = 0 ; index < function->GetConfigList().GetCount() ; index++)
vars.Add(function->GetConfigList().Item(index));
}
int cnt = lstVariables->GetItemCount();
int pos;
// check for changed or added vars
for (pos = 0 ; pos < cnt ; pos++)
{
wxString newVar = lstVariables->GetText(pos);
wxString newVal = lstVariables->GetText(pos, 1);
wxString oldVal;
for (index = 0 ; index < vars.GetCount() ; index++)
{
wxString var = vars.Item(index);
if (var.BeforeFirst('=').IsSameAs(newVar, false))
{
oldVal = var.Mid(newVar.Length() + 1);
vars.RemoveAt(index);
break;
}
}
// Reset the vars if they've changed, or the function definition has
// changed, which will remove them all :-(
if ((oldVal != newVal) || didChange)
{
if (newVar != wxT("search_path") && newVar != wxT("temp_tablespaces"))
sql += wxT("ALTER ")+ objType + name
+ wxT("\n SET ") + newVar
+ wxT("='") + newVal
+ wxT("';\n");
else
sql += wxT("ALTER ")+ objType + name
+ wxT("\n SET ") + newVar
+ wxT("=") + newVal
+ wxT(";\n");
}
}
// check for removed vars
for (pos = 0 ; pos < (int)vars.GetCount() ; pos++)
{
sql += wxT("ALTER ")+ objType + name
+ wxT("\n RESET ") + vars.Item(pos).BeforeFirst('=')
+ wxT(";\n");
}
sql += GetGrant(wxT("X"), objType + name);
}
if (isProcedure)
AppendComment(sql, wxT("PROCEDURE ") + qtIdent(cbSchema->GetValue()) + wxT(".") + qtIdent(GetName()), function);
else
{
AppendComment(sql, wxT("FUNCTION ") + name, function);
if (seclabelPage && connection->BackendMinimumVersion(9, 1))
sql += seclabelPage->GetSqlForSecLabels(wxT("FUNCTION"), name);
}
return sql;
}
void dlgFunction::OnChangeWindow(wxCommandEvent &ev)
{
CheckChange();
}
void dlgFunction::OnChange(wxCommandEvent &event)
{
CheckChange();
}