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

644 lines
17 KiB
C++

//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// dlgForeignTable.cpp - PostgreSQL Foreign Table Property
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
// App headers
#include "utils/misc.h"
#include "dlg/dlgForeignTable.h"
#include "schema/pgSchema.h"
#include "schema/pgForeignTable.h"
#include "schema/pgDatatype.h"
#include "ctl/ctlSeclabelPanel.h"
// pointer to controls
#define cbForeignServer CTRL_COMBOBOX("cbForeignServer")
#define lstMembers CTRL_LISTVIEW("lstMembers")
#define txtMembername CTRL_TEXT("txtMembername")
#define btnAddMember CTRL_BUTTON("btnAddMember")
#define btnChangeMember CTRL_BUTTON("btnChangeMember")
#define btnRemoveMember CTRL_BUTTON("btnRemoveMember")
#define lstOptions CTRL_LISTVIEW("lstOptions")
#define txtOption CTRL_TEXT("txtOption")
#define txtValue CTRL_TEXT("txtValue")
#define btnAdd CTRL_BUTTON("wxID_ADD")
#define btnRemove CTRL_BUTTON("wxID_REMOVE")
#define chkNotNull CTRL_CHECKBOX("chkNotNull")
BEGIN_EVENT_TABLE(dlgForeignTable, dlgTypeProperty)
EVT_BUTTON(XRCID("btnAddMember"), dlgForeignTable::OnMemberAdd)
EVT_BUTTON(XRCID("btnChangeMember"), dlgForeignTable::OnMemberChange)
EVT_BUTTON(XRCID("btnRemoveMember"), dlgForeignTable::OnMemberRemove)
EVT_LIST_ITEM_SELECTED(XRCID("lstMembers"), dlgForeignTable::OnMemberSelChange)
EVT_TEXT(XRCID("cbDatatype"), dlgForeignTable::OnSelChangeTyp)
EVT_COMBOBOX(XRCID("cbDatatype"), dlgForeignTable::OnSelChangeTyp)
EVT_TEXT(XRCID("txtMembername"), dlgForeignTable::OnChangeMember)
EVT_TEXT(XRCID("cbDatatype"), dlgForeignTable::OnSelChangeTyp)
EVT_COMBOBOX(XRCID("cbDatatype"), dlgForeignTable::OnSelChangeTyp)
EVT_TEXT(XRCID("txtLength"), dlgForeignTable::OnSelChangeTypOrLen)
EVT_TEXT(XRCID("txtPrecision"), dlgForeignTable::OnSelChangeTypOrLen)
EVT_CHECKBOX(XRCID("chkNotNull"), dlgForeignTable::OnChangeMember)
EVT_LIST_ITEM_SELECTED(XRCID("lstOptions"), dlgForeignTable::OnSelChangeOption)
EVT_TEXT(XRCID("txtOption"), dlgForeignTable::OnChangeOptionName)
EVT_BUTTON(wxID_ADD, dlgForeignTable::OnAddOption)
EVT_BUTTON(wxID_REMOVE, dlgForeignTable::OnRemoveOption)
END_EVENT_TABLE();
dlgProperty *pgForeignTableFactory::CreateDialog(frmMain *frame, pgObject *node, pgObject *parent)
{
return new dlgForeignTable(this, frame, (pgForeignTable *)node, (pgSchema *)parent);
}
dlgForeignTable::dlgForeignTable(pgaFactory *f, frmMain *frame, pgForeignTable *node, pgSchema *sch)
: dlgTypeProperty(f, frame, wxT("dlgForeignTable"))
{
foreigntable = node;
schema = sch;
seclabelPage = new ctlSeclabelPanel(nbNotebook);
lstMembers->CreateColumns(0, _("Member"), _("Data type"), _("Constraint"), -1);
queriesToBeSplitted = false;
}
void dlgForeignTable::OnChangeMember(wxCommandEvent &ev)
{
btnAddMember->Enable(
!txtMembername->GetValue().Strip(wxString::both).IsEmpty()
&& cbDatatype->GetGuessedSelection() >= 0);
btnChangeMember->Enable(true);
}
pgObject *dlgForeignTable::GetObject()
{
return foreigntable;
}
int dlgForeignTable::Go(bool modal)
{
seclabelPage->SetConnection(connection);
seclabelPage->SetObject(foreigntable);
this->Connect(EVT_SECLABELPANEL_CHANGE, wxCommandEventHandler(dlgForeignTable::OnChange));
// Fill owner combobox
if (!foreigntable)
cbOwner->Append(wxT(""));
AddGroups();
AddUsers(cbOwner);
// Fill datatype combobox
FillDatatype(cbDatatype);
// Initialize options listview and buttons
lstOptions->AddColumn(_("Option"), 80);
lstOptions->AddColumn(_("Value"), 40);
txtOption->SetValue(wxT(""));
txtValue->SetValue(wxT(""));
btnAdd->Disable();
btnRemove->Disable();
if (foreigntable)
{
// Edit Mode
cbForeignServer->SetValue(foreigntable->GetForeignServer());
cbForeignServer->Disable();
txtMembername->Enable(true);
btnAddMember->Enable(true);
btnChangeMember->Enable(false);
btnRemoveMember->Enable(false);
wxArrayString elements = foreigntable->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(')');
if (!fullType.AfterFirst(')').IsEmpty()) typeName += fullType.AfterFirst(')');
}
int fnd=-1;
for (pos = 0; pos < cbDatatype->GetCount() - 1; pos++)
{
if (cbDatatype->GetString(pos) == typeName)
{
memberTypes.Add(GetTypeInfo(pos));
fnd=pos;
break;
}
}
if (fnd==-1) {
wxLogWarning(_("Type %s not found in cbDatatype(%d)\n"),
typeName.c_str(),
cbDatatype->GetCount()
);
}
memberLengths.Add(typeLength);
memberPrecisions.Add(typePrecision);
memberNotNulls.Add(elements.Item(i + 2));
}
cbDatatype->Enable();
txtLength->Enable();
wxArrayString options = foreigntable->GetOptionsArray();
wxString optionname, optionvalue;
for (unsigned int index = 0; index < options.Count(); index += 2)
{
optionname = options.Item(index);
optionvalue = options.Item(index + 1);
lstOptions->AppendItem(optionname, optionvalue);
}
}
else
{
// Create mode
cbOwner->Append(wxEmptyString);
cbOwner->Disable();
pgSet *set = connection->ExecuteSet(
wxT("SELECT srvname\n")
wxT(" FROM pg_foreign_server\n")
wxT(" ORDER BY srvname"));
if (set)
{
while (!set->Eof())
{
wxString srvname = set->GetVal(wxT("srvname"));
cbForeignServer->Append(srvname);
set->MoveNext();
}
delete set;
}
cbForeignServer->SetSelection(0);
}
txtLength->SetValidator(numericValidator);
return dlgTypeProperty::Go(modal);
}
void dlgForeignTable::OnSelChangeTyp(wxCommandEvent &ev)
{
txtLength->SetValue(wxEmptyString);
txtPrecision->SetValue(wxEmptyString);
cbDatatype->GuessSelection(ev);
chkNotNull->SetValue(false);
OnSelChangeTypOrLen(ev);
}
void dlgForeignTable::OnSelChangeTypOrLen(wxCommandEvent &ev)
{
CheckLenEnable();
txtLength->Enable(isVarLen);
txtPrecision->Enable(isVarPrec);
CheckChange();
OnChangeMember(ev);
}
void dlgForeignTable::CheckChange()
{
bool enable = true;
if (foreigntable)
{
enable = txtComment->GetValue() != foreigntable->GetComment()
|| cbSchema->GetValue() != foreigntable->GetSchema()->GetName()
|| cbOwner->GetValue() != foreigntable->GetOwner()
|| GetSqlForTypes() != wxEmptyString
|| GetSql().Length() > 0;
if (seclabelPage && connection->BackendMinimumVersion(9, 1))
enable = enable || !(seclabelPage->GetSqlForSecLabels().IsEmpty());
}
else
{
wxString name = GetName();
CheckValid(enable, !name.IsEmpty(), _("Please specify name."));
CheckValid(enable, cbForeignServer->GetCurrentSelection() >= 0, _("Please specify a foreign server."));
}
EnableOK(enable);
}
void dlgForeignTable::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(!txtLength->GetValue().IsEmpty());
txtPrecision->SetValue(memberPrecisions.Item(pos));
txtPrecision->Enable(!txtPrecision->GetValue().IsEmpty());
chkNotNull->SetValue(memberNotNulls.Item(pos) == wxT("NOT NULL"));
chkNotNull->Enable();
btnChangeMember->Enable();
btnRemoveMember->Enable();
}
}
void dlgForeignTable::OnMemberAdd(wxCommandEvent &ev)
{
wxString name = txtMembername->GetValue().Strip(wxString::both);
wxString type = cbDatatype->GetValue();
wxString length = wxEmptyString;
wxString precision = wxEmptyString;
wxString notnull = wxEmptyString;
if (txtLength->GetValue() != wxT("") && txtLength->IsEnabled())
length = txtLength->GetValue();
if (txtPrecision->GetValue() != wxT("") && txtPrecision->IsEnabled())
precision = txtPrecision->GetValue();
notnull = chkNotNull->GetValue() ? wxT("NOT NULL") : wxEmptyString;
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, notnull);
memberTypes.Add(GetTypeInfo(cbDatatype->GetGuessedSelection()));
memberLengths.Add(length);
memberPrecisions.Add(precision);
memberNotNulls.Add(notnull);
}
txtMembername->SetValue(wxEmptyString);
cbDatatype->SetValue(wxEmptyString);
txtLength->SetValue(wxEmptyString);
txtPrecision->SetValue(wxEmptyString);
chkNotNull->SetValue(false);
CheckChange();
}
void dlgForeignTable::OnMemberChange(wxCommandEvent &ev)
{
wxString name = txtMembername->GetValue().Strip(wxString::both);
wxString type = cbDatatype->GetValue();
wxString length = wxEmptyString;
wxString precision = wxEmptyString;
wxString notnull = wxEmptyString;
if (txtLength->GetValue() != wxT("") && txtLength->IsEnabled())
length = txtLength->GetValue();
if (txtPrecision->GetValue() != wxT("") && txtPrecision->IsEnabled())
precision = txtPrecision->GetValue();
notnull = chkNotNull->GetValue() ? wxT("NOT NULL") : wxEmptyString;
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, notnull);
memberTypes.Insert(GetTypeInfo(cbDatatype->GetGuessedSelection()), pos);
memberLengths.Insert(length, pos);
memberPrecisions.Insert(precision, pos);
memberNotNulls.Insert(notnull, pos);
memberTypes.RemoveAt(pos + 1);
memberLengths.RemoveAt(pos + 1);
memberPrecisions.RemoveAt(pos + 1);
memberNotNulls.RemoveAt(pos + 1);
}
}
CheckChange();
}
void dlgForeignTable::OnMemberRemove(wxCommandEvent &ev)
{
long pos = lstMembers->GetSelection();
if (pos >= 0)
{
lstMembers->DeleteItem(pos);
memberTypes.RemoveAt(pos);
memberLengths.RemoveAt(pos);
memberPrecisions.RemoveAt(pos);
memberNotNulls.RemoveAt(pos);
}
CheckChange();
}
pgObject *dlgForeignTable::CreateObject(pgCollection *collection)
{
pgObject *obj = 0;
return obj;
}
void dlgForeignTable::OnChangeOptionName(wxCommandEvent &ev)
{
btnAdd->Enable(txtOption->GetValue().Length() > 0);
}
void dlgForeignTable::OnSelChangeOption(wxListEvent &ev)
{
int row = lstOptions->GetSelection();
if (row >= 0)
{
txtOption->SetValue(lstOptions->GetText(row, 0));
txtValue->SetValue(lstOptions->GetText(row, 1));
}
btnRemove->Enable(row >= 0);
}
void dlgForeignTable::OnAddOption(wxCommandEvent &ev)
{
bool found = false;
for (int pos = 0 ; pos < lstOptions->GetItemCount() ; pos++)
{
if (lstOptions->GetText(pos).IsSameAs(txtOption->GetValue(), false))
{
lstOptions->SetItem(pos, 1, txtValue->GetValue());
found = true;
break;
}
}
if (!found)
{
lstOptions->AppendItem(txtOption->GetValue(), txtValue->GetValue());
}
txtOption->SetValue(wxT(""));
txtValue->SetValue(wxT(""));
btnAdd->Disable();
CheckChange();
}
void dlgForeignTable::OnRemoveOption(wxCommandEvent &ev)
{
int sel = lstOptions->GetSelection();
lstOptions->DeleteItem(sel);
txtOption->SetValue(wxT(""));
txtValue->SetValue(wxT(""));
btnRemove->Disable();
CheckChange();
}
wxString dlgForeignTable::GetOptionsSql()
{
wxArrayString options = foreigntable->GetOptionsArray();
wxString optionname, optionvalue, sqloptions;
bool found;
int pos;
for (unsigned int index = 0; index < options.Count(); index += 2)
{
optionname = options.Item(index);
optionvalue = options.Item(index + 1);
// check for options
found = false;
for (pos = 0 ; pos < lstOptions->GetItemCount() && !found; pos++)
{
found = lstOptions->GetText(pos, 0).Cmp(optionname) == 0;
if (found) break;
}
if (found)
{
if (lstOptions->GetText(pos, 1).Cmp(optionvalue) != 0)
{
if (sqloptions.Length() > 0)
sqloptions += wxT(", ");
sqloptions += wxT("SET ") + optionname + wxT(" '") + lstOptions->GetText(pos, 1) + wxT("'");
}
}
else
{
if (sqloptions.Length() > 0)
sqloptions += wxT(", ");
sqloptions += wxT("DROP ") + optionname;
}
}
for (pos = 0 ; pos < lstOptions->GetItemCount() ; pos++)
{
options = foreigntable->GetOptionsArray();
found = false;
for (unsigned int index = 0; index < options.Count() && !found; index += 2)
{
optionname = options.Item(index);
optionvalue = options.Item(index + 1);
found = lstOptions->GetText(pos, 0).Cmp(optionname) == 0;
}
if (!found)
{
if (sqloptions.Length() > 0)
sqloptions += wxT(", ");
sqloptions += wxT("ADD ") + lstOptions->GetText(pos, 0) + wxT(" '") + lstOptions->GetText(pos, 1) + wxT("'");
}
}
return sqloptions;
}
wxString dlgForeignTable::GetSql()
{
wxString sql;
wxString name;
if (foreigntable)
{
// Edit Mode
name = qtIdent(foreigntable->GetSchema()->GetName()) + wxT(".") + qtIdent(GetName());
AppendNameChange(sql, wxT("FOREIGN TABLE ") + foreigntable->GetQuotedFullIdentifier());
AppendOwnerChange(sql, wxT("FOREIGN TABLE ") + name);
sql += GetSqlForTypes();
wxString sqloptions = GetOptionsSql();
if (sqloptions.Length() > 0)
{
sql += wxT("ALTER FOREIGN TABLE ") + name
+ wxT("\n OPTIONS (") + sqloptions + wxT(");\n");
}
AppendSchemaChange(sql, wxT("FOREIGN TABLE ") + name);
}
else
{
name = qtIdent(cbSchema->GetValue()) + wxT(".") + qtIdent(GetName());
// Create Mode
sql = wxT("CREATE FOREIGN TABLE " + name);
sql += wxT(" (");
int i;
for (i = 0 ; i < lstMembers->GetItemCount() ; i++)
{
if (i)
sql += wxT(",\n ");
sql += qtIdent(lstMembers->GetItemText(i)) + wxT(" ")
+ GetFullTypeName(i);
}
sql += wxT(") SERVER ") + cbForeignServer->GetValue();
// check for options
if (lstOptions->GetItemCount() > 0)
{
wxString options = wxEmptyString;
for (int pos = 0 ; pos < lstOptions->GetItemCount() ; pos++)
{
if (options.Length() > 0)
options += wxT(", ");
options += lstOptions->GetText(pos, 0)
+ wxT(" '") + lstOptions->GetText(pos, 1) + wxT("' ");
}
sql += wxT("\n OPTIONS (") + options + wxT(")");
}
sql += wxT(";\n");
}
AppendComment(sql, wxT("FOREIGN TABLE ") + qtIdent(cbSchema->GetValue()) + wxT(".") + qtIdent(GetName()), foreigntable);
if (seclabelPage && connection->BackendMinimumVersion(9, 1))
sql += seclabelPage->GetSqlForSecLabels(wxT("FOREIGN TABLE "), qtIdent(cbSchema->GetValue()) + wxT(".") + qtIdent(GetName()));
return sql;
}
wxString dlgForeignTable::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(")");
}
typname += wxT(" ") + memberNotNulls.Item(type);
return typname;
}
wxString dlgForeignTable::GetSqlForTypes()
{
wxString sql = wxEmptyString;
wxString old_name, old_type, new_name, new_type;
wxArrayString elements = foreigntable->GetTypesArray();
bool modified = lstMembers->GetItemCount() * 3 != (int)elements.GetCount();
size_t i;
// Check if there is a change
for (int i = 0 ; i < lstMembers->GetItemCount() && !modified; i++)
{
old_name = elements.Item(i * 3);
old_type = elements.Item(i * 3 + 1) + wxT(" ") + elements.Item(i * 3 + 2);
new_name = lstMembers->GetItemText(i);
new_type = GetFullTypeName(i);
modified = modified || old_name != new_name || old_type != new_type;
}
if (modified)
{
// Drop all old attributes
for (i = 0 ; i < elements.GetCount() ; i += 3)
{
old_name = elements.Item(i);
sql += wxT("ALTER FOREIGN TABLE ") + foreigntable->GetQuotedFullIdentifier()
+ wxT("\n DROP COLUMN ") + old_name + wxT(";\n");
}
// Add all new attributes
for (int i = 0 ; i < lstMembers->GetItemCount() ; i++)
{
new_name = lstMembers->GetItemText(i);
new_type = GetFullTypeName(i);
sql += wxT("ALTER FOREIGN TABLE ") + foreigntable->GetQuotedFullIdentifier()
+ wxT("\n ADD COLUMN ") + new_name + wxT(" ") + new_type + wxT(";\n");
}
}
return sql;
}
void dlgForeignTable::OnChange(wxCommandEvent &event)
{
CheckChange();
}