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

1432 lines
40 KiB
C++

//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// dlgType.cpp - PostgreSQL TYPE Property
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
// App headers
#include "utils/misc.h"
#include "dlg/dlgType.h"
#include "schema/pgSchema.h"
#include "schema/pgType.h"
#include "schema/pgDatatype.h"
#include "ctl/ctlSeclabelPanel.h"
#include "frm/frmMain.h"
#include "schema/pgUser.h"
#include "schema/pgGroup.h"
// pointer to controls
#define rdbType CTRL_RADIOBOX("rdbType")
#define cbInput CTRL_COMBOBOX("cbInput")
#define cbOutput CTRL_COMBOBOX("cbOutput")
#define cbReceive CTRL_COMBOBOX("cbReceive")
#define cbSend CTRL_COMBOBOX("cbSend")
#define cbTypmodin CTRL_COMBOBOX("cbTypmodin")
#define cbTypmodout CTRL_COMBOBOX("cbTypmodout")
#define cbAnalyze CTRL_COMBOBOX("cbAnalyze")
#define cbCategory CTRL_COMBOBOX("cbCategory")
#define chkPrefered CTRL_CHECKBOX("chkPrefered")
#define chkVariable CTRL_CHECKBOX("chkVariable")
#define txtIntLength CTRL_TEXT("txtIntLength")
#define txtDefault CTRL_TEXT("txtDefault")
#define cbElement CTRL_COMBOBOX2("cbElement")
#define txtDelimiter CTRL_TEXT("txtDelimiter")
#define chkByValue CTRL_CHECKBOX("chkByValue")
#define cbAlignment CTRL_COMBOBOX("cbAlignment")
#define cbStorage CTRL_COMBOBOX("cbStorage")
#define lstMembers CTRL_LISTVIEW("lstMembers")
#define txtMembername CTRL_TEXT("txtMembername")
#define lstLabels CTRL_LISTVIEW("lstLabels")
#define txtLabel CTRL_TEXT("txtLabel")
#define btnAddMember CTRL_BUTTON("btnAddMember")
#define btnChangeMember CTRL_BUTTON("btnChangeMember")
#define btnRemoveMember CTRL_BUTTON("btnRemoveMember")
#define btnAddAfterLabel CTRL_BUTTON("btnAddAfterLabel")
#define btnAddBeforeLabel CTRL_BUTTON("btnAddBeforeLabel")
#define btnRemoveLabel CTRL_BUTTON("btnRemoveLabel")
#define pnlDefinition CTRL_PANEL("pnlDefinition")
#define pnlDefinitionExtern CTRL_PANEL("pnlDefinitionExtern")
#define pnlDefinitionComposite CTRL_PANEL("pnlDefinitionComposite")
#define pnlDefinitionEnum CTRL_PANEL("pnlDefinitionEnum")
#define pnlDefinitionRange CTRL_PANEL("pnlDefinitionRange")
#define chkCollatable CTRL_CHECKBOX("chkCollatable")
#define cbCollation CTRL_COMBOBOX("cbCollation")
#define cbSubtype CTRL_COMBOBOX("cbSubtype")
#define cbSubtypeOpclass CTRL_COMBOBOX("cbSubtypeOpclass")
#define cbRngCollation CTRL_COMBOBOX("cbRngCollation")
#define cbCanonical CTRL_COMBOBOX("cbCanonical")
#define cbSubtypeDiff CTRL_COMBOBOX("cbSubtypeDiff")
BEGIN_EVENT_TABLE(dlgType, dlgTypeProperty)
EVT_RADIOBOX(XRCID("rdbType"), dlgType::OnTypeChange)
EVT_TEXT(XRCID("cbInput"), dlgProperty::OnChange)
EVT_COMBOBOX(XRCID("cbInput"), dlgProperty::OnChange)
EVT_TEXT(XRCID("cbOutput"), dlgProperty::OnChange)
EVT_COMBOBOX(XRCID("cbOutput"), dlgProperty::OnChange)
EVT_TEXT(XRCID("txtIntLength"), dlgProperty::OnChange)
EVT_CHECKBOX(XRCID("chkVariable"), dlgProperty::OnChange)
EVT_BUTTON(XRCID("btnAddMember"), dlgType::OnMemberAdd)
EVT_BUTTON(XRCID("btnChangeMember"), dlgType::OnMemberChange)
EVT_BUTTON(XRCID("btnRemoveMember"), dlgType::OnMemberRemove)
EVT_BUTTON(XRCID("btnAddBeforeLabel"), dlgType::OnLabelAddBefore)
EVT_BUTTON(XRCID("btnAddAfterLabel"), dlgType::OnLabelAddAfter)
EVT_BUTTON(XRCID("btnRemoveLabel"), dlgType::OnLabelRemove)
EVT_LIST_ITEM_SELECTED(XRCID("lstMembers"), dlgType::OnMemberSelChange)
EVT_LIST_ITEM_SELECTED(XRCID("lstLabels"), dlgType::OnLabelSelChange)
EVT_TEXT(XRCID("cbDatatype"), dlgType::OnSelChangeTyp)
EVT_COMBOBOX(XRCID("cbDatatype"), dlgType::OnSelChangeTyp)
EVT_TEXT(XRCID("txtMembername"), dlgType::OnChangeMember)
EVT_TEXT(XRCID("txtLength"), dlgType::OnSelChangeTypOrLen)
EVT_TEXT(XRCID("txtPrecision"), dlgType::OnSelChangeTypOrLen)
EVT_BUTTON(CTL_ADDPRIV, dlgType::OnAddPriv)
EVT_BUTTON(CTL_DELPRIV, dlgType::OnDelPriv)
EVT_TEXT(XRCID("cbSubtype"), dlgType::OnSubtypeChange)
EVT_COMBOBOX(XRCID("cbSubtype"), dlgType::OnSubtypeChange)
EVT_TEXT(XRCID("txtName"), dlgType::OnNameChange)
#ifdef __WXMAC__
EVT_SIZE( dlgType::OnChangeSize)
#endif
END_EVENT_TABLE();
dlgProperty *pgTypeFactory::CreateDialog(frmMain *frame, pgObject *node, pgObject *parent)
{
return new dlgType(this, frame, (pgType *)node, (pgSchema *)parent);
}
dlgType::dlgType(pgaFactory *f, frmMain *frame, pgType *node, pgSchema *sch)
: dlgTypeProperty(f, frame, wxT("dlgType"))
{
type = node;
schema = sch;
seclabelPage = new ctlSeclabelPanel(nbNotebook);
lstMembers->CreateColumns(0, _("Member"), _("Data type"), _("Collation"), -1);
lstLabels->InsertColumn(0, _("Label"), wxLIST_FORMAT_LEFT, GetClientSize().GetWidth());
cbStorage->Append(wxT("PLAIN"));
cbStorage->Append(wxT("MAIN"));
cbStorage->Append(wxT("EXTERNAL"));
cbStorage->Append(wxT("EXTENDED"));
queriesToBeSplitted = false;
/* Type Privileges */
securityChanged = false;
if (node)
connection = node->GetConnection();
securityPage = new ctlSecurityPanel(nbNotebook, wxT("USAGE"), "U", frame->GetImageList());
if (connection && connection->BackendMinimumVersion(9, 2) && (!node || node->CanCreate()))
{
// Fetch Groups Information
pgSet *setGrp = connection->ExecuteSet(wxT("SELECT groname FROM pg_group ORDER BY groname"));
if (setGrp)
{
while (!setGrp->Eof())
{
groups.Add(setGrp->GetVal(0));
setGrp->MoveNext();
}
delete setGrp;
}
if (node)
{
wxString strAcl = node->GetAcl();
if (!strAcl.IsEmpty())
{
wxArrayString aclArray;
strAcl = strAcl.Mid(1, strAcl.Length() - 2);
getArrayFromCommaSeparatedList(strAcl, aclArray);
wxString roleName;
for (unsigned int index = 0; index < aclArray.Count(); index++)
{
wxString strCurrAcl = aclArray[index];
/*
* In rare case, we can have ',' (comma) in the user name.
* But, we need to handle them also
*/
if (strCurrAcl.Find(wxChar('=')) == wxNOT_FOUND)
{
// Check it is start of the ACL
if (strCurrAcl[0U] == (wxChar)'"')
roleName = strCurrAcl + wxT(",");
continue;
}
else
strCurrAcl = roleName + strCurrAcl;
if (strCurrAcl[0U] == (wxChar)'"')
strCurrAcl = strCurrAcl.Mid(1, strCurrAcl.Length() - 1);
roleName = strCurrAcl.BeforeLast('=');
wxString value = strCurrAcl.Mid(roleName.Length() + 1).BeforeLast('/');
int icon = userFactory.GetIconId();
if (roleName.Left(6).IsSameAs(wxT("group "), false))
{
icon = groupFactory.GetIconId();
roleName = wxT("group ") + qtStrip(roleName.Mid(6));
}
else if (roleName.IsEmpty())
{
icon = PGICON_PUBLIC;
roleName = wxT("public");
}
else
{
roleName = qtStrip(roleName);
for (unsigned int index = 0; index < groups.Count(); index++)
if (roleName == groups[index])
{
roleName = wxT("group ") + roleName;
icon = groupFactory.GetIconId();
break;
}
}
securityPage->lbPrivileges->AppendItem(icon, roleName, value);
currentAcl.Add(roleName + wxT("=") + value);
// Reset roleName
roleName.Empty();
}
}
}
}
else
securityPage->Disable();
}
#ifdef __WXMAC__
void dlgType::OnChangeSize(wxSizeEvent &ev)
{
securityPage->lbPrivileges->SetSize(wxDefaultCoord, wxDefaultCoord,
ev.GetSize().GetWidth(), ev.GetSize().GetHeight() - 550);
if (GetAutoLayout())
{
Layout();
}
}
#endif
void dlgType::OnChangeMember(wxCommandEvent &ev)
{
wxString name = txtMembername->GetValue().Strip(wxString::both);
btnAddMember->Enable(
((type && connection->BackendMinimumVersion(9, 1)) || !type)
&& !name.IsEmpty()
&& lstMembers->FindItem(-1, name, false) == -1
&& cbDatatype->GetGuessedSelection() >= 0);
btnChangeMember->Enable(
lstMembers->FindItem(-1, name, false) == lstMembers->GetFirstSelected()
|| lstMembers->FindItem(-1, name, false) == -1);
}
void dlgType::showDefinition(int panel)
{
pnlDefinitionExtern->Show(false);
pnlDefinitionComposite->Show(false);
pnlDefinitionEnum->Show(false);
pnlDefinitionRange->Show(false);
switch (panel)
{
case 0:
pnlDefinitionComposite->Show(true);
break;
case 1:
pnlDefinitionEnum->Show(true);
break;
case 2:
pnlDefinitionExtern->Show(true);
break;
case 3:
pnlDefinitionRange->Show(true);
break;
}
pnlDefinitionComposite->GetParent()->Layout();
// we don't need to call GetParent()->Layout() for all four panels
// because they all share the same parent
}
void dlgType::OnTypeChange(wxCommandEvent &ev)
{
showDefinition(rdbType->GetSelection());
CheckChange();
}
pgObject *dlgType::GetObject()
{
return type;
}
int dlgType::Go(bool modal)
{
pgSet *set;
cbRngCollation->Enable(false);
if (connection->BackendMinimumVersion(9, 2))
{
securityPage->SetConnection(connection);
if (securityPage->cbGroups)
{
// Fetch Groups Information
for ( unsigned int index = 0; index < groups.Count();)
securityPage->cbGroups->Append(wxT("group ") + groups[index++]);
// Fetch Users Information
if (settings->GetShowUsersForPrivileges())
{
securityPage->stGroup->SetLabel(_("Group/User"));
dlgProperty::AddUsers(securityPage->cbGroups);
}
}
securityPage->lbPrivileges->GetParent()->Layout();
// Load the range combox (the 403 opcmethod is btree)
set = connection->ExecuteSet(
wxT("SELECT DISTINCT typ.typname\n")
wxT(" FROM pg_opclass opc\n")
wxT(" JOIN pg_type typ ON opc.opcintype=typ.oid\n")
wxT(" WHERE opc.opcmethod=403\n")
wxT(" ORDER BY typname"));
if (set)
{
while (!set->Eof())
{
wxString name = qtTypeIdent(set->GetVal(wxT("typname")));
cbSubtype->Append(name);
set->MoveNext();
}
delete set;
}
cbSubtype->SetSelection(0);
}
else
{
rdbType->Enable(3, false);
}
if (connection->BackendMinimumVersion(9, 1))
{
seclabelPage->SetConnection(connection);
seclabelPage->SetObject(type);
this->Connect(EVT_SECLABELPANEL_CHANGE, wxCommandEventHandler(dlgType::OnChange));
}
else
seclabelPage->Disable();
FillDatatype(cbDatatype, cbElement);
if (connection->BackendMinimumVersion(9, 1))
{
// fill collation combobox
cbCollation->Append(wxEmptyString);
cbRngCollation->Append(wxEmptyString);
set = connection->ExecuteSet(
wxT("SELECT nspname, collname\n")
wxT(" FROM pg_collation c, pg_namespace n\n")
wxT(" WHERE c.collnamespace=n.oid\n")
wxT(" ORDER BY nspname, collname"));
if (set)
{
while (!set->Eof())
{
wxString name = qtIdent(set->GetVal(wxT("nspname"))) + wxT(".") + qtIdent(set->GetVal(wxT("collname")));
cbCollation->Append(name);
cbRngCollation->Append(name);
set->MoveNext();
}
delete set;
}
cbCollation->SetSelection(0);
cbRngCollation->SetSelection(0);
}
if (type)
{
// Edit Mode
cbSchema->Enable(connection->BackendMinimumVersion(8, 1));
txtName->Enable(connection->BackendMinimumVersion(8, 4));
rdbType->SetSelection(type->GetTypeClass());
rdbType->Disable();
showDefinition(type->GetTypeClass());
cbInput->Append(type->GetInputFunction());
cbInput->SetSelection(0);
cbInput->Disable();
cbOutput->Append(type->GetOutputFunction());
cbOutput->SetSelection(0);
cbOutput->Disable();
cbReceive->Append(type->GetReceiveFunction());
cbReceive->SetSelection(0);
cbReceive->Disable();
cbSend->Append(type->GetSendFunction());
cbSend->SetSelection(0);
cbSend->Disable();
cbTypmodin->Append(type->GetTypmodinFunction());
cbTypmodin->SetSelection(0);
cbTypmodin->Disable();
cbTypmodout->Append(type->GetTypmodoutFunction());
cbTypmodout->SetSelection(0);
cbTypmodout->Disable();
cbAnalyze->Append(type->GetAnalyzeFunction());
cbAnalyze->SetSelection(0);
cbAnalyze->Disable();
cbCategory->Append(catGetText(type->GetCategory()));
cbCategory->SetSelection(0);
cbCategory->Disable();
chkPrefered->SetValue(type->GetPrefered());
chkPrefered->Disable();
chkVariable->SetValue(type->GetInternalLength() < 0);
chkVariable->Disable();
if (type->GetInternalLength() > 0)
txtIntLength->SetValue(NumToStr(type->GetInternalLength()));
txtIntLength->Disable();
txtDefault->SetValue(type->GetDefault());
txtDefault->Disable();
cbElement->Append(type->GetElement());
cbElement->SetSelection(0);
cbElement->Disable();
txtDelimiter->SetValue(type->GetDelimiter());
txtDelimiter->Disable();
chkByValue->SetValue(type->GetPassedByValue());
chkByValue->Disable();
cbAlignment->SetValue(type->GetAlignment());
cbAlignment->Disable();
cbStorage->SetValue(type->GetStorage());
cbStorage->Disable();
chkCollatable->SetValue(type->GetCollatable());
chkCollatable->Disable();
bool changeok = connection->BackendMinimumVersion(9, 1);
txtMembername->Enable(changeok);
cbCollation->Enable(changeok);
btnAddMember->Enable(false);
btnChangeMember->Enable(false);
btnRemoveMember->Enable(false);
txtLabel->Enable(connection->BackendMinimumVersion(9, 1));
btnAddBeforeLabel->Enable(connection->BackendMinimumVersion(9, 1));
btnAddAfterLabel->Enable(connection->BackendMinimumVersion(9, 1));
btnRemoveLabel->Disable();
wxArrayString elements = type->GetTypesArray();
wxString fullType, typeName, typeLength, typePrecision;
size_t pos;
size_t i;
for (i = 0 ; i < elements.GetCount() ; i += 3)
{
lstMembers->AppendItem(0, elements.Item(i), elements.Item(i + 1), elements.Item(i + 2));
fullType = elements.Item(i + 1);
typeName = fullType;
typeLength = wxEmptyString;
typePrecision = wxEmptyString;
if (fullType.Find(wxT("(")) > 0)
{
// there is at least a length
typeName = fullType.BeforeFirst('(');
if (fullType.Find(wxT(",")) > 0)
{
// there is also a precision
typeLength = fullType.AfterFirst('(').BeforeFirst(',');
typePrecision = fullType.AfterFirst(',').BeforeFirst(')');
}
else
typeLength = fullType.AfterFirst('(').BeforeFirst(')');
}
for (pos = 0; pos < cbDatatype->GetCount() - 1; pos++)
{
if (cbDatatype->GetString(pos) == typeName)
{
memberTypes.Add(GetTypeInfo(pos));
break;
}
}
memberLengths.Add(typeLength);
memberPrecisions.Add(typePrecision);
memberCollations.Add(elements.Item(i + 2));
memberOriginalNames.Add(elements.Item(i));
}
cbDatatype->Enable(changeok);
txtLength->Enable(changeok);
// Load the enum labels
elements = type->GetLabelArray();
for (i = 0 ; i < elements.GetCount() ; i++)
lstLabels->AppendItem(0, elements.Item(i));
// Load the RANGE informations
cbSubtype->SetValue(type->GetSubtypeFunctionStr());
cbSubtype->Disable();
cbSubtypeOpclass->Append(type->GetSubtypeOpClassFunctionStr());
cbSubtypeOpclass->SetSelection(0);
cbSubtypeOpclass->Disable();
cbRngCollation->Append(type->GetCollationFunctionStr());
cbRngCollation->SetSelection(0);
cbRngCollation->Disable();
cbCanonical->Append(type->GetCanonical());
cbCanonical->SetSelection(0);
cbCanonical->Disable();
cbSubtypeDiff->Append(type->GetSubtypeDiff());
cbSubtypeDiff->SetSelection(0);
cbSubtypeDiff->Disable();
if (!connection->BackendMinimumVersion(7, 5))
cbOwner->Disable();
}
else
{
// Create mode
cbOwner->Disable();
bool hasSendRcv = connection->BackendMinimumVersion(7, 4);
bool hasTypmod = connection->BackendMinimumVersion(8, 3);
cbCategory->Enable(connection->BackendMinimumVersion(8, 4));
chkPrefered->Enable(connection->BackendMinimumVersion(8, 4));
if (hasSendRcv)
{
cbReceive->Append(wxEmptyString);
cbSend->Append(wxEmptyString);
cbAnalyze->Append(wxEmptyString);
}
else
{
cbReceive->Disable();
cbSend->Disable();
cbAnalyze->Disable();
}
if (hasTypmod)
{
cbTypmodin->Append(wxEmptyString);
cbTypmodout->Append(wxEmptyString);
}
else
{
cbTypmodin->Disable();
cbTypmodout->Disable();
}
if (!connection->BackendMinimumVersion(8, 3))
rdbType->Enable(TYPE_ENUM, false);
chkCollatable->Enable(connection->BackendMinimumVersion(9, 1));
cbCollation->Enable(connection->BackendMinimumVersion(9, 1));
set = connection->ExecuteSet(
wxT("SELECT proname, nspname\n")
wxT(" FROM (\n")
wxT(" SELECT proname, nspname, max(proargtypes[0]) AS arg0, max(proargtypes[1]) AS arg1\n")
wxT(" FROM pg_proc p\n")
wxT(" JOIN pg_namespace n ON n.oid=pronamespace\n")
wxT(" GROUP BY proname, nspname\n")
wxT(" HAVING count(proname) = 1 ) AS uniquefunc\n")
wxT(" WHERE arg0 <> 0 AND arg1 = 0"));
if (set)
{
while (!set->Eof())
{
wxString pn = database->GetSchemaPrefix(set->GetVal(wxT("nspname"))) + set->GetVal(wxT("proname"));
cbInput->Append(pn);
cbOutput->Append(pn);
if (hasSendRcv)
{
cbReceive->Append(pn);
cbSend->Append(pn);
cbAnalyze->Append(pn);
}
if (hasTypmod)
{
cbTypmodin->Append(pn);
cbTypmodout->Append(pn);
}
set->MoveNext();
}
delete set;
}
if (hasTypmod)
{
set = connection->ExecuteSet(
wxT("SELECT proname, nspname\n")
wxT(" FROM pg_proc p\n")
wxT(" JOIN pg_namespace n ON n.oid=pronamespace\n")
wxT(" WHERE prorettype=(SELECT oid FROM pg_type WHERE typname='int4')")
wxT(" AND proargtypes[0]=(SELECT oid FROM pg_type WHERE typname='_cstring')")
wxT(" AND proargtypes[1] IS NULL")
wxT(" ORDER BY nspname, proname"));
if (set)
{
while (!set->Eof())
{
wxString pn = database->GetSchemaPrefix(set->GetVal(wxT("nspname"))) + set->GetVal(wxT("proname"));
cbTypmodin->Append(pn);
set->MoveNext();
}
delete set;
}
set = connection->ExecuteSet(
wxT("SELECT proname, nspname\n")
wxT(" FROM pg_proc p\n")
wxT(" JOIN pg_namespace n ON n.oid=pronamespace\n")
wxT(" WHERE prorettype=(SELECT oid FROM pg_type WHERE typname='cstring')")
wxT(" AND proargtypes[0]=(SELECT oid FROM pg_type WHERE typname='int4')")
wxT(" AND proargtypes[1] IS NULL")
wxT(" ORDER BY nspname, proname"));
if (set)
{
while (!set->Eof())
{
wxString pn = database->GetSchemaPrefix(set->GetVal(wxT("nspname"))) + set->GetVal(wxT("proname"));
cbTypmodout->Append(pn);
set->MoveNext();
}
delete set;
}
}
}
txtLength->SetValidator(numericValidator);
return dlgTypeProperty::Go(modal);
}
void dlgType::OnSelChangeTyp(wxCommandEvent &ev)
{
txtLength->SetValue(wxEmptyString);
txtPrecision->SetValue(wxEmptyString);
cbDatatype->GuessSelection(ev);
cbCollation->SetValue(wxEmptyString);
OnSelChangeTypOrLen(ev);
}
void dlgType::OnNameChange(wxCommandEvent &ev)
{
wxString qry;
wxString shelltype;
pgSet *set;
cbCanonical->Clear();
if (!txtName->GetValue().IsEmpty())
{
// Grab the shelltype
qry = wxT("SELECT oid FROM pg_type WHERE typname='") + qtIdent(txtName->GetValue()) + wxT("'");
set = connection->ExecuteSet(qry);
if (set)
{
if (!set->Eof())
{
shelltype = set->GetVal(wxT("oid"));
}
delete set;
}
if (!shelltype.IsEmpty())
{
// Load the canonical functions
qry = wxT("SELECT proname, nspname\n")
wxT(" FROM pg_proc\n")
wxT(" JOIN pg_namespace n ON n.oid=pronamespace\n")
wxT(" WHERE prorettype=") + shelltype +
wxT(" AND proargtypes='") + shelltype + wxT("'\n")
wxT(" ORDER BY proname\n");
cbCanonical->Append(wxEmptyString);
set = connection->ExecuteSet(qry);
if (set)
{
while (!set->Eof())
{
wxString procname = database->GetSchemaPrefix(set->GetVal(wxT("nspname"))) + set->GetVal(wxT("proname"));
cbCanonical->Append(procname);
set->MoveNext();
}
delete set;
}
cbCanonical->SetSelection(0);
}
}
CheckChange();
}
void dlgType::OnSubtypeChange(wxCommandEvent &ev)
{
wxString subtypeoid;
cbSubtypeOpclass->Clear();
cbSubtypeDiff->Clear();
if (!cbSubtype->GetValue().IsEmpty())
{
cbSubtypeOpclass->Append(wxEmptyString);
pgSet *set = connection->ExecuteSet(
wxT("SELECT opc.opcname, opc.opcintype\n")
wxT(" FROM pg_opclass opc\n")
wxT(" JOIN pg_type typ ON opc.opcintype=typ.oid ")
wxT(" AND typ.typname='") + cbSubtype->GetValue() + wxT("'\n")
wxT(" WHERE opc.opcmethod=403\n")
wxT(" ORDER BY opcname"));
if (set)
{
while (!set->Eof())
{
wxString name = qtIdent(set->GetVal(wxT("opcname")));
subtypeoid = set->GetVal(wxT("opcintype"));
cbSubtypeOpclass->Append(name);
set->MoveNext();
}
delete set;
}
cbSubtypeOpclass->SetSelection(0);
set = connection->ExecuteSet(
wxT("SELECT typcollation FROM pg_type WHERE typname='") + cbSubtype->GetValue() + wxT("'\n"));
if (set)
{
if (!set->Eof())
{
cbRngCollation->Enable(set->GetLong(wxT("typcollation")) > 0);
}
delete set;
}
else
{
cbRngCollation->Enable(false);
}
// Load the subtypediff functions (701 is double precision type)
set = connection->ExecuteSet(
wxT("SELECT proname, nspname\n")
wxT(" FROM pg_proc\n")
wxT(" JOIN pg_namespace n ON n.oid=pronamespace\n")
wxT(" WHERE prorettype=701 ")
wxT(" AND proargtypes='") + subtypeoid + wxT(" ") + subtypeoid + wxT("'\n")
wxT(" ORDER BY proname\n"));
if (set)
{
while (!set->Eof())
{
wxString procname = database->GetSchemaPrefix(set->GetVal(wxT("nspname"))) + set->GetVal(wxT("proname"));
cbSubtypeDiff->Append(procname);
set->MoveNext();
}
delete set;
}
}
}
void dlgType::OnSelChangeTypOrLen(wxCommandEvent &ev)
{
if ((type && connection->BackendMinimumVersion(9, 1)) || !type)
{
CheckLenEnable();
txtLength->Enable(isVarLen);
txtPrecision->Enable(isVarPrec);
cbCollation->Enable(connection->BackendMinimumVersion(9, 1));
CheckChange();
OnChangeMember(ev);
}
}
void dlgType::CheckChange()
{
bool enable = true;
if (rdbType->GetSelection() == TYPE_COMPOSITE)
{
CheckValid(enable, lstMembers->GetItemCount() > 1, _("Please specify at least two members."));
}
else if (rdbType->GetSelection() == TYPE_ENUM)
{
CheckValid(enable, lstLabels->GetItemCount() >= 1, _("Please specify at least one label."));
}
else if (rdbType->GetSelection() == TYPE_EXTERNAL)
{
txtLength->Enable(!chkVariable->GetValue());
CheckValid(enable, cbInput->GetCurrentSelection() >= 0, _("Please specify input conversion function."));
CheckValid(enable, cbOutput->GetCurrentSelection() >= 0, _("Please specify output conversion function."));
CheckValid(enable, chkVariable->GetValue() || StrToLong(txtLength->GetValue()) > 0, _("Please specify internal storage length."));
}
else
{
CheckValid(enable, cbSubtype->GetCurrentSelection() >= 0, _("Please specify subtype function."));
}
if (type)
{
enable = enable && (GetName() != type->GetName()
|| txtComment->GetValue() != type->GetComment()
|| cbSchema->GetValue() != type->GetSchema()->GetName()
|| cbOwner->GetValue() != type->GetOwner()
|| (rdbType->GetSelection() == TYPE_COMPOSITE && GetSqlForTypes() != wxEmptyString)
|| (GetSql().Length() > 0 && connection->BackendMinimumVersion(9, 1)));
if (seclabelPage && connection->BackendMinimumVersion(9, 1))
enable = enable || !(seclabelPage->GetSqlForSecLabels().IsEmpty());
}
else
{
wxString name = GetName();
CheckValid(enable, !name.IsEmpty(), _("Please specify name."));
CheckValid(enable, !name.StartsWith(wxT("_")), _("Name may not start with '_'."));
}
EnableOK(enable || securityChanged);
}
void dlgType::OnMemberSelChange(wxListEvent &ev)
{
long pos = lstMembers->GetSelection();
if (pos >= 0)
{
txtMembername->SetValue(lstMembers->GetText(pos));
cbDatatype->SetValue(memberTypes.Item(pos).AfterFirst(':'));
txtLength->SetValue(memberLengths.Item(pos));
txtLength->Enable(((type && connection->BackendMinimumVersion(9, 1)) || !type) && !txtLength->GetValue().IsEmpty());
txtPrecision->SetValue(memberPrecisions.Item(pos));
txtPrecision->Enable(((type && connection->BackendMinimumVersion(9, 1)) || !type) && !txtPrecision->GetValue().IsEmpty());
cbCollation->SetValue(memberCollations.Item(pos));
cbCollation->Enable(connection->BackendMinimumVersion(9, 1));
btnChangeMember->Enable((type && connection->BackendMinimumVersion(9, 1)) || !type);
btnRemoveMember->Enable((type && connection->BackendMinimumVersion(9, 1)) || !type);
}
}
void dlgType::OnMemberAdd(wxCommandEvent &ev)
{
wxString name = txtMembername->GetValue().Strip(wxString::both);
wxString type = cbDatatype->GetValue();
wxString length = wxEmptyString;
wxString precision = wxEmptyString;
wxString collation = wxEmptyString;
if (txtLength->GetValue() != wxT("") && txtLength->IsEnabled())
length = txtLength->GetValue();
if (txtPrecision->GetValue() != wxT("") && txtPrecision->IsEnabled())
precision = txtPrecision->GetValue();
if (cbCollation->GetValue() != wxT("") && cbCollation->IsEnabled())
collation = cbCollation->GetValue();
if (!length.IsEmpty())
{
type += wxT("(") + length;
if (!precision.IsEmpty())
type += wxT(",") + precision;
type += wxT(")");
}
if (!name.IsEmpty())
{
size_t pos = lstMembers->GetItemCount();
lstMembers->InsertItem(pos, name, 0);
lstMembers->SetItem(pos, 1, type);
lstMembers->SetItem(pos, 2, collation);
memberTypes.Add(GetTypeInfo(cbDatatype->GetGuessedSelection()));
memberLengths.Add(length);
memberPrecisions.Add(precision);
memberCollations.Add(collation);
memberOriginalNames.Add(wxEmptyString);
}
CheckChange();
}
void dlgType::OnMemberChange(wxCommandEvent &ev)
{
wxString name = txtMembername->GetValue().Strip(wxString::both);
wxString type = cbDatatype->GetValue();
wxString length = wxEmptyString;
wxString precision = wxEmptyString;
wxString collation = wxEmptyString;
if (txtLength->GetValue() != wxT("") && txtLength->IsEnabled())
length = txtLength->GetValue();
if (txtPrecision->GetValue() != wxT("") && txtPrecision->IsEnabled())
precision = txtPrecision->GetValue();
if (cbCollation->GetValue() != wxT("") && cbCollation->IsEnabled())
collation = cbCollation->GetValue();
if (!length.IsEmpty())
{
type += wxT("(") + length;
if (!precision.IsEmpty())
type += wxT(",") + precision;
type += wxT(")");
}
if (!name.IsEmpty())
{
long pos = lstMembers->GetFirstSelected();
if (pos >= 0)
{
lstMembers->SetItem(pos, 0, name);
lstMembers->SetItem(pos, 1, type);
lstMembers->SetItem(pos, 2, collation);
memberTypes.Insert(GetTypeInfo(cbDatatype->GetGuessedSelection()), pos);
memberLengths.Insert(length, pos);
memberPrecisions.Insert(precision, pos);
memberCollations.Insert(collation, pos);
memberTypes.RemoveAt(pos + 1);
memberLengths.RemoveAt(pos + 1);
memberPrecisions.RemoveAt(pos + 1);
memberCollations.RemoveAt(pos + 1);
}
}
CheckChange();
}
void dlgType::OnMemberRemove(wxCommandEvent &ev)
{
long pos = lstMembers->GetSelection();
if (pos >= 0)
{
lstMembers->DeleteItem(pos);
memberTypes.RemoveAt(pos);
memberLengths.RemoveAt(pos);
memberPrecisions.RemoveAt(pos);
memberCollations.RemoveAt(pos);
memberOriginalNames.RemoveAt(pos);
}
CheckChange();
}
void dlgType::OnLabelSelChange(wxListEvent &ev)
{
long pos = lstLabels->GetSelection();
if (pos >= 0)
{
txtLabel->SetValue(lstLabels->GetText(pos));
}
}
void dlgType::OnLabelAddBefore(wxCommandEvent &ev)
{
wxString label = txtLabel->GetValue().Strip(wxString::both);
if (!label.IsEmpty())
{
long pos = lstLabels->FindItem(-1, label);
if (pos < 0)
{
if (lstLabels->GetFirstSelected() >= 0)
pos = lstLabels->GetFirstSelected();
else
pos = 0;
lstLabels->InsertItem(pos, label, 0);
}
}
txtLabel->SetValue(wxEmptyString);
CheckChange();
}
void dlgType::OnLabelAddAfter(wxCommandEvent &ev)
{
wxString label = txtLabel->GetValue().Strip(wxString::both);
if (!label.IsEmpty())
{
long pos = lstLabels->FindItem(-1, label);
if (pos < 0)
{
if (lstLabels->GetFirstSelected() >= 0)
pos = lstLabels->GetFirstSelected() + 1;
else
pos = lstLabels->GetItemCount();
lstLabels->InsertItem(pos, label, 0);
}
}
txtLabel->SetValue(wxEmptyString);
CheckChange();
}
void dlgType::OnLabelRemove(wxCommandEvent &ev)
{
long pos = lstLabels->GetSelection();
if (pos >= 0)
lstLabels->DeleteItem(pos);
CheckChange();
}
pgObject *dlgType::CreateObject(pgCollection *collection)
{
pgObject *obj = 0; //pgType::ReadObjects(collection, 0, wxT("\n WHERE usename=") + qtDbString(name));
return obj;
}
wxString dlgType::GetSql()
{
wxString sql, direction, objname;
size_t existingitems_index, listitems_index, offset;
if (type)
{
// Edit Mode
objname = schema->GetQuotedPrefix() + qtIdent(GetName());
AppendNameChange(sql, wxT("TYPE ") + type->GetQuotedFullIdentifier());
AppendOwnerChange(sql, wxT("TYPE ") + objname);
sql += GetSqlForTypes();
if (rdbType->GetSelection() == TYPE_ENUM && connection->BackendMinimumVersion(9, 1))
{
wxArrayString elements = type->GetLabelArray();
existingitems_index = 0;
for (listitems_index = 0 ; listitems_index < (size_t)lstLabels->GetItemCount() ; listitems_index++)
{
if (existingitems_index >= elements.GetCount() || lstLabels->GetItemText(listitems_index) != elements.Item(existingitems_index))
{
queriesToBeSplitted = true;
if (listitems_index == 0)
{
direction = wxT("BEFORE");
offset = 0;
}
else
{
direction = wxT("AFTER");
offset = -1;
}
sql += wxT("ALTER TYPE ") + objname
+ wxT("\n ADD VALUE ") + connection->qtDbString(lstLabels->GetItemText(listitems_index))
+ wxT(" ") + direction + wxT(" ")
+ connection->qtDbString(elements.Item(existingitems_index + offset))
+ wxT(";\n");
}
else
existingitems_index++;
}
}
AppendSchemaChange(sql, wxT("TYPE ") + objname);
}
else
{
// Create Mode
sql = wxT("CREATE TYPE ") + schema->GetQuotedPrefix() + qtIdent(GetName());
if (rdbType->GetSelection() == TYPE_COMPOSITE)
{
sql += wxT(" AS\n (");
int i;
for (i = 0 ; i < lstMembers->GetItemCount() ; i++)
{
if (i)
sql += wxT(",\n ");
sql += qtIdent(lstMembers->GetItemText(i)) + wxT(" ")
+ GetFullTypeName(i);
}
}
else if (rdbType->GetSelection() == TYPE_ENUM)
{
sql += wxT(" AS ENUM\n (");
int i;
for (i = 0 ; i < lstLabels->GetItemCount() ; i++)
{
if (i)
sql += wxT(",\n ");
sql += connection->qtDbString(lstLabels->GetItemText(i));
}
}
else if (rdbType->GetSelection() == TYPE_EXTERNAL)
{
sql += wxT("\n (INPUT=");
AppendQuoted(sql, cbInput->GetValue());
sql += wxT(", OUTPUT=");
AppendQuoted(sql, cbOutput->GetValue());
if (connection->BackendMinimumVersion(7, 4))
{
if (cbReceive->GetCurrentSelection() > 0 || cbSend->GetCurrentSelection() > 0)
{
if (cbReceive->GetCurrentSelection() > 0)
{
sql += wxT(",\n RECEIVE=");
AppendQuoted(sql, cbReceive->GetValue());
if (cbSend->GetCurrentSelection() > 0)
{
sql += wxT(", SEND=");
AppendQuoted(sql, cbSend->GetValue());
}
}
else
{
sql += wxT(",\n SEND=");
AppendQuoted(sql, cbSend->GetValue());
}
}
if (cbAnalyze->GetCurrentSelection() > 0)
{
sql += wxT(",\n ANALYZE=");
AppendQuoted(sql, cbAnalyze->GetValue());
}
}
if (connection->BackendMinimumVersion(8, 3))
{
if (cbTypmodin->GetCurrentSelection() > 0 || cbTypmodout->GetCurrentSelection() > 0)
{
if (cbTypmodin->GetCurrentSelection() > 0)
{
sql += wxT(",\n TYPMOD_IN=");
AppendQuoted(sql, cbTypmodin->GetValue());
if (cbTypmodout->GetCurrentSelection() > 0)
{
sql += wxT(", TYPMOD_OUT=");
AppendQuoted(sql, cbTypmodout->GetValue());
}
}
else
{
sql += wxT(",\n TYPMOD_OUT=");
AppendQuoted(sql, cbTypmodout->GetValue());
}
}
}
if (connection->BackendMinimumVersion(8, 4))
{
sql += wxT(",\n CATEGORY=") + qtDbString(cbCategory->GetValue());
if (chkPrefered->GetValue())
sql += wxT(",\n PREFERRED=true");
}
sql += wxT(",\n INTERNALLENGTH=");
if (chkVariable->GetValue())
sql += wxT("VARIABLE");
else
sql += txtLength->GetValue();
AppendIfFilled(sql, wxT(",\n DEFAULT="), txtDefault->GetValue());
if (!cbElement->GetValue().IsEmpty())
{
sql += wxT(",\n ELEMENT=");
AppendQuoted(sql, cbElement->GetValue());
AppendIfFilled(sql, wxT(", DELIMITER="), qtDbString(txtDelimiter->GetValue().Strip(wxString::both)));
}
if (chkByValue->GetValue())
sql += wxT(",\n PASSEDBYVALUE");
AppendIfFilled(sql, wxT(",\n ALIGNMENT="), cbAlignment->GetValue());
AppendIfFilled(sql, wxT(",\n STORAGE="), cbStorage->GetValue());
if (connection->BackendMinimumVersion(9, 1) && chkCollatable->GetValue())
sql += wxT(",\n COLLATABLE=true");
}
else
{
sql += wxT(" AS RANGE\n (SUBTYPE=");
AppendQuoted(sql, cbSubtype->GetValue());
if (!cbSubtypeOpclass->GetValue().IsEmpty())
{
sql += wxT(", SUBTYPE_OPCLASS=");
AppendQuoted(sql, cbSubtypeOpclass->GetValue());
}
if (!cbRngCollation->GetValue().IsEmpty())
{
sql += wxT(", COLLATION=") + cbRngCollation->GetValue();
}
if (!cbCanonical->GetValue().IsEmpty())
{
sql += wxT(", CANONICAL=");
AppendQuoted(sql, cbCanonical->GetValue());
}
if (!cbSubtypeDiff->GetValue().IsEmpty())
{
sql += wxT(", SUBTYPE_DIFF=");
AppendQuoted(sql, cbSubtypeDiff->GetValue());
}
}
sql += wxT(");\n");
}
AppendComment(sql, wxT("TYPE ") + qtIdent(cbSchema->GetValue()) + wxT(".") + qtIdent(GetName()), type);
if (seclabelPage && connection->BackendMinimumVersion(9, 1))
sql += seclabelPage->GetSqlForSecLabels(wxT("TYPE"), qtIdent(cbSchema->GetValue()) + wxT(".") + qtIdent(GetName()));
// securityPage will exists only for PG 9.2 and later
if (connection->BackendMinimumVersion(9, 2))
sql += securityPage->GetGrant(wxT("U"), wxT("TYPE ") + qtIdent(cbSchema->GetValue()) + wxT(".") + qtIdent(GetName()), &currentAcl);
return sql;
}
wxString dlgType::GetFullTypeName(int type)
{
wxString typname = memberTypes.Item(type).AfterFirst(':');
if (!memberLengths.Item(type).IsEmpty())
{
typname += wxT("(") + memberLengths.Item(type);
if (!memberPrecisions.Item(type).IsEmpty())
typname += wxT(",") + memberPrecisions.Item(type);
typname += wxT(")");
}
if (!memberCollations.Item(type).IsEmpty() && memberCollations.Item(type) != wxT("pg_catalog.\"default\""))
typname += wxT(" COLLATE ") + memberCollations.Item(type);
return typname;
}
wxString dlgType::GetSqlForTypes()
{
wxString sql = wxEmptyString;
wxString objname, old_name, old_type, old_collation, new_name, new_type, new_full_type, new_collation, original_name;
wxArrayString elements = type->GetTypesArray();
int newindex;
unsigned int oldindex = 0;
int hold = 0;
objname = schema->GetQuotedPrefix() + qtIdent(GetName());
for (newindex = 0 ; newindex < lstMembers->GetItemCount() ; newindex = newindex + 1 - hold)
{
// this will decide whether we progress to the next new item,
// or whether we need to continue checking the old list first
hold = 0;
// these are a copy of the list before any changes
if (elements.GetCount() >= (oldindex * 3) + 3)
{
old_name = elements.Item(oldindex * 3);
old_type = elements.Item(oldindex * 3 + 1);
old_collation = elements.Item(oldindex * 3 + 2);
}
else
{
// we've now used up all the old attributes
old_name = wxEmptyString;
old_type = wxEmptyString;
old_collation = wxEmptyString;
}
// this is the original name of the type before editing
original_name = memberOriginalNames.Item(newindex);
new_name = lstMembers->GetItemText(newindex);
new_type = memberTypes.Item(newindex).AfterFirst(':');
new_full_type = GetFullTypeName(newindex);
new_collation = memberCollations.Item(newindex);
if (!original_name.IsEmpty() && original_name == old_name && (new_name != old_name
|| new_type != old_type || new_collation != old_collation))
{
// if this was originally in the list and the name has changed then rename it
if (new_name != old_name)
{
sql += wxT("ALTER TYPE ") + objname + wxT("\n RENAME ATTRIBUTE ")
+ qtIdent(original_name) + wxT(" TO ") + qtIdent(new_name) + wxT(";\n");
}
if (new_type != old_type || new_collation != old_collation)
{
sql += wxT("ALTER TYPE ") + objname + wxT("\n ALTER ATTRIBUTE ")
+ qtIdent(new_name);
// the syntax for alter attribute requires that we always specify the type
sql += wxT(" SET DATA TYPE ") + new_type;
if (new_collation != old_collation)
sql += wxT(" COLLATE ") + new_collation;
sql += wxT(";\n");
}
}
else if (!original_name.IsEmpty() && original_name != old_name)
{
// the old attribute isn't in the new list so drop it
// don't move through new list yet
hold = 1;
sql += wxT("ALTER TYPE ") + objname + wxT("\n DROP ATTRIBUTE ")
+ qtIdent(old_name) + wxT(";\n");
}
else if (original_name.IsEmpty())
{
if (!old_name.IsEmpty())
{
sql += wxT("ALTER TYPE ") + objname + wxT("\n DROP ATTRIBUTE ")
+ qtIdent(old_name) + wxT(";\n");
}
sql += wxT("ALTER TYPE ") + objname + wxT("\n ADD ATTRIBUTE ")
+ qtIdent(new_name) + wxT(" ") + new_full_type + wxT(";\n");
}
else
{
// do nothing
}
oldindex++;
if (newindex + 1 - hold == lstMembers->GetItemCount() && elements.GetCount() >= (oldindex * 3) + 3)
{
// remove remaining old attributes
for (; elements.GetCount() >= (oldindex * 3) + 3; oldindex++)
{
old_name = elements.Item(oldindex * 3);
old_type = elements.Item(oldindex * 3 + 1);
old_collation = elements.Item(oldindex * 3 + 2);
sql += wxT("ALTER TYPE ") + objname + wxT("\n DROP ATTRIBUTE ")
+ qtIdent(old_name) + wxT(";\n");
}
break;
}
}
return sql;
}
void dlgType::OnChange(wxCommandEvent &event)
{
CheckChange();
}
void dlgType::OnAddPriv(wxCommandEvent &ev)
{
securityChanged = true;
CheckChange();
}
void dlgType::OnDelPriv(wxCommandEvent &ev)
{
securityChanged = true;
CheckChange();
}
wxString dlgType::catGetText(wxString c)
{
if (c == wxT("A"))
return wxT("Array types");
if (c == wxT("B"))
return wxT("Boolean types");
if (c == wxT("C"))
return wxT("Composite types");
if (c == wxT("D"))
return wxT("Date/time types");
if (c == wxT("E"))
return wxT("Enum types");
if (c == wxT("G"))
return wxT("Geometric types");
if (c == wxT("I"))
return wxT("Network address types");
if (c == wxT("N"))
return wxT("Numeric types");
if (c == wxT("P"))
return wxT("Pseudo-types");
if (c == wxT("S"))
return wxT("String types");
if (c == wxT("T"))
return wxT("Timespan types");
if (c == wxT("U"))
return wxT("User-defined types");
if (c == wxT("V"))
return wxT("Bit-string types");
if (c == wxT("X"))
return wxT("unknown type");
return wxEmptyString;
}
wxString dlgType::catGetChar(wxString t)
{
if (t == wxT("Array types"))
return wxT("A");
if (t == wxT("Boolean types"))
return wxT("B");
if (t == wxT("Composite types"))
return wxT("C");
if (t == wxT("Date/time types"))
return wxT("D");
if (t == wxT("Enum types"))
return wxT("E");
if (t == wxT("Geometric types"))
return wxT("G");
if (t == wxT("Network address types"))
return wxT("I");
if (t == wxT("Numeric types"))
return wxT("N");
if (t == wxT("Pseudo-types"))
return wxT("P");
if (t == wxT("String types"))
return wxT("S");
if (t == wxT("Timespan types"))
return wxT("T");
if (t == wxT("User-defined types"))
return wxT("U");
if (t == wxT("Bit-string types"))
return wxT("V");
if (t == wxT("unknown type"))
return wxT("X");
return wxEmptyString;
}