mirror of
https://github.com/levinsv/pgadmin3.git
synced 2026-05-15 14:15:49 -06:00
Добавлено использование автоподстановки в окно редактирования процедур и функции. Добавлена поддержка UTF-8. Добавлена поддержка unicode для идентификаторов. Добавлено сохранение выбранных опций в диалоге Выравнивания.
1153 lines
32 KiB
C++
1153 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);
|
|
}
|
|
if (function && function->GetDatabase())
|
|
txtSqlBox->SetDatabase(function->GetDatabase()->GetConnection());
|
|
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();
|
|
}
|