support PG11

Поддержка PostgreSQL 11 только для Windows
This commit is contained in:
levinsv 2018-10-10 22:59:25 +05:00
commit 4af765213c
1765 changed files with 407959 additions and 0 deletions

23
.gitignore vendored Normal file
View file

@ -0,0 +1,23 @@
*.sbr
*.obj
*.cod
*.dll
*.tlog
*.7z
*.bsc
Release/pgAdmin3.Build.CppClean.log
Release/pgAdmin3.ilk
Release/pgAdmin3.log
Release/pgAdmin3.map
Release/pgAdmin3.pch
Release/pgAdmin3.pdb
Release/pgAdmin3.res
Release/vc110.pdb
Release/Win32/libssh2/vc110.pdb
Release/x64/libssh2/vc110.pdb
Release/pgAdmin3.lastbuildstate
Release/hist.log
Release/1.sql
Release/1.xml
Release/33.xml

Binary file not shown.

View file

@ -0,0 +1,56 @@
D:\PostgreSQL\pgadmin3\pgadmin\include\images\aggregate-sm.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\aggregate.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\aggregates.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\back.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\backup.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\baddatabase.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\cast-sm.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\cast.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\casts.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\catalog-sm.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\catalog.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\catalogobject-sm.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\catalogobject.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\catalogobjects.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\catalogs.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\check.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\checkbad.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\checked.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\clearAll.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\clip_copy.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\clip_cut.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\clip_paste.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\closeddatabase-sm.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\closeddatabase.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\collation-sm.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\collation.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\collations.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\column-sm.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\column.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\columns.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\configuration.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\configurations.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\connect.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\constraints.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\continue.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\conversion.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\conversions.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\create.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\database-sm.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\database.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\databases.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\ddAddColumn.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\ddAddColumnCursor.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\ddAddForeignKey.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\ddcancel.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\ddDeleteTableCursor.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\ddDown.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\ddforeignkey.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\ddforeignkeyfromuk.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\ddforeignkeyuniquekey.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\ddforeignkeyuniquekeyfromuk.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\ddgendiagram.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\ddMaximizeTable.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\ddMinimizeTable.pngc
D:\PostgreSQL\pgadmin3\pgadmin\include\images\ddMinMaxCursor.pngc
D:\PostgreSQL\pgadmin3\pgadmin\Debug\pgAdmin3.write.1.tlog

View file

@ -0,0 +1,2 @@
#v4.0:v100:false
Debug|Win32|D:\PostgreSQL\pgadmin3\|

5
Debug/pgAdmin3.log Normal file
View file

@ -0,0 +1,5 @@
 "git" не является внутренней или внешней
командой, исполняемой программой или пакетным файлом.
Not generating svnversion.h, not changed.
precomp.cpp
d:\wxwidgets-2.8.12\include\wx\setup.h(20): fatal error C1083: Cannot open include file: '../../../lib/vc_dll/mswud/wx/setup.h': No such file or directory

View file

BIN
Debug/vc100.idb Normal file

Binary file not shown.

BIN
Debug/vc100.pdb Normal file

Binary file not shown.

BIN
Release/pgAdmin3.exe Normal file

Binary file not shown.

513
agent/dlgJob.cpp Normal file
View file

@ -0,0 +1,513 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// dlgJob.cpp - PostgreSQL Job Property
//
//////////////////////////////////////////////////////////////////////////
// App headers
#include "pgAdmin3.h"
#include "utils/misc.h"
#include "agent/dlgJob.h"
#include "agent/dlgStep.h"
#include "agent/dlgSchedule.h"
#include "agent/pgaJob.h"
#include "agent/pgaStep.h"
#include "agent/pgaSchedule.h"
#include "frm/frmMain.h"
// pointer to controls
#define txtID CTRL_TEXT("txtID")
#define chkEnabled CTRL_CHECKBOX("chkEnabled")
#define cbJobclass CTRL_COMBOBOX("cbJobclass")
#define txtHostAgent CTRL_TEXT("txtHostAgent")
#define txtCreated CTRL_TEXT("txtCreated")
#define txtChanged CTRL_TEXT("txtChanged")
#define txtNextrun CTRL_TEXT("txtNextrun")
#define txtLastrun CTRL_TEXT("txtLastrun")
#define txtLastresult CTRL_TEXT("txtLastResult")
#define lstSteps CTRL_LISTVIEW("lstSteps")
#define btnChangeStep CTRL_BUTTON("btnChangeStep")
#define btnAddStep CTRL_BUTTON("btnAddStep")
#define btnRemoveStep CTRL_BUTTON("btnRemoveStep")
#define lstSchedules CTRL_LISTVIEW("lstSchedules")
#define btnChangeSchedule CTRL_BUTTON("btnChangeSchedule")
#define btnAddSchedule CTRL_BUTTON("btnAddSchedule")
#define btnRemoveSchedule CTRL_BUTTON("btnRemoveSchedule")
BEGIN_EVENT_TABLE(dlgJob, dlgAgentProperty)
EVT_CHECKBOX(XRCID("chkEnabled"), dlgProperty::OnChange)
EVT_COMBOBOX(XRCID("cbJobclass"), dlgProperty::OnChange)
EVT_TEXT(XRCID("txtHostAgent"), dlgProperty::OnChange)
EVT_LIST_ITEM_SELECTED(XRCID("lstSteps"), dlgJob::OnSelChangeStep)
EVT_BUTTON(XRCID("btnChangeStep"), dlgJob::OnChangeStep)
EVT_BUTTON(XRCID("btnAddStep"), dlgJob::OnAddStep)
EVT_BUTTON(XRCID("btnRemoveStep"), dlgJob::OnRemoveStep)
EVT_LIST_ITEM_SELECTED(XRCID("lstSchedules"), dlgJob::OnSelChangeSchedule)
EVT_BUTTON(XRCID("btnChangeSchedule"), dlgJob::OnChangeSchedule)
EVT_BUTTON(XRCID("btnAddSchedule"), dlgJob::OnAddSchedule)
EVT_BUTTON(XRCID("btnRemoveSchedule"), dlgJob::OnRemoveSchedule)
#ifdef __WXMAC__
EVT_SIZE( dlgJob::OnChangeSize)
#endif
END_EVENT_TABLE();
dlgProperty *pgaJobFactory::CreateDialog(frmMain *frame, pgObject *node, pgObject *parent)
{
return new dlgJob(this, frame, (pgaJob *)node);
}
dlgJob::dlgJob(pgaFactory *f, frmMain *frame, pgaJob *node)
: dlgAgentProperty(f, frame, wxT("dlgJob"))
{
job = node;
txtID->Disable();
txtCreated->Disable();
txtChanged->Disable();
txtNextrun->Disable();
txtLastrun->Disable();
txtLastresult->Disable();
lstSteps->CreateColumns(0, _("Step"), _("Comment"), 90);
lstSteps->AddColumn(wxT("cmd"), 0);
lstSteps->AddColumn(wxT("id"), 0);
lstSchedules->CreateColumns(0, _("Schedule"), _("Comment"), 90);
lstSchedules->AddColumn(wxT("cmd"), 0);
lstSchedules->AddColumn(wxT("id"), 0);
btnChangeStep->Disable();
btnRemoveStep->Disable();
btnChangeSchedule->Disable();
btnRemoveSchedule->Disable();
}
pgObject *dlgJob::GetObject()
{
return job;
}
#ifdef __WXMAC__
void dlgJob::OnChangeSize(wxSizeEvent &ev)
{
lstSteps->SetSize(wxDefaultCoord, wxDefaultCoord,
ev.GetSize().GetWidth(), ev.GetSize().GetHeight() - 350);
lstSchedules->SetSize(wxDefaultCoord, wxDefaultCoord,
ev.GetSize().GetWidth(), ev.GetSize().GetHeight() - 350);
if (GetAutoLayout())
{
Layout();
}
}
#endif
int dlgJob::Go(bool modal)
{
int returncode;
pgSet *jcl = connection->ExecuteSet(wxT("SELECT jclname FROM pgagent.pga_jobclass"));
if (jcl)
{
while (!jcl->Eof())
{
cbJobclass->Append(jcl->GetVal(0));
jcl->MoveNext();
}
delete jcl;
}
if (job)
{
// edit mode
recId = job->GetRecId();
txtID->SetValue(NumToStr(recId));
cbJobclass->SetValue(job->GetJobclass());
chkEnabled->SetValue(job->GetEnabled());
txtHostAgent->SetValue(job->GetHostAgent());
txtCreated->SetValue(DateToStr(job->GetCreated()));
txtChanged->SetValue(DateToStr(job->GetChanged()));
txtNextrun->SetValue(DateToStr(job->GetNextrun()));
txtLastrun->SetValue(DateToStr(job->GetLastrun()));
txtLastresult->SetValue(job->GetLastresult());
wxCookieType cookie;
pgObject *data = 0;
wxTreeItemId item, stepsItem, schedulesItem;
item = mainForm->GetBrowser()->GetFirstChild(job->GetId(), cookie);
while (item)
{
data = mainForm->GetBrowser()->GetObject(item);
if (data->GetMetaType() == PGM_STEP)
stepsItem = item;
else if (data->GetMetaType() == PGM_SCHEDULE)
schedulesItem = item;
item = mainForm->GetBrowser()->GetNextChild(job->GetId(), cookie);
}
if (stepsItem)
{
pgCollection *coll = (pgCollection *)data;
// make sure all columns are appended
coll->ShowTreeDetail(mainForm->GetBrowser());
// this is the columns collection
item = mainForm->GetBrowser()->GetFirstChild(stepsItem, cookie);
// add columns
while (item)
{
data = mainForm->GetBrowser()->GetObject(item);
if (data->IsCreatedBy(stepFactory))
{
pgaStep *step = (pgaStep *)data;
int pos = lstSteps->AppendItem(stepFactory.GetIconId(), step->GetName(), step->GetComment());
lstSteps->SetItem(pos, 3, NumToStr((long)step));
previousSteps.Add(NumToStr((long)step));
}
item = mainForm->GetBrowser()->GetNextChild(stepsItem, cookie);
}
}
if (schedulesItem)
{
pgCollection *coll = (pgCollection *)data;
// make sure all columns are appended
coll->ShowTreeDetail(mainForm->GetBrowser());
// this is the columns collection
item = mainForm->GetBrowser()->GetFirstChild(schedulesItem, cookie);
// add columns
while (item)
{
data = mainForm->GetBrowser()->GetObject(item);
if (data->IsCreatedBy(scheduleFactory))
{
pgaSchedule *schedule = (pgaSchedule *)data;
int pos = lstSchedules->AppendItem(scheduleFactory.GetIconId(), schedule->GetName(), schedule->GetComment());
lstSchedules->SetItem(pos, 3, NumToStr((long)schedule));
previousSchedules.Add(NumToStr((long)schedule));
}
item = mainForm->GetBrowser()->GetNextChild(schedulesItem, cookie);
}
}
}
else
{
// create mode
cbJobclass->SetSelection(0);
btnChangeStep->Hide();
btnChangeSchedule->Hide();
}
returncode = dlgProperty::Go(modal);
SetSqlReadOnly(true);
// This fixes a UI glitch on MacOS X
// Because of the new layout code, the Columns pane doesn't size itself properly
SetSize(GetSize().GetWidth() + 1, GetSize().GetHeight());
SetSize(GetSize().GetWidth() - 1, GetSize().GetHeight());
return returncode;
}
pgObject *dlgJob::CreateObject(pgCollection *collection)
{
pgObject *obj = jobFactory.CreateObjects(collection, 0, wxT(" WHERE jobid=") + NumToStr(recId) + wxT("\n"));
return obj;
}
void dlgJob::CheckChange()
{
bool enable = true;
wxString name = GetName();
if (job)
{
enable = txtComment->GetValue() != job->GetComment()
|| name != job->GetName()
|| chkEnabled->GetValue() != job->GetEnabled()
|| txtHostAgent->GetValue() != job->GetHostAgent();
if (!enable)
{
enable = !GetUpdateSql().IsEmpty();
}
}
if (statusBar)
statusBar->SetStatusText(wxEmptyString);
CheckValid(enable, !txtName->GetValue().IsEmpty(), _("Please specify name."));
EnableOK(enable);
}
void dlgJob::OnChangeStep(wxCommandEvent &ev)
{
long pos = lstSteps->GetSelection();
pgaStep *obj = (pgaStep *) StrToLong(lstSteps->GetText(pos, 3));
dlgStep step(&stepFactory, mainForm, obj, job);
step.CenterOnParent();
step.SetConnection(connection);
if (step.Go(true) != wxID_CANCEL)
{
lstSteps->SetItem(pos, 0, step.GetName());
lstSteps->SetItem(pos, 1, step.GetComment());
if (lstSteps->GetText(pos, 3).IsEmpty())
{
wxString *stepSql = new wxString(step.GetInsertSql());
lstSteps->SetItemData(pos, (long)stepSql);
}
else
{
wxString *stepSql = new wxString(step.GetUpdateSql());
lstSteps->SetItemData(pos, (long)stepSql);
}
CheckChange();
}
}
void dlgJob::OnSelChangeStep(wxListEvent &ev)
{
btnChangeStep->Enable();
btnRemoveStep->Enable();
}
void dlgJob::OnAddStep(wxCommandEvent &ev)
{
dlgStep step(&stepFactory, mainForm, NULL, job);
step.CenterOnParent();
step.SetConnection(connection);
if (step.Go(true) != wxID_CANCEL)
{
int pos = lstSteps->AppendItem(stepFactory.GetIconId(), step.GetName(), step.GetComment());
wxString *stepSql = new wxString(step.GetInsertSql());
lstSteps->SetItemData(pos, (long)stepSql);
CheckChange();
}
}
void dlgJob::OnRemoveStep(wxCommandEvent &ev)
{
delete (wxString *)lstSteps->GetItemData(lstSteps->GetSelection());
lstSteps->DeleteCurrentItem();
btnChangeStep->Disable();
btnRemoveStep->Disable();
CheckChange();
}
void dlgJob::OnSelChangeSchedule(wxListEvent &ev)
{
btnChangeSchedule->Enable();
btnRemoveSchedule->Enable();
}
void dlgJob::OnChangeSchedule(wxCommandEvent &ev)
{
long pos = lstSchedules->GetSelection();
pgaSchedule *obj = (pgaSchedule *) StrToLong(lstSchedules->GetText(pos, 3));
dlgSchedule schedule(&scheduleFactory, mainForm, obj, job);
schedule.CenterOnParent();
schedule.SetConnection(connection);
if (schedule.Go(true) != wxID_CANCEL)
{
lstSchedules->SetItem(pos, 0, schedule.GetName());
lstSchedules->SetItem(pos, 1, schedule.GetComment());
if (lstSchedules->GetText(pos, 3).IsEmpty())
{
wxString *scheduleSql = new wxString(schedule.GetInsertSql());
lstSchedules->SetItemData(pos, (long)scheduleSql);
}
else
{
wxString *scheduleSql = new wxString(schedule.GetUpdateSql());
lstSchedules->SetItemData(pos, (long)scheduleSql);
}
CheckChange();
}
}
void dlgJob::OnAddSchedule(wxCommandEvent &ev)
{
dlgSchedule schedule(&scheduleFactory, mainForm, NULL, job);
schedule.CenterOnParent();
schedule.SetConnection(connection);
if (schedule.Go(true) != wxID_CANCEL)
{
int pos = lstSchedules->AppendItem(scheduleFactory.GetIconId(), schedule.GetName(), schedule.GetComment());
wxString *scheduleSql = new wxString(schedule.GetInsertSql());
lstSchedules->SetItemData(pos, (long)scheduleSql);
CheckChange();
}
}
void dlgJob::OnRemoveSchedule(wxCommandEvent &ev)
{
delete (wxString *)lstSchedules->GetItemData(lstSchedules->GetSelection());
lstSchedules->DeleteCurrentItem();
btnChangeSchedule->Disable();
btnRemoveSchedule->Disable();
CheckChange();
}
wxString dlgJob::GetInsertSql()
{
wxString sql;
if (!job)
{
sql = wxT("INSERT INTO pgagent.pga_job (jobid, jobjclid, jobname, jobdesc, jobenabled, jobhostagent)\n")
wxT("SELECT <JobId>, jcl.jclid, ") + qtDbString(GetName()) +
wxT(", ") + qtDbString(txtComment->GetValue()) + wxT(", ") + BoolToStr(chkEnabled->GetValue()) +
wxT(", ") + qtDbString(txtHostAgent->GetValue()) + wxT("\n")
wxT(" FROM pgagent.pga_jobclass jcl WHERE jclname=") + qtDbString(cbJobclass->GetValue()) + wxT(";\n");
}
return sql;
}
wxString dlgJob::GetUpdateSql()
{
wxString sql, name;
name = GetName();
if (job)
{
// edit mode
wxString vars;
if (name != job->GetName())
{
if (!vars.IsEmpty())
vars.Append(wxT(", "));
vars.Append(wxT("jobname = ") + qtDbString(name));
}
if (cbJobclass->GetValue().Trim() != job->GetJobclass())
{
if (!vars.IsEmpty())
vars.Append(wxT(", "));
vars.Append(wxT("jobjclid= (SELECT jclid FROM pgagent.pga_jobclass WHERE jclname=") + qtDbString(cbJobclass->GetValue()) + wxT(")"));
}
if (chkEnabled->GetValue() != job->GetEnabled())
{
if (!vars.IsEmpty())
vars.Append(wxT(", "));
vars.Append(wxT("jobenabled = ") + BoolToStr(chkEnabled->GetValue()));
}
if (txtHostAgent->GetValue() != job->GetHostAgent())
{
if (!vars.IsEmpty())
vars.Append(wxT(", "));
vars.Append(wxT("jobhostagent = ") + qtDbString(txtHostAgent->GetValue()));
}
if (txtComment->GetValue() != job->GetComment())
{
if (!vars.IsEmpty())
vars.Append(wxT(", "));
vars.Append(wxT("jobdesc = ") + qtDbString(txtComment->GetValue()));
}
if (!vars.IsEmpty())
sql = wxT("UPDATE pgagent.pga_job SET ") + vars + wxT("\n")
wxT(" WHERE jobid=") + NumToStr(recId) + wxT(";\n");
}
else
{
// create mode
// done by GetInsertSql
}
int pos, index;
wxArrayString tmpSteps = previousSteps;
for (pos = 0 ; pos < lstSteps->GetItemCount() ; pos++)
{
wxString str = lstSteps->GetText(pos, 3);
if (!str.IsEmpty())
{
index = tmpSteps.Index(str);
if (index >= 0)
tmpSteps.RemoveAt(index);
}
if(lstSteps->GetItemData(pos))
{
str = *(wxString *)lstSteps->GetItemData(pos);
if (!str.IsEmpty())
sql += str;
}
}
for (index = 0 ; index < (int)tmpSteps.GetCount() ; index++)
{
sql += wxT("DELETE FROM pgagent.pga_jobstep WHERE jstid=")
+ NumToStr(((pgaStep *)StrToLong(tmpSteps.Item(index)))->GetRecId()) + wxT(";\n");
}
wxArrayString tmpSchedules = previousSchedules;
for (pos = 0 ; pos < lstSchedules->GetItemCount() ; pos++)
{
wxString str = lstSchedules->GetText(pos, 3);
if (!str.IsEmpty())
{
index = tmpSchedules.Index(str);
if (index >= 0)
tmpSchedules.RemoveAt(index);
}
if(lstSchedules->GetItemData(pos))
{
str = *(wxString *)lstSchedules->GetItemData(pos);
if (!str.IsEmpty())
sql += str;
}
}
for (index = 0 ; index < (int)tmpSchedules.GetCount() ; index++)
{
sql += wxT("DELETE FROM pgagent.pga_schedule WHERE jscid=")
+ NumToStr(((pgaStep *)StrToLong(tmpSchedules.Item(index)))->GetRecId()) + wxT(";\n");
}
return sql;
}

859
agent/dlgSchedule.cpp Normal file
View file

@ -0,0 +1,859 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// dlgSchedule.cpp - PostgreSQL Schedule Property
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// App headers
#include "utils/misc.h"
#include "agent/dlgSchedule.h"
#include "agent/pgaSchedule.h"
// image for de/-select all
#include "images/check.pngc"
#include "images/uncheck.pngc"
// pointer to controls
#define txtID CTRL_TEXT("txtID")
#define chkEnabled CTRL_CHECKBOX("chkEnabled")
#define calStart CTRL_CALENDAR("calStart")
#define timStart CTRL_TIME("timStart")
#define calEnd CTRL_CALENDAR("calEnd")
#define timEnd CTRL_TIME("timEnd")
#define chkWeekdays CTRL_CHECKLISTBOX("chkWeekdays")
#define chkMonthdays CTRL_CHECKLISTBOX("chkMonthdays")
#define chkMonths CTRL_CHECKLISTBOX("chkMonths")
#define chkHours CTRL_CHECKLISTBOX("chkHours")
#define chkMinutes CTRL_CHECKLISTBOX("chkMinutes")
#define lstExceptions CTRL_LISTVIEW("lstExceptions")
#define timException CTRL_TIME("timException")
#define calException CTRL_CALENDAR("calException")
#define btnAddException CTRL_BUTTON("btnAddException")
#define btnChangeException CTRL_BUTTON("btnChangeException")
#define btnRemoveException CTRL_BUTTON("btnRemoveException")
#define btnWeekdays CTRL_BUTTON("btnWeekdays")
#define btnMonthdays CTRL_BUTTON("btnMonthdays")
#define btnMonths CTRL_BUTTON("btnMonths")
#define btnHours CTRL_BUTTON("btnHours")
#define btnMinutes CTRL_BUTTON("btnMinutes")
BEGIN_EVENT_TABLE(dlgSchedule, dlgAgentProperty)
EVT_CHECKBOX(XRCID("chkEnabled"), dlgSchedule::OnChangeCom)
EVT_CALENDAR_SEL_CHANGED(XRCID("calStart"), dlgSchedule::OnChangeCal)
EVT_TEXT(XRCID("timStart"), dlgSchedule::OnChangeCom)
EVT_CALENDAR_SEL_CHANGED(XRCID("calEnd"), dlgSchedule::OnChangeCal)
EVT_TEXT(XRCID("timEnd"), dlgSchedule::OnChangeCom)
EVT_LIST_ITEM_SELECTED(XRCID("lstExceptions"), dlgSchedule::OnSelChangeException)
EVT_BUTTON(XRCID("btnAddException"), dlgSchedule::OnAddException)
EVT_BUTTON(XRCID("btnChangeException"), dlgSchedule::OnChangeException)
EVT_BUTTON(XRCID("btnRemoveException"), dlgSchedule::OnRemoveException)
EVT_BUTTON(XRCID("btnWeekdays"), dlgSchedule::OnSelectAllWeekdays)
EVT_BUTTON(XRCID("btnMonthdays"), dlgSchedule::OnSelectAllMonthdays)
EVT_BUTTON(XRCID("btnMonths"), dlgSchedule::OnSelectAllMonths)
EVT_BUTTON(XRCID("btnHours"), dlgSchedule::OnSelectAllHours)
EVT_BUTTON(XRCID("btnMinutes"), dlgSchedule::OnSelectAllMinutes)
EVT_CHECKLISTBOX(XRCID("chkWeekdays"), dlgSchedule::OnChangeCom)
EVT_CHECKLISTBOX(XRCID("chkMonthdays"), dlgSchedule::OnChangeCom)
EVT_CHECKLISTBOX(XRCID("chkMonths"), dlgSchedule::OnChangeCom)
EVT_CHECKLISTBOX(XRCID("chkHours"), dlgSchedule::OnChangeCom)
EVT_CHECKLISTBOX(XRCID("chkMinutes"), dlgSchedule::OnChangeCom)
#ifdef __WXMAC__
EVT_SIZE( dlgSchedule::OnChangeSize)
#endif
END_EVENT_TABLE();
dlgProperty *pgaScheduleFactory::CreateDialog(frmMain *frame, pgObject *node, pgObject *parent)
{
return new dlgSchedule(this, frame, (pgaSchedule *)node, (pgaJob *)parent);
}
dlgSchedule::dlgSchedule(pgaFactory *f, frmMain *frame, pgaSchedule *node, pgaJob *j)
: dlgAgentProperty(f, frame, wxT("dlgSchedule"))
{
schedule = node;
job = j;
if (job)
jobId = job->GetRecId();
else
jobId = 0;
lstExceptions->CreateColumns(0, _("Date"), _("Time"), 90);
lstExceptions->AddColumn(wxT("dirty"), 0);
lstExceptions->AddColumn(wxT("id"), 0);
txtID->Disable();
btnChangeException->Disable();
btnRemoveException->Disable();
int i;
for (i = 0 ; i < 24 ; i++)
chkHours->Append(wxString::Format(wxT("%02d"), i));
for (i = 0 ; i < 60 ; i++)
chkMinutes->Append(wxString::Format(wxT("%02d"), i));
}
pgObject *dlgSchedule::GetObject()
{
return schedule;
}
#ifdef __WXMAC__
void dlgSchedule::OnChangeSize(wxSizeEvent &ev)
{
lstExceptions->SetSize(wxDefaultCoord, wxDefaultCoord,
ev.GetSize().GetWidth(), ev.GetSize().GetHeight() - 350);
if (GetAutoLayout())
{
Layout();
}
}
#endif
int dlgSchedule::Go(bool modal)
{
int returncode;
if (schedule)
{
// edit mode
recId = schedule->GetRecId();
txtID->SetValue(NumToStr(recId));
chkEnabled->SetValue(schedule->GetEnabled());
calStart->SetValue(schedule->GetStart().GetDateOnly());
timStart->SetTime(schedule->GetStart());
if (schedule->GetEnd().IsValid())
{
calEnd->SetValue(schedule->GetEnd().GetDateOnly());
timEnd->SetTime(schedule->GetEnd());
}
else
{
calEnd->SetValue(wxInvalidDateTime);
timEnd->SetTime(wxInvalidDateTime);
timEnd->Disable();
}
unsigned int x;
for (x = 0; x < schedule->GetMonths().Length(); x++ )
if (schedule->GetMonths()[x] == 't') chkMonths->Check(x, true);
for (x = 0; x < schedule->GetMonthdays().Length(); x++ )
if (schedule->GetMonthdays()[x] == 't') chkMonthdays->Check(x, true);
for (x = 0; x < schedule->GetWeekdays().Length(); x++ )
if (schedule->GetWeekdays()[x] == 't') chkWeekdays->Check(x, true);
for (x = 0; x < schedule->GetHours().Length(); x++ )
if (schedule->GetHours()[x] == 't') chkHours->Check(x, true);
for (x = 0; x < schedule->GetMinutes().Length(); x++ )
if (schedule->GetMinutes()[x] == 't') chkMinutes->Check(x, true);
wxString id, dateToken, timeToken;
wxDateTime val;
long pos = 0;
wxStringTokenizer tkz(schedule->GetExceptions(), wxT("|"));
while (tkz.HasMoreTokens() )
{
dateToken.Empty();
timeToken.Empty();
// First is the ID
id = tkz.GetNextToken();
// Look for a date
if (tkz.HasMoreTokens())
dateToken = tkz.GetNextToken();
// Look for a time
if (tkz.HasMoreTokens())
timeToken = tkz.GetNextToken();
if (!dateToken.IsEmpty() && !timeToken.IsEmpty())
{
val.ParseDate(dateToken);
val.ParseTime(timeToken);
pos = lstExceptions->AppendItem(0, val.FormatDate(), val.FormatTime());
}
else if (!dateToken.IsEmpty() && timeToken.IsEmpty())
{
val.ParseDate(dateToken);
pos = lstExceptions->AppendItem(0, val.FormatDate(), _("<any>"));
}
else if (dateToken.IsEmpty() && !timeToken.IsEmpty())
{
val.ParseTime(timeToken);
pos = lstExceptions->AppendItem(0, _("<any>"), val.FormatTime());
}
lstExceptions->SetItem(pos, 2, BoolToStr(false));
lstExceptions->SetItem(pos, 3, id);
}
wxNotifyEvent ev;
}
else
{
// create mode
}
// setup de-/select buttons
InitSelectAll();
returncode = dlgProperty::Go(modal);
SetSqlReadOnly(true);
return returncode;
}
pgObject *dlgSchedule::CreateObject(pgCollection *collection)
{
pgObject *obj = scheduleFactory.CreateObjects(collection, 0, wxT(" AND jscid=") + NumToStr(recId) + wxT("\n"));
return obj;
}
void dlgSchedule::OnChangeCal(wxCalendarEvent &ev)
{
CheckChange();
}
void dlgSchedule::OnChangeCom(wxCommandEvent &ev)
{
CheckChange();
}
void dlgSchedule::CheckChange()
{
timEnd->Enable(calEnd->GetValue().IsValid());
wxString name = GetName();
bool enable = true;
if (statusBar)
statusBar->SetStatusText(wxEmptyString);
InitSelectAll();
CheckValid(enable, !name.IsEmpty(), _("Please specify name."));
CheckValid(enable, calStart->GetValue().IsValid(), _("Please specify start date."));
if (enable)
{
EnableOK(!GetSql().IsEmpty());
}
else
EnableOK(false);
}
void dlgSchedule::OnSelChangeException(wxListEvent &ev)
{
int sel = lstExceptions->GetSelection();
if (sel >= 0)
{
wxString exDate = lstExceptions->GetText(sel, 0);
wxString exTime = lstExceptions->GetText(sel, 1);
wxDateTime val, null;
if (exDate == _("<any>"))
calException->SetValue(null);
else
{
val.ParseDate(exDate);
calException->SetValue(val);
}
if (exTime == _("<any>"))
timException->SetTime(null);
else
{
val.ParseTime(exTime);
timException->SetTime(val);
}
btnChangeException->Enable();
btnRemoveException->Enable();
}
}
void dlgSchedule::OnAddException(wxCommandEvent &ev)
{
if (!calException->GetValue().IsValid() && timException->GetValue().IsNull())
{
wxMessageBox(_("You must enter a valid date and/or time!"), _("Add exception"), wxICON_EXCLAMATION | wxOK, this);
return;
}
wxString exDate, exTime;
if (calException->GetValue().IsValid())
exDate = calException->GetValue().FormatDate();
else
exDate = _("<any>");
if (!timException->GetValue().IsNull())
exTime = timException->GetValue().Format();
else
exTime = _("<any>");
for (int pos = 0; pos < lstExceptions->GetItemCount(); pos++)
{
if (lstExceptions->GetText(pos, 0) == exDate &&
lstExceptions->GetText(pos, 1) == exTime)
{
wxMessageBox(_("The specified exception already exists!"), _("Add exception"), wxICON_EXCLAMATION | wxOK, this);
return;
}
if (lstExceptions->GetText(pos, 0) == exDate &&
lstExceptions->GetText(pos, 1) == _("<any>"))
{
wxMessageBox(_("An exception already exists for any time on this date!"), _("Add exception"), wxICON_EXCLAMATION | wxOK, this);
return;
}
if (lstExceptions->GetText(pos, 1) == exTime &&
lstExceptions->GetText(pos, 0) == _("<any>"))
{
wxMessageBox(_("An exception already exists for this time on any date!"), _("Add exception"), wxICON_EXCLAMATION | wxOK, this);
return;
}
}
lstExceptions->AppendItem(0, exDate, exTime);
CheckChange();
}
void dlgSchedule::OnChangeException(wxCommandEvent &ev)
{
if (!calException->GetValue().IsValid() && timException->GetValue().IsNull())
{
wxMessageBox(_("You must enter a valid date and/or time!"), _("Add exception"), wxICON_EXCLAMATION | wxOK, this);
return;
}
wxString exDate, exTime;
if (calException->GetValue().IsValid())
exDate = calException->GetValue().FormatDate();
else
exDate = _("<any>");
if (!timException->GetValue().IsNull())
exTime = timException->GetValue().Format();
else
exTime = _("<any>");
long item = lstExceptions->GetFocusedItem();
for (int pos = 0; pos < lstExceptions->GetItemCount(); pos++)
{
if (item != pos)
{
if (lstExceptions->GetText(pos, 0) == exDate &&
lstExceptions->GetText(pos, 1) == exTime)
{
wxMessageBox(_("The specified exception already exists!"), _("Add exception"), wxICON_EXCLAMATION | wxOK, this);
return;
}
if (lstExceptions->GetText(pos, 0) == exDate &&
lstExceptions->GetText(pos, 1) == _("<any>"))
{
wxMessageBox(_("An exception already exists for any time on this date!"), _("Add exception"), wxICON_EXCLAMATION | wxOK, this);
return;
}
if (lstExceptions->GetText(pos, 1) == exTime &&
lstExceptions->GetText(pos, 0) == _("<any>"))
{
wxMessageBox(_("An exception already exists for this time on any date!"), _("Add exception"), wxICON_EXCLAMATION | wxOK, this);
return;
}
}
}
lstExceptions->SetItem(item, 0, exDate);
lstExceptions->SetItem(item, 1, exTime);
lstExceptions->SetItem(item, 2, BoolToStr(true));
CheckChange();
}
void dlgSchedule::OnRemoveException(wxCommandEvent &ev)
{
if (lstExceptions->GetText(lstExceptions->GetFocusedItem(), 3) != wxEmptyString)
{
deleteExceptions.Add(lstExceptions->GetText(lstExceptions->GetFocusedItem(), 3));
}
lstExceptions->DeleteCurrentItem();
btnChangeException->Disable();
btnRemoveException->Disable();
CheckChange();
}
wxString dlgSchedule::GetComment()
{
return txtComment->GetValue();
}
wxString dlgSchedule::GetInsertSql()
{
wxString sql;
if (!schedule)
{
wxString name = GetName();
wxString jscjobid, list = wxT("NULL");
if (jobId)
jscjobid = NumToStr(jobId);
else
jscjobid = wxT("<JobId>");
// Build the various arrays of values
sql = wxT("INSERT INTO pgagent.pga_schedule (jscid, jscjobid, jscname, jscdesc, jscminutes, jschours, jscweekdays, jscmonthdays, jscmonths, jscenabled, jscstart, jscend)\n")
wxT("VALUES(<SchId>, ") + jscjobid + wxT(", ") + qtDbString(name) + wxT(", ") + qtDbString(txtComment->GetValue()) + wxT(", ")
+ wxT("'") + ChkListBox2PgArray(chkMinutes) + wxT("', ")
+ wxT("'") + ChkListBox2PgArray(chkHours) + wxT("', ")
+ wxT("'") + ChkListBox2PgArray(chkWeekdays) + wxT("', ")
+ wxT("'") + ChkListBox2PgArray(chkMonthdays) + wxT("', ")
+ wxT("'") + ChkListBox2PgArray(chkMonths) + wxT("', ")
+ BoolToStr(chkEnabled->GetValue()) + wxT(", ")
+ wxT("'") + DateToAnsiStr(calStart->GetValue() + timStart->GetValue()) + wxT("'");
if (calEnd->GetValue().IsValid())
sql += wxT(", '") + DateToAnsiStr(calEnd->GetValue() + timEnd->GetValue()) + wxT("'");
else
sql += wxT(", NULL");
sql += wxT(");\n");
}
return sql;
}
wxString dlgSchedule::GetUpdateSql()
{
wxString sql, name;
name = GetName();
if (schedule)
{
// edit mode
wxString vars;
if (name != schedule->GetName())
{
if (!vars.IsEmpty())
vars.Append(wxT(", "));
vars.Append(wxT("jscname = ") + qtDbString(name));
}
if (txtComment->GetValue() != schedule->GetComment())
{
if (!vars.IsEmpty())
vars.Append(wxT(", "));
vars.Append(wxT("jscdesc = ") + qtDbString(txtComment->GetValue()));
}
if ((!chkEnabled->IsChecked() && schedule->GetEnabled()) || (chkEnabled->IsChecked() && !schedule->GetEnabled()))
{
if (!vars.IsEmpty())
vars.Append(wxT(", "));
vars.Append(wxT("jscenabled = ") + BoolToStr(chkEnabled->IsChecked()));
}
if (calStart->GetValue() + timStart->GetValue() != schedule->GetStart())
{
if (!vars.IsEmpty())
vars.Append(wxT(", "));
vars.Append(wxT("jscstart = '") + DateToAnsiStr(calStart->GetValue() + timStart->GetValue()) + wxT("'"));
}
if (calEnd->GetValue().IsValid())
{
if (schedule->GetEnd().IsValid())
{
if (calEnd->GetValue() + timEnd->GetValue() != schedule->GetEnd())
{
if (!vars.IsEmpty())
vars.Append(wxT(", "));
vars.Append(wxT("jscend = '") + DateToAnsiStr(calEnd->GetValue() + timEnd->GetValue()) + wxT("'"));
}
}
else
{
if (!vars.IsEmpty())
vars.Append(wxT(", "));
vars.Append(wxT("jscend = '") + DateToAnsiStr(calEnd->GetValue() + wxTimeSpan()) + wxT("'"));
}
}
else
{
if (schedule->GetEnd().IsValid())
{
if (!vars.IsEmpty())
vars.Append(wxT(", "));
vars.Append(wxT("jscend = NULL"));
}
}
if (ChkListBox2StrArray(chkMinutes) != schedule->GetMinutes())
{
if (!vars.IsEmpty())
vars.Append(wxT(", "));
vars.Append(wxT("jscminutes = '") + ChkListBox2PgArray(chkMinutes) + wxT("'"));
}
if (ChkListBox2StrArray(chkHours) != schedule->GetHours())
{
if (!vars.IsEmpty())
vars.Append(wxT(", "));
vars.Append(wxT("jschours = '") + ChkListBox2PgArray(chkHours) + wxT("'"));
}
if (ChkListBox2StrArray(chkWeekdays) != schedule->GetWeekdays())
{
if (!vars.IsEmpty())
vars.Append(wxT(", "));
vars.Append(wxT("jscweekdays = '") + ChkListBox2PgArray(chkWeekdays) + wxT("'"));
}
if (ChkListBox2StrArray(chkMonthdays) != schedule->GetMonthdays())
{
if (!vars.IsEmpty())
vars.Append(wxT(", "));
vars.Append(wxT("jscmonthdays = '") + ChkListBox2PgArray(chkMonthdays) + wxT("'"));
}
if (ChkListBox2StrArray(chkMonths) != schedule->GetMonths())
{
if (!vars.IsEmpty())
vars.Append(wxT(", "));
vars.Append(wxT("jscmonths = '") + ChkListBox2PgArray(chkMonths) + wxT("'"));
}
if (!vars.IsEmpty())
sql = wxT("UPDATE pgagent.pga_schedule SET ") + vars + wxT("\n")
wxT(" WHERE jscid=") + NumToStr(recId) + wxT(";\n");
}
else
{
// create mode
// Handled by GetInsertSQL
}
unsigned int x = 0;
int y = 0;
wxDateTime tmpDateTime;
wxString newDate, newTime;
// Remove old exceptions
for (x = 0; x < deleteExceptions.Count(); x++)
{
sql += wxT("DELETE FROM pgagent.pga_exception\n WHERE jexid = ") + deleteExceptions[x] + wxT(";\n");
}
// Update dirty exceptions
for (y = 0; y < lstExceptions->GetItemCount(); y++)
{
if (lstExceptions->GetText(y, 2) == BoolToStr(true) &&
lstExceptions->GetText(y, 3) != wxEmptyString)
{
if (lstExceptions->GetText(y, 0) == _("<any>"))
newDate = wxT("null");
else
{
tmpDateTime.ParseFormat(lstExceptions->GetText(y, 0), wxT("%x"));
newDate = wxT("'") + tmpDateTime.FormatISODate() + wxT("'");
}
if (lstExceptions->GetText(y, 1) == _("<any>"))
newTime = wxT("null");
else
{
tmpDateTime.ParseTime(lstExceptions->GetText(y, 1));
newTime = wxT("'") + tmpDateTime.FormatISOTime() + wxT("'");
}
sql += wxT("UPDATE pgagent.pga_exception SET jexdate = ") + newDate +
wxT(", jextime = ") + newTime + wxT("\n WHERE jexid = ") +
lstExceptions->GetText(y, 3) + wxT(";\n");
}
}
// Insert new exceptions
for (y = 0; y < lstExceptions->GetItemCount(); y++)
{
if (lstExceptions->GetText(y, 2) == wxEmptyString &&
lstExceptions->GetText(y, 3) == wxEmptyString)
{
if (lstExceptions->GetText(y, 0) == _("<any>"))
newDate = wxT("null");
else
{
tmpDateTime.ParseFormat(lstExceptions->GetText(y, 0), wxT("%x"));
newDate = wxT("'") + tmpDateTime.FormatISODate() + wxT("'");
}
if (lstExceptions->GetText(y, 1) == _("<any>"))
newTime = wxT("null");
else
{
tmpDateTime.ParseTime(lstExceptions->GetText(y, 1));
newTime = wxT("'") + tmpDateTime.FormatISOTime() + wxT("'");
}
sql += wxT("INSERT INTO pgagent.pga_exception (jexscid, jexdate, jextime)\n VALUES (")
+ NumToStr(recId) + wxT(", ") + newDate + wxT(", ") + newTime + wxT(");\n");
}
}
return sql;
}
const wxString dlgSchedule::ChkListBox2PgArray(wxCheckListBox *lb)
{
wxString res = wxT("{");
for (unsigned int x = 0; x < lb->GetCount(); x++)
{
if (lb->IsChecked(x))
res += wxT("t,");
else
res += wxT("f,");
}
if (res.Length() > 1)
res.RemoveLast();
res += wxT("}");
return res;
}
const wxString dlgSchedule::ChkListBox2StrArray(wxCheckListBox *lb)
{
wxString res;
for (unsigned int x = 0; x < lb->GetCount(); x++)
{
if (lb->IsChecked(x))
res += wxT("t");
else
res += wxT("f");
}
return res;
}
void dlgSchedule::OnSelectAll(wxCommandEvent &ev, int origin)
{
bool check = false;
wxBitmapButton *btn;
wxCheckListBox *lb;
wxString tooltip;
switch (origin)
{
case 1:
btn = ((wxBitmapButton *)btnWeekdays);
lb = chkWeekdays;
break;
case 2:
btn = ((wxBitmapButton *)btnMonthdays);
lb = chkMonthdays;
break;
case 3:
btn = ((wxBitmapButton *)btnMonths);
lb = chkMonths;
break;
case 4:
btn = ((wxBitmapButton *)btnHours);
lb = chkHours;
break;
case 5:
btn = ((wxBitmapButton *)btnMinutes);
lb = chkMinutes;
break;
default:
return;
break;
}
for (unsigned int x = 0; x < lb->GetCount(); x++)
{
if (!lb->IsChecked(x))
{
check = true;
break;
}
}
for (unsigned int x = 0; x < lb->GetCount(); x++)
{
lb->Check(x, check);
}
CheckChange();
}
void dlgSchedule::InitSelectAll()
{
bool check = false;
wxBitmapButton *btn;
wxCheckListBox *lb;
wxString tooltip;
btn = ((wxBitmapButton *)btnWeekdays);
lb = chkWeekdays;
for (unsigned int x = 0; x < lb->GetCount(); x++)
{
if (!lb->IsChecked(x))
{
check = true;
break;
}
}
if (check)
{
btn->SetBitmapLabel(*check_png_bmp);
tooltip = _("Select all week days");
}
else
{
btn->SetBitmapLabel(*uncheck_png_bmp);
tooltip = _("Deselect all week days");
}
btn->SetToolTip(tooltip);
check = false;
btn = ((wxBitmapButton *)btnMonthdays);
lb = chkMonthdays;
for (unsigned int x = 0; x < lb->GetCount(); x++)
{
if (!lb->IsChecked(x))
{
check = true;
break;
}
}
if (check)
{
btn->SetBitmapLabel(*check_png_bmp);
tooltip = _("Select all month days");
}
else
{
btn->SetBitmapLabel(*uncheck_png_bmp);
tooltip = _("Deselect all month days");
}
btn->SetToolTip(tooltip);
check = false;
btn = ((wxBitmapButton *)btnMonths);
lb = chkMonths;
for (unsigned int x = 0; x < lb->GetCount(); x++)
{
if (!lb->IsChecked(x))
{
check = true;
break;
}
}
if (check)
{
btn->SetBitmapLabel(*check_png_bmp);
tooltip = _("Select all months");
}
else
{
btn->SetBitmapLabel(*uncheck_png_bmp);
tooltip = _("Deselect all months");
}
btn->SetToolTip(tooltip);
check = false;
btn = ((wxBitmapButton *)btnHours);
lb = chkHours;
for (unsigned int x = 0; x < lb->GetCount(); x++)
{
if (!lb->IsChecked(x))
{
check = true;
break;
}
}
if (check)
{
btn->SetBitmapLabel(*check_png_bmp);
tooltip = _("Select all hours");
}
else
{
btn->SetBitmapLabel(*uncheck_png_bmp);
tooltip = _("Deselect all hours");
}
btn->SetToolTip(tooltip);
check = false;
btn = ((wxBitmapButton *)btnMinutes);
lb = chkMinutes;
for (unsigned int x = 0; x < lb->GetCount(); x++)
{
if (!lb->IsChecked(x))
{
check = true;
break;
}
}
if (check)
{
btn->SetBitmapLabel(*check_png_bmp);
tooltip = _("Select all minutes");
}
else
{
btn->SetBitmapLabel(*uncheck_png_bmp);
tooltip = _("Deselect all minutes");
}
btn->SetToolTip(tooltip);
}

483
agent/dlgStep.cpp Normal file
View file

@ -0,0 +1,483 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// dlgStep.cpp - PostgreSQL Step Property
//
//////////////////////////////////////////////////////////////////////////
// App headers
#include "pgAdmin3.h"
#include "utils/misc.h"
#include "agent/dlgStep.h"
#include "agent/pgaStep.h"
#include "dlg/dlgSelectDatabase.h"
#include "schema/pgTable.h"
// pointer to controls
#define txtID CTRL_TEXT("txtID")
#define chkEnabled CTRL_CHECKBOX("chkEnabled")
#define rbxKind CTRL_RADIOBOX("rbxKind")
#define rbxOnError CTRL_RADIOBOX("rbxOnError")
#define pnlDefinition CTRL_PANEL("pnlDefinition")
#define txtSqlBox CTRL_TEXT("txtSqlBox")
#define cbDatabase CTRL_COMBOBOX2("cbDatabase")
#define txtConnStr CTRL_TEXT("txtConnStr")
#define btnSelDatabase CTRL_BUTTON("btnSelDatabase")
#define rbRemoteConn CTRL_RADIOBUTTON("rbRemoteConn")
#define rbLocalConn CTRL_RADIOBUTTON("rbLocalConn")
#define CTL_SQLBOX 188
BEGIN_EVENT_TABLE(dlgStep, dlgAgentProperty)
EVT_CHECKBOX(XRCID("chkEnabled"), dlgProperty::OnChange)
EVT_COMBOBOX(XRCID("cbDatabase"), dlgProperty::OnChange)
EVT_RADIOBOX(XRCID("rbxKind"), dlgProperty::OnChange)
EVT_RADIOBOX(XRCID("rbxOnError"), dlgProperty::OnChange)
EVT_TEXT(XRCID("txtConnStr"), dlgProperty::OnChange)
EVT_STC_MODIFIED(CTL_SQLBOX, dlgProperty::OnChangeStc)
EVT_BUTTON(XRCID("btnSelDatabase"), dlgStep::OnSelectDatabase)
EVT_RADIOBUTTON(XRCID("rbRemoteConn"), dlgStep::OnSelRemoteConn)
EVT_RADIOBUTTON(XRCID("rbLocalConn"), dlgStep::OnSelLocalConn)
END_EVENT_TABLE();
dlgProperty *pgaStepFactory::CreateDialog(frmMain *frame, pgObject *node, pgObject *parent)
{
return new dlgStep(this, frame, (pgaStep *)node, (pgaJob *)parent);
}
dlgStep::dlgStep(pgaFactory *f, frmMain *frame, pgaStep *node, pgaJob *j)
: dlgAgentProperty(f, frame, wxT("dlgStep"))
{
step = node;
job = j;
if (job)
jobId = job->GetRecId();
else
jobId = 0;
sqlBox = new ctlSQLBox(pnlDefinition, CTL_SQLBOX, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE | wxSUNKEN_BORDER | wxTE_RICH2);
wxWindow *placeholder = CTRL_TEXT("txtSqlBox");
wxSizer *sizer = placeholder->GetContainingSizer();
sizer->Add(sqlBox, 1, wxRIGHT | wxGROW, 5);
sizer->Detach(placeholder);
delete placeholder;
sizer->Layout();
txtID->Disable();
}
pgObject *dlgStep::GetObject()
{
return step;
}
int dlgStep::Go(bool modal)
{
int returncode;
hasConnStrSupport = connection->TableHasColumn(wxT("pgagent"), wxT("pga_jobstep"), wxT("jstconnstr"));
cbDatabase->Append(wxT(" "));
cbDatabase->SetSelection(0);
pgSet *db = connection->ExecuteSet(wxT("SELECT datname FROM pg_database"));
if (db)
{
while (!db->Eof())
{
cbDatabase->Append(db->GetVal(0));
db->MoveNext();
}
delete db;
}
if (step)
{
// edit mode
recId = step->GetRecId();
txtID->SetValue(NumToStr(recId));
if (step->HasConnectionString())
{
rbRemoteConn->SetValue(true);
txtConnStr->Enable(true);
txtConnStr->ChangeValue(step->GetConnStr());
btnSelDatabase->Enable(true);
cbDatabase->Enable(false);
}
else
{
rbLocalConn->SetValue(true);
if (step->GetDbname().IsEmpty())
cbDatabase->SetSelection(0);
else
cbDatabase->SetValue(step->GetDbname());
}
rbxKind->SetSelection(wxString(wxT("sb")).Find(step->GetKindChar()));
rbxOnError->SetSelection(wxString(wxT("fsi")).Find(step->GetOnErrorChar()));
sqlBox->SetText(step->GetCode());
chkEnabled->SetValue(step->GetEnabled());
}
else
{
// create mode
rbLocalConn->SetValue(true);
cbDatabase->Enable(true);
btnSelDatabase->Enable(false);
txtConnStr->Enable(false);
if (!hasConnStrSupport)
rbLocalConn->Enable(false);
}
returncode = dlgProperty::Go(modal);
SetSqlReadOnly(true);
return returncode;
}
pgObject *dlgStep::CreateObject(pgCollection *collection)
{
wxString name = GetName();
pgObject *obj = stepFactory.CreateObjects(collection, 0, wxT(" AND jstid=") + NumToStr(recId) + wxT("\n"));
return obj;
}
void dlgStep::CheckChange()
{
wxString name = GetName();
bool enable;
if (step)
{
enable = name != step->GetName()
|| chkEnabled->GetValue() != step->GetEnabled()
|| rbxKind->GetSelection() != wxString(wxT("sb")).Find(step->GetKindChar())
|| rbxOnError->GetSelection() != wxString(wxT("fsi")).Find(step->GetOnErrorChar())
|| txtComment->GetValue() != step->GetComment()
|| sqlBox->GetText() != step->GetCode();
if (!enable && rbxKind->GetSelection() == 0)
{
if (hasConnStrSupport)
{
if (step->HasConnectionString())
{
if (rbRemoteConn->GetValue())
enable = txtConnStr->GetValue().Trim() != step->GetConnStr();
else
enable = true;
}
else
{
if (rbRemoteConn->GetValue())
enable = true;
else
enable = cbDatabase->GetValue().Trim() != step->GetDbname();
}
}
else
{
enable = cbDatabase->GetValue().Trim() != step->GetDbname();
}
}
}
else
{
enable = true;
}
if (statusBar)
statusBar->SetStatusText(wxEmptyString);
CheckValid(enable, !name.IsEmpty(), _("Please specify name."));
CheckValid(enable, sqlBox->GetLength() > 0, _("Please specify code to execute."));
// Disable/enable the database combo
if (rbxKind->GetSelection() == 1)
{
rbRemoteConn->Enable(false);
rbLocalConn->Enable(false);
// I don't see any reason to make
// the database combobox selection to 0
//cbDatabase->SetSelection(0);
txtConnStr->Enable(false);
btnSelDatabase->Enable(false);
cbDatabase->Enable(false);
}
else
{
if (hasConnStrSupport)
{
rbRemoteConn->Enable(true);
rbLocalConn->Enable(true);
if (rbRemoteConn->GetValue())
{
wxString validConnStr;
btnSelDatabase->Enable(true);
txtConnStr->Enable(true);
cbDatabase->Enable(false);
CheckValid(enable, !txtConnStr->GetValue().Trim().IsEmpty(), _("Please select a connection string."));
CheckValid(enable, dlgSelectDatabase::getValidConnectionString(txtConnStr->GetValue().Trim(), validConnStr), _("Please enter a valid connection string"));
}
else
{
cbDatabase->Enable(true);
btnSelDatabase->Enable(false);
txtConnStr->Enable(false);
CheckValid(enable, !cbDatabase->GetValue().Trim().IsEmpty(), _("Please select a database."));
}
}
else
{
cbDatabase->Enable(true);
// Make sure both radio buttons are disabled
rbRemoteConn->Enable(false);
rbLocalConn->Enable(false);
CheckValid(enable, !cbDatabase->GetValue().Trim().IsEmpty(), _("Please select a database."));
}
}
EnableOK(enable);
}
wxString dlgStep::GetComment()
{
return txtComment->GetValue();
}
wxString dlgStep::GetInsertSql()
{
wxString sql;
if (!step)
{
wxString name = GetName();
wxString kind = wxT("sb")[rbxKind->GetSelection()];
wxString onerror = wxT("fsi")[rbxOnError->GetSelection()];
wxString db, connstr;
wxString jstjobid;
if (jobId)
jstjobid = NumToStr(jobId);
else
jstjobid = wxT("<JobId>");
// SQL script expected
if (rbxKind->GetSelection() == 0)
{
if (hasConnStrSupport && rbRemoteConn->GetValue())
{
connstr = qtDbString(txtConnStr->GetValue().Trim());
db = wxT("''");
}
else
{
db = qtDbString(cbDatabase->GetValue().Trim());
connstr = wxT("''");
}
}
else
{
db = wxT("''");
connstr = wxT("''");
}
sql = wxT("INSERT INTO pgagent.pga_jobstep (jstid, jstjobid, jstname, jstdesc, jstenabled, jstkind, jstonerror, jstcode, jstdbname");
if (hasConnStrSupport)
sql += wxT(", jstconnstr");
sql += wxT(")\n ") \
wxT("SELECT <StpId>, ") + jstjobid + wxT(", ") + qtDbString(name) + wxT(", ") + qtDbString(txtComment->GetValue()) + wxT(", ")
+ BoolToStr(chkEnabled->GetValue()) + wxT(", ") + qtDbString(kind) + wxT(", ")
+ qtDbString(onerror) + wxT(", ") + qtDbString(sqlBox->GetText()) + wxT(", ") + db;
if (hasConnStrSupport)
{
sql += wxT(", ") + connstr;
}
sql += wxT(";\n");
}
return sql;
}
wxString dlgStep::GetUpdateSql()
{
wxString sql;
if (step)
{
// edit mode
wxString name = GetName();
wxString kind = wxT("sb")[rbxKind->GetSelection()];
wxString vars;
if (name != step->GetName())
{
if (!vars.IsEmpty())
vars.Append(wxT(", "));
vars.Append(wxT("jstname=") + qtDbString(name));
}
if (chkEnabled->GetValue() != step->GetEnabled())
{
if (!vars.IsEmpty())
vars.Append(wxT(", "));
vars.Append(wxT("jstenabled=") + BoolToStr(chkEnabled->GetValue()));
}
if (hasConnStrSupport && kind == wxT("s"))
{
if (rbRemoteConn->GetValue())
{
if (step->HasConnectionString())
{
if (txtConnStr->GetValue().Trim() != step->GetConnStr())
{
if (!vars.IsEmpty())
vars.Append(wxT(", "));
vars.Append(wxT("jstconnstr=") + qtDbString(txtConnStr->GetValue().Trim()));
}
}
else
{
if (!vars.IsEmpty())
vars.Append(wxT(", "));
vars.Append(wxT("jstconnstr=") + qtDbString(txtConnStr->GetValue().Trim()) + wxT(", "));
vars.Append(wxT("jstdbname=''"));
}
}
else
{
if (step->HasConnectionString())
{
if (!vars.IsEmpty())
vars.Append(wxT(", "));
vars.Append(wxT("jstdbname=") + qtDbString(cbDatabase->GetValue().Trim()) + wxT(", "));
vars.Append(wxT("jstconnstr=''"));
}
else
{
if (cbDatabase->GetValue().Trim() != step->GetDbname())
{
if (!vars.IsEmpty())
vars.Append(wxT(", "));
vars.Append(wxT("jstdbname=") + qtDbString(cbDatabase->GetValue().Trim()));
}
}
}
}
else if (kind == wxT("s"))
{
if (cbDatabase->GetValue().Trim() != step->GetDbname())
{
if (!vars.IsEmpty())
vars.Append(wxT(", "));
vars.Append(wxT("jstdbname=") + qtDbString(cbDatabase->GetValue().Trim()));
}
}
else
{
if (!vars.IsEmpty())
vars.Append(wxT(", "));
vars.Append(wxT("jstdbname=''"));
if (hasConnStrSupport)
vars.Append(wxT(", jstconnstr=''"));
}
if (rbxKind->GetSelection() != kind)
{
if (!vars.IsEmpty())
vars.Append(wxT(", "));
vars.Append(wxT("jstkind=") + qtDbString(kind));
}
if (rbxOnError->GetSelection() != wxString(wxT("fsi")).Find(step->GetOnErrorChar()))
{
wxString onerror = wxT("fsi")[rbxOnError->GetSelection()];
if (!vars.IsEmpty())
vars.Append(wxT(", "));
vars.Append(wxT("jstonerror='") + onerror + wxT("'"));
}
if (txtComment->GetValue() != step->GetComment())
{
if (!vars.IsEmpty())
vars.Append(wxT(", "));
vars.Append(wxT("jstdesc=") + qtDbString(txtComment->GetValue()));
}
if (sqlBox->GetText() != step->GetCode())
{
{
if (!vars.IsEmpty())
vars.Append(wxT(", "));
vars.Append(wxT("jstcode=") + qtDbString(sqlBox->GetText()));
}
}
if (!vars.IsEmpty())
sql = wxT("UPDATE pgagent.pga_jobstep\n")
wxT(" SET ") + vars + wxT("\n")
wxT(" WHERE jstid=") + NumToStr(step->GetRecId()) +
wxT(";\n");
}
else
{
// create mode; handled by GetInsertSql()
}
return sql;
}
bool dlgStep::IsUpToDate()
{
if (step && !step->IsUpToDate())
return false;
else
return true;
}
void dlgStep::OnSelectDatabase(wxCommandEvent &ev)
{
dlgSelectDatabase dlgSD(this, wxID_ANY);
if (dlgSD.ShowModal() == wxID_OK)
{
wxString strConnStr = dlgSD.getConnInfo();
if (!strConnStr.IsEmpty())
txtConnStr->SetValue(strConnStr);
}
}
void dlgStep::OnSelRemoteConn(wxCommandEvent &ev)
{
if (rbRemoteConn->GetValue())
{
cbDatabase->Enable(false);
btnSelDatabase->Enable(true);
txtConnStr->Enable(true);
}
dlgProperty::OnChange(ev);
}
void dlgStep::OnSelLocalConn(wxCommandEvent &ev)
{
if (rbLocalConn->GetValue())
{
cbDatabase->Enable(true);
btnSelDatabase->Enable(false);
txtConnStr->Enable(false);
}
dlgProperty::OnChange(ev);
}

23
agent/module.mk Normal file
View file

@ -0,0 +1,23 @@
#######################################################################
#
# pgAdmin III - PostgreSQL Tools
#
# Copyright (C) 2002 - 2016, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
# module.mk - pgadmin/agent/ Makefile fragment
#
#######################################################################
pgadmin3_SOURCES += \
agent/dlgJob.cpp \
agent/dlgSchedule.cpp \
agent/dlgStep.cpp \
agent/pgaJob.cpp \
agent/pgaSchedule.cpp \
agent/pgaStep.cpp
EXTRA_DIST += \
agent/module.mk

397
agent/pgaJob.cpp Normal file
View file

@ -0,0 +1,397 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// pgaJob.h - PostgreSQL Agent Job
//
//////////////////////////////////////////////////////////////////////////
// wxWindows headers
#include <wx/wx.h>
// App headers
#include "pgAdmin3.h"
#include "utils/misc.h"
#include "frm/frmMain.h"
#include "schema/pgObject.h"
#include "schema/pgCollection.h"
#include "schema/pgDatabase.h"
#include "agent/pgaJob.h"
#include "agent/pgaStep.h"
#include "agent/pgaSchedule.h"
pgaJob::pgaJob(const wxString &newName)
: pgServerObject(jobFactory, newName)
{
}
wxString pgaJob::GetTranslatedMessage(int kindOfMessage) const
{
wxString message = wxEmptyString;
switch (kindOfMessage)
{
case RETRIEVINGDETAILS:
message = _("Retrieving details on pgAgent job");
break;
case REFRESHINGDETAILS:
message = _("Refreshing pgAgent job");
break;
case PROPERTIESREPORT:
message = _("pgAgent job properties report");
break;
case PROPERTIES:
message = _("pgAgent job properties");
break;
case DDLREPORT:
message = _("pgAgent job DDL report");
break;
case DEPENDENCIESREPORT:
message = _("pgAgent job dependencies report");
break;
case DEPENDENCIES:
message = _("pgAgent job dependencies");
break;
case DEPENDENTSREPORT:
message = _("pgAgent job dependents report");
break;
case DEPENDENTS:
message = _("pgAgent job dependents");
break;
case DROPEXCLUDINGDEPS:
message = wxString::Format(_("Are you sure you wish to drop job \"%s\"?"),
GetFullIdentifier().c_str());
break;
case DROPTITLE:
message = _("Drop job?");
break;
}
if (!message.IsEmpty() && !(kindOfMessage == DROPEXCLUDINGDEPS || kindOfMessage == DROPTITLE))
message += wxT(" - ") + GetName();
return message;
}
int pgaJob::GetIconId()
{
if (GetEnabled())
return jobFactory.GetIconId();
else
return jobFactory.GetDisabledId();
}
wxMenu *pgaJob::GetNewMenu()
{
wxMenu *menu = pgObject::GetNewMenu();
if (1) // check priv.
{
stepFactory.AppendMenu(menu);
scheduleFactory.AppendMenu(menu);
}
return menu;
}
bool pgaJob::DropObject(wxFrame *frame, ctlTree *browser, bool cascaded)
{
return GetConnection()->ExecuteVoid(wxT("DELETE FROM pgagent.pga_job WHERE jobid=") + NumToStr(GetRecId()));
}
void pgaJob::ShowTreeDetail(ctlTree *browser, frmMain *form, ctlListView *properties, ctlSQLBox *sqlPane)
{
if (!expandedKids)
{
expandedKids = true;
browser->RemoveDummyChild(this);
// Log
wxLogInfo(wxT("Adding child objects to Job."));
browser->AppendCollection(this, scheduleFactory);
browser->AppendCollection(this, stepFactory);
}
if (properties)
{
CreateListColumns(properties);
properties->AppendItem(_("Name"), GetName());
properties->AppendItem(_("ID"), GetRecId());
properties->AppendYesNoItem(_("Enabled"), GetEnabled());
properties->AppendItem(_("Host agent"), GetHostAgent());
properties->AppendItem(_("Job class"), GetJobclass());
properties->AppendItem(_("Created"), GetCreated());
properties->AppendItem(_("Changed"), GetChanged());
properties->AppendItem(_("Next run"), GetNextrun());
properties->AppendItem(_("Last run"), GetLastrun());
properties->AppendItem(_("Last result"), GetLastresult());
if (!GetCurrentAgent().IsEmpty())
properties->AppendItem(_("Running at"), GetCurrentAgent());
else
properties->AppendItem(_("Running at"), _("Not currently running"));
properties->AppendItem(_("Comment"), firstLineOnly(GetComment()));
}
}
pgObject *pgaJob::Refresh(ctlTree *browser, const wxTreeItemId item)
{
pgObject *job = 0;
pgObject *obj = browser->GetObject(browser->GetItemParent(item));
if (obj && obj->IsCollection())
job = jobFactory.CreateObjects((pgCollection *)obj, 0, wxT("\n WHERE j.jobid=") + NumToStr(GetRecId()));
return job;
}
pgObject *pgaJobFactory::CreateObjects(pgCollection *collection, ctlTree *browser, const wxString &restriction)
{
pgaJob *job = 0;
pgSet *jobs = collection->GetConnection()->ExecuteSet(
wxT("SELECT j.*, cl.*, ag.*, sub.jlgstatus AS joblastresult ")
wxT(" FROM pgagent.pga_job j JOIN")
wxT(" pgagent.pga_jobclass cl ON cl.jclid=jobjclid LEFT OUTER JOIN")
wxT(" pgagent.pga_jobagent ag ON ag.jagpid=jobagentid LEFT OUTER JOIN")
wxT(" (SELECT DISTINCT ON (jlgjobid) jlgstatus, jlgjobid")
wxT(" FROM pgagent.pga_joblog")
wxT(" ORDER BY jlgjobid, jlgid desc) sub ON sub.jlgjobid = j.jobid ")
+ restriction +
wxT("ORDER BY jobname;"));
if (jobs)
{
while (!jobs->Eof())
{
wxString status;
if (jobs->GetVal(wxT("joblastresult")) == wxT("r"))
status = _("Running");
else if (jobs->GetVal(wxT("joblastresult")) == wxT("s"))
status = _("Successful");
else if (jobs->GetVal(wxT("joblastresult")) == wxT("f"))
status = _("Failed");
else if (jobs->GetVal(wxT("joblastresult")) == wxT("d"))
status = _("Aborted");
else if (jobs->GetVal(wxT("joblastresult")) == wxT("i"))
status = _("No steps");
else
status = _("Unknown");
job = new pgaJob(jobs->GetVal(wxT("jobname")));
job->iSetServer(collection->GetServer());
job->iSetRecId(jobs->GetLong(wxT("jobid")));
job->iSetComment(jobs->GetVal(wxT("jobdesc")));
job->iSetEnabled(jobs->GetBool(wxT("jobenabled")));
job->iSetJobclass(jobs->GetVal(wxT("jclname")));
job->iSetHostAgent(jobs->GetVal(wxT("jobhostagent")));
job->iSetCreated(jobs->GetDateTime(wxT("jobcreated")));
job->iSetChanged(jobs->GetDateTime(wxT("jobchanged")));
job->iSetNextrun(jobs->GetDateTime(wxT("jobnextrun")));
job->iSetLastrun(jobs->GetDateTime(wxT("joblastrun")));
job->iSetLastresult(status);
job->iSetCurrentAgent(jobs->GetVal(wxT("jagstation")));
if (browser)
{
browser->AppendObject(collection, job);
jobs->MoveNext();
}
else
break;
}
delete jobs;
}
return job;
}
void pgaJob::ShowStatistics(frmMain *form, ctlListView *statistics)
{
wxString sql =
wxT("SELECT jlgid")
wxT(", jlgstatus")
wxT(", jlgstart")
wxT(", jlgduration")
wxT(", (jlgstart + jlgduration) AS endtime")
wxT(" FROM pgagent.pga_joblog\n")
wxT(" WHERE jlgjobid = ") + NumToStr(GetRecId()) +
wxT(" ORDER BY jlgstart DESC") +
wxT(" LIMIT ") + NumToStr(settings->GetMaxRows());
if (statistics)
{
wxLogInfo(wxT("Displaying statistics for job %s"), GetFullIdentifier().c_str());
// Add the statistics view columns
statistics->ClearAll();
statistics->AddColumn(_("Run"), 50);
statistics->AddColumn(_("Status"), 60);
statistics->AddColumn(_("Start time"), 95);
statistics->AddColumn(_("End time"), 95);
statistics->AddColumn(_("Duration"), 70);
pgSet *stats = GetConnection()->ExecuteSet(sql);
wxString status;
wxDateTime startTime;
wxDateTime endTime;
if (stats)
{
while (!stats->Eof())
{
if (stats->GetVal(1) == wxT("r"))
status = _("Running");
else if (stats->GetVal(1) == wxT("s"))
status = _("Successful");
else if (stats->GetVal(1) == wxT("f"))
status = _("Failed");
else if (stats->GetVal(1) == wxT("d"))
status = _("Aborted");
else if (stats->GetVal(1) == wxT("i"))
status = _("No steps");
else
status = _("Unknown");
startTime.ParseDateTime(stats->GetVal(2));
endTime.ParseDateTime(stats->GetVal(4));
long pos = statistics->AppendItem(stats->GetVal(0), status, startTime.Format());
if (stats->GetVal(4).Length() > 0)
statistics->SetItem(pos, 3, endTime.Format());
statistics->SetItem(pos, 4, stats->GetVal(3));
stats->MoveNext();
}
delete stats;
}
}
}
bool pgaJob::RunNow()
{
if (!GetConnection()->ExecuteVoid(wxT("UPDATE pgagent.pga_job SET jobnextrun = now() WHERE jobid=") + NumToStr(GetRecId())))
return false;
return true;
}
pgaJobCollection::pgaJobCollection(pgaFactory *factory, pgServer *sv)
: pgServerObjCollection(factory, sv)
{
}
wxString pgaJobCollection::GetTranslatedMessage(int kindOfMessage) const
{
wxString message = wxEmptyString;
switch (kindOfMessage)
{
case RETRIEVINGDETAILS:
message = _("Retrieving details on pgAgent jobs");
break;
case REFRESHINGDETAILS:
message = _("Refreshing pgAgent jobs");
break;
case OBJECTSLISTREPORT:
message = _("pgAgent jobs list report");
break;
}
return message;
}
pgaJobObject::pgaJobObject(pgaJob *_job, pgaFactory &factory, const wxString &newName)
: pgServerObject(factory, newName)
{
job = _job;
server = job->GetServer();
}
pgaJobObjCollection::pgaJobObjCollection(pgaFactory *factory, pgaJob *_job)
: pgServerObjCollection(factory, _job->GetServer())
{
job = _job;
}
bool pgaJobObjCollection::CanCreate()
{
return job->CanCreate();
}
pgCollection *pgaJobObjFactory::CreateCollection(pgObject *obj)
{
return new pgaJobObjCollection(GetCollectionFactory(), (pgaJob *)obj);
}
/////////////////////////////
#include "images/job.pngc"
#include "images/jobs.pngc"
#include "images/jobdisabled.pngc"
pgaJobFactory::pgaJobFactory()
: pgServerObjFactory(__("pgAgent Job"), __("New Job"), __("Create a new Job."), job_png_img)
{
metaType = PGM_JOB;
disabledId = addIcon(jobdisabled_png_img);
}
pgCollection *pgaJobFactory::CreateCollection(pgObject *obj)
{
return new pgaJobCollection(GetCollectionFactory(), (pgServer *)obj);
}
pgaJobFactory jobFactory;
static pgaCollectionFactory cf(&jobFactory, __("Jobs"), jobs_png_img);
runNowFactory::runNowFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar) : contextActionFactory(list)
{
mnu->Append(id, _("&Run now"), _("Reschedule the job to run now."));
}
wxWindow *runNowFactory::StartDialog(frmMain *form, pgObject *obj)
{
if (!((pgaJob *)(obj))->RunNow())
{
wxLogError(_("Failed to reschedule the job."));
}
form->Refresh(obj);
return 0;
}
bool runNowFactory::CheckEnable(pgObject *obj)
{
if (obj)
{
if (obj->GetMetaType() == PGM_JOB && !obj->IsCollection())
return true;
}
return false;
}

576
agent/pgaSchedule.cpp Normal file
View file

@ -0,0 +1,576 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// pgaSchedule.cpp - PostgreSQL Agent Schedule
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
#include <wx/arrimpl.cpp>
// App headers
#include "utils/misc.h"
#include "schema/pgObject.h"
#include "schema/pgDatabase.h"
#include "schema/pgCollection.h"
#include "agent/pgaSchedule.h"
#include "agent/pgaStep.h"
#include "agent/pgaSchedule.h"
pgaSchedule::pgaSchedule(pgCollection *_collection, const wxString &newName)
: pgaJobObject(_collection->GetJob(), scheduleFactory, newName)
{
}
wxString pgaSchedule::GetTranslatedMessage(int kindOfMessage) const
{
wxString message = wxEmptyString;
switch (kindOfMessage)
{
case RETRIEVINGDETAILS:
message = _("Retrieving details on pgAgent schedule");
break;
case REFRESHINGDETAILS:
message = _("Refreshing pgAgent schedule");
break;
case PROPERTIESREPORT:
message = _("pgAgent schedule properties report");
break;
case PROPERTIES:
message = _("pgAgent schedule properties");
break;
case DDLREPORT:
message = _("pgAgent schedule DDL report");
break;
case DEPENDENCIESREPORT:
message = _("pgAgent schedule dependencies report");
break;
case DEPENDENCIES:
message = _("pgAgent schedule dependencies");
break;
case DEPENDENTSREPORT:
message = _("pgAgent schedule dependents report");
break;
case DEPENDENTS:
message = _("pgAgent schedule dependents");
break;
case DROPEXCLUDINGDEPS:
message = wxString::Format(_("Are you sure you wish to drop schedule \"%s\"?"),
GetFullIdentifier().c_str());
break;
case DROPTITLE:
message = _("Drop schedule?");
break;
}
if (!message.IsEmpty() && !(kindOfMessage == DROPEXCLUDINGDEPS || kindOfMessage == DROPTITLE))
message += wxT(" - ") + GetName();
return message;
}
bool pgaSchedule::DropObject(wxFrame *frame, ctlTree *browser, bool cascaded)
{
return GetConnection()->ExecuteVoid(wxT("DELETE FROM pgagent.pga_schedule WHERE jscid=") + NumToStr(GetRecId()));
}
void pgaSchedule::ShowTreeDetail(ctlTree *browser, frmMain *form, ctlListView *properties, ctlSQLBox *sqlPane)
{
if (!expandedKids)
{
expandedKids = true;
}
if (properties)
{
CreateListColumns(properties);
properties->AppendItem(_("Name"), GetName());
properties->AppendItem(_("ID"), GetRecId());
properties->AppendYesNoItem(_("Enabled"), GetEnabled());
properties->AppendItem(_("Start date"), GetStart());
properties->AppendItem(_("End date"), GetEnd());
properties->AppendItem(_("Minutes"), GetMinutesString());
properties->AppendItem(_("Hours"), GetHoursString());
properties->AppendItem(_("Weekdays"), GetWeekdaysString());
properties->AppendItem(_("Monthdays"), GetMonthdaysString());
properties->AppendItem(_("Months"), GetMonthsString());
properties->AppendItem(_("Exceptions"), GetExceptionsString());
properties->AppendItem(_("Comment"), firstLineOnly(GetComment()));
}
}
pgObject *pgaSchedule::Refresh(ctlTree *browser, const wxTreeItemId item)
{
pgObject *schedule = 0;
pgCollection *coll = browser->GetParentCollection(item);
if (coll)
schedule = scheduleFactory.CreateObjects(coll, 0, wxT("\n AND jscid=") + NumToStr(GetRecId()));
return schedule;
}
pgObject *pgaScheduleFactory::CreateObjects(pgCollection *collection, ctlTree *browser, const wxString &restriction)
{
pgaSchedule *schedule = 0;
wxString tmp;
pgSet *schedules = collection->GetConnection()->ExecuteSet(
wxT("SELECT * FROM pgagent.pga_schedule\n")
wxT(" WHERE jscjobid=") + NumToStr(collection->GetJob()->GetRecId()) + wxT("\n")
+ restriction +
wxT(" ORDER BY jscname"));
if (schedules)
{
while (!schedules->Eof())
{
schedule = new pgaSchedule(collection, schedules->GetVal(wxT("jscname")));
schedule->iSetRecId(schedules->GetLong(wxT("jscid")));
schedule->iSetStart(schedules->GetDateTime(wxT("jscstart")));
schedule->iSetEnd(schedules->GetDateTime(wxT("jscend")));
schedule->iSetEnabled(schedules->GetBool(wxT("jscenabled")));
tmp = schedules->GetVal(wxT("jscminutes"));
tmp.Replace(wxT("{"), wxT(""));
tmp.Replace(wxT("}"), wxT(""));
tmp.Replace(wxT(","), wxT(""));
schedule->iSetMinutes(tmp);
tmp = schedules->GetVal(wxT("jschours"));
tmp.Replace(wxT("{"), wxT(""));
tmp.Replace(wxT("}"), wxT(""));
tmp.Replace(wxT(","), wxT(""));
schedule->iSetHours(tmp);
tmp = schedules->GetVal(wxT("jscweekdays"));
tmp.Replace(wxT("{"), wxT(""));
tmp.Replace(wxT("}"), wxT(""));
tmp.Replace(wxT(","), wxT(""));
schedule->iSetWeekdays(tmp);
tmp = schedules->GetVal(wxT("jscmonthdays"));
tmp.Replace(wxT("{"), wxT(""));
tmp.Replace(wxT("}"), wxT(""));
tmp.Replace(wxT(","), wxT(""));
schedule->iSetMonthdays(tmp);
tmp = schedules->GetVal(wxT("jscmonths"));
tmp.Replace(wxT("{"), wxT(""));
tmp.Replace(wxT("}"), wxT(""));
tmp.Replace(wxT(","), wxT(""));
schedule->iSetMonths(tmp);
schedule->iSetComment(schedules->GetVal(wxT("jscdesc")));
pgSet *exceptions = collection->GetConnection()->ExecuteSet(
wxT("SELECT * FROM pgagent.pga_exception\n")
wxT(" WHERE jexscid=") + NumToStr(schedule->GetRecId()) + wxT("\n"));
tmp.Empty();
if (exceptions)
{
while (!exceptions->Eof())
{
tmp += exceptions->GetVal(wxT("jexid"));
tmp += wxT("|");
tmp += exceptions->GetVal(wxT("jexdate"));
tmp += wxT("|");
tmp += exceptions->GetVal(wxT("jextime"));
tmp += wxT("|");
exceptions->MoveNext();
}
}
schedule->iSetExceptions(tmp);
delete exceptions;
if (browser)
{
browser->AppendObject(collection, schedule);
schedules->MoveNext();
}
else
break;
}
delete schedules;
}
return schedule;
}
wxString pgaSchedule::GetMinutesString()
{
size_t x = 0;
bool isWildcard = true;
wxString res, tmp;
for (x = 0; x <= minutes.Length(); x++)
{
if (minutes[x] == 't')
{
tmp.Printf(wxT("%.2d, "), (int)x);
res += tmp;
isWildcard = false;
}
}
if (isWildcard)
{
res = _("Every minute");
}
else
{
if (res.Length() > 2)
{
res.RemoveLast();
res.RemoveLast();
}
}
return res;
}
wxString pgaSchedule::GetHoursString()
{
size_t x = 0;
bool isWildcard = true;
wxString res, tmp;
for (x = 0; x <= hours.Length(); x++)
{
if (hours[x] == 't')
{
tmp.Printf(wxT("%.2d, "), (int)x);
res += tmp;
isWildcard = false;
}
}
if (isWildcard)
{
res = _("Every hour");
}
else
{
if (res.Length() > 2)
{
res.RemoveLast();
res.RemoveLast();
}
}
return res;
}
wxString pgaSchedule::GetWeekdaysString()
{
size_t x = 0;
bool isWildcard = true;
wxString res;
for (x = 0; x <= weekdays.Length(); x++)
{
if (weekdays[x] == 't')
{
switch (x)
{
case 0:
res += _("Sunday");
res += wxT(", ");
break;
case 1:
res += _("Monday");
res += wxT(", ");
break;
case 2:
res += _("Tuesday");
res += wxT(", ");
break;
case 3:
res += _("Wednesday");
res += wxT(", ");
break;
case 4:
res += _("Thursday");
res += wxT(", ");
break;
case 5:
res += _("Friday");
res += wxT(", ");
break;
case 6:
res += _("Saturday");
res += wxT(", ");
break;
default:
res += _("The mythical 8th day!");
res += wxT(", ");
break;
}
isWildcard = false;
}
}
if (isWildcard)
{
res = _("Any day of the week");
}
else
{
if (res.Length() > 2)
{
res.RemoveLast();
res.RemoveLast();
}
}
return res;
}
wxString pgaSchedule::GetMonthdaysString()
{
size_t x = 0;
bool isWildcard = true;
wxString res, tmp;
for (x = 0; x <= monthdays.Length(); x++)
{
if (monthdays[x] == 't')
{
if (x < 31)
{
tmp.Printf(wxT("%.2d, "), (int)(x + 1));
res += tmp;
}
else
{
res += _("Last day");
res += wxT(", ");
}
isWildcard = false;
}
}
if (isWildcard)
{
res = _("Every day");
}
else
{
if (res.Length() > 2)
{
res.RemoveLast();
res.RemoveLast();
}
}
return res;
}
wxString pgaSchedule::GetMonthsString()
{
size_t x = 0;
bool isWildcard = true;
wxString res;
for (x = 0; x <= months.Length(); x++)
{
if (months[x] == 't')
{
switch (x)
{
case 0:
res += _("January");
res += wxT(", ");
break;
case 1:
res += _("February");
res += wxT(", ");
break;
case 2:
res += _("March");
res += wxT(", ");
break;
case 3:
res += _("April");
res += wxT(", ");
break;
case 4:
res += _("May");
res += wxT(", ");
break;
case 5:
res += _("June");
res += wxT(", ");
break;
case 6:
res += _("July");
res += wxT(", ");
break;
case 7:
res += _("August");
res += wxT(", ");
break;
case 8:
res += _("September");
res += wxT(", ");
break;
case 9:
res += _("October");
res += wxT(", ");
break;
case 10:
res += _("November");
res += wxT(", ");
break;
case 11:
res += _("December");
res += wxT(", ");
break;
default:
res += _("The mythical 13th month!");
res += wxT(", ");
break;
}
isWildcard = false;
}
}
if (isWildcard)
{
res = _("Every month");
}
else
{
if (res.Length() > 2)
{
res.RemoveLast();
res.RemoveLast();
}
}
return res;
}
wxString pgaSchedule::GetExceptionsString()
{
wxString tmp, token, dateToken, timeToken;
wxDateTime val;
wxStringTokenizer tkz(exceptions, wxT("|"));
while (tkz.HasMoreTokens() )
{
dateToken.Empty();
timeToken.Empty();
// First is the ID which can be ignored
token = tkz.GetNextToken();
// Look for a date
if (tkz.HasMoreTokens())
dateToken = tkz.GetNextToken();
// Look for a time
if (tkz.HasMoreTokens())
timeToken = tkz.GetNextToken();
if (tmp.IsEmpty())
tmp += wxT("[");
else
tmp += wxT(", [");
if (!dateToken.IsEmpty() && !timeToken.IsEmpty())
{
val.ParseDate(dateToken);
val.ParseTime(timeToken);
tmp += val.Format();
}
else if (!dateToken.IsEmpty() && timeToken.IsEmpty())
{
val.ParseDate(dateToken);
tmp += val.FormatDate();
}
else if (dateToken.IsEmpty() && !timeToken.IsEmpty())
{
val.ParseTime(timeToken);
tmp += val.FormatTime();
}
tmp += wxT("]");
}
return tmp;
}
/////////////////////////////
pgaScheduleCollection::pgaScheduleCollection(pgaFactory *factory, pgaJob *job)
: pgaJobObjCollection(factory, job)
{
}
wxString pgaScheduleCollection::GetTranslatedMessage(int kindOfMessage) const
{
wxString message = wxEmptyString;
switch (kindOfMessage)
{
case RETRIEVINGDETAILS:
message = _("Retrieving details on pgAgent schedules");
break;
case REFRESHINGDETAILS:
message = _("Refreshing pgAgent schedules");
break;
case OBJECTSLISTREPORT:
message = _("pgAgent schedules list report");
break;
}
return message;
}
/////////////////////////////
#include "images/schedule.pngc"
#include "images/schedules.pngc"
pgaScheduleFactory::pgaScheduleFactory()
: pgaJobObjFactory(__("Schedule"), __("New Schedule"), __("Create a new Schedule."), schedule_png_img)
{
metaType = PGM_SCHEDULE;
}
pgCollection *pgaScheduleFactory::CreateCollection(pgObject *obj)
{
return new pgaScheduleCollection(GetCollectionFactory(), (pgaJob *)obj);
}
pgaScheduleFactory scheduleFactory;
static pgaCollectionFactory cf(&scheduleFactory, __("Schedules"), schedules_png_img);

327
agent/pgaStep.cpp Normal file
View file

@ -0,0 +1,327 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// pgaStep.cpp - PostgreSQL Agent Step
//
//////////////////////////////////////////////////////////////////////////
// wxWindows headers
#include <wx/wx.h>
// App headers
#include "pgAdmin3.h"
#include "utils/misc.h"
#include "schema/pgObject.h"
#include "schema/pgDatabase.h"
#include "schema/pgCollection.h"
#include "agent/pgaStep.h"
#include "agent/pgaSchedule.h"
pgaStep::pgaStep(pgCollection *_collection, const wxString &newName)
: pgaJobObject(_collection->GetJob(), stepFactory, newName)
{
}
wxString pgaStep::GetTranslatedMessage(int kindOfMessage) const
{
wxString message = wxEmptyString;
switch (kindOfMessage)
{
case RETRIEVINGDETAILS:
message = _("Retrieving details on pgAgent step");
break;
case REFRESHINGDETAILS:
message = _("Refreshing pgAgent step");
break;
case PROPERTIESREPORT:
message = _("pgAgent step properties report");
break;
case PROPERTIES:
message = _("pgAgent step properties");
break;
case DDLREPORT:
message = _("pgAgent step DDL report");
break;
case DEPENDENCIESREPORT:
message = _("pgAgent step dependencies report");
break;
case DEPENDENCIES:
message = _("pgAgent step dependencies");
break;
case DEPENDENTSREPORT:
message = _("pgAgent step dependents report");
break;
case DEPENDENTS:
message = _("pgAgent step dependents");
break;
case DROPEXCLUDINGDEPS:
message = wxString::Format(_("Are you sure you wish to drop step \"%s\"?"),
GetFullIdentifier().c_str());
break;
case DROPTITLE:
message = _("Drop step?");
break;
}
if (!message.IsEmpty() && !(kindOfMessage == DROPEXCLUDINGDEPS || kindOfMessage == DROPTITLE))
message += wxT(" - ") + GetName();
return message;
}
bool pgaStep::IsUpToDate()
{
wxString sql = wxT("SELECT xmin FROM pgagent.pga_jobstep WHERE jstid = ") + NumToStr(GetRecId());
if (!GetConnection() || GetConnection()->ExecuteScalar(sql) != NumToStr(GetXid()))
return false;
else
return true;
}
bool pgaStep::DropObject(wxFrame *frame, ctlTree *browser, bool cascaded)
{
return GetConnection()->ExecuteVoid(wxT("DELETE FROM pgagent.pga_jobstep WHERE jstid=") + NumToStr(GetRecId()));
}
void pgaStep::ShowTreeDetail(ctlTree *browser, frmMain *form, ctlListView *properties, ctlSQLBox *sqlPane)
{
if (!expandedKids)
{
expandedKids = true;
}
if (properties)
{
CreateListColumns(properties);
properties->AppendItem(_("Name"), GetName());
properties->AppendItem(_("ID"), GetRecId());
properties->AppendYesNoItem(_("Enabled"), GetEnabled());
properties->AppendItem(_("Kind"), GetKind());
if (GetConnStr().IsEmpty())
properties->AppendItem(_("Database"), GetDbname());
else
properties->AppendItem(_("Connection String"), GetConnStr());
properties->AppendItem(_("Code"), GetCode());
properties->AppendItem(_("On error"), GetOnError());
properties->AppendItem(_("Comment"), firstLineOnly(GetComment()));
}
}
pgObject *pgaStep::Refresh(ctlTree *browser, const wxTreeItemId item)
{
pgObject *step = 0;
pgCollection *coll = browser->GetParentCollection(item);
if (coll)
step = stepFactory.CreateObjects(coll, 0, wxT("\n AND jstid=") + NumToStr(GetRecId()));
return step;
}
pgObject *pgaStepFactory::CreateObjects(pgCollection *collection, ctlTree *browser, const wxString &restriction)
{
pgaStep *step = 0;
pgSet *steps = collection->GetConnection()->ExecuteSet(
wxT("SELECT xmin, * FROM pgagent.pga_jobstep\n")
wxT(" WHERE jstjobid=") + NumToStr(collection->GetJob()->GetRecId()) + wxT("\n")
+ restriction +
wxT(" ORDER BY jstname"));
if (steps)
{
while (!steps->Eof())
{
step = new pgaStep(collection, steps->GetVal(wxT("jstname")));
step->iSetRecId(steps->GetLong(wxT("jstid")));
step->iSetXid(steps->GetOid(wxT("xmin")));
step->iSetDbname(steps->GetVal(wxT("jstdbname")));
if (steps->HasColumn(wxT("jstconnstr")))
step->iSetConnStr(steps->GetVal(wxT("jstconnstr")));
step->iSetCode(steps->GetVal(wxT("jstcode")));
step->iSetEnabled(steps->GetBool(wxT("jstenabled")));
wxChar kindc = *steps->GetVal(wxT("jstkind")).c_str();
wxString kinds;
switch (kindc)
{
case 'b':
kinds = _("Batch");
break;
case 's':
kinds = wxT("SQL");
break;
}
step->iSetKindChar(kindc);
step->iSetKind(kinds);
wxChar onerrc = *steps->GetVal(wxT("jstonerror")).c_str();
wxString onerrs;
switch (onerrc)
{
case 's':
onerrs = _("Succeed");
break;
case 'f':
onerrs = _("Fail");
break;
case 'i':
onerrs = _("Ignore");
break;
}
step->iSetOnErrorChar(onerrc);
step->iSetOnError(onerrs);
step->iSetComment(steps->GetVal(wxT("jstdesc")));
if (browser)
{
browser->AppendObject(collection, step);
steps->MoveNext();
}
else
break;
}
delete steps;
}
return step;
}
void pgaStep::ShowStatistics(frmMain *form, ctlListView *statistics)
{
wxString sql =
wxT("SELECT jsljlgid")
wxT(", jslstatus")
wxT(", jslresult")
wxT(", jslstart")
wxT(", jslduration")
wxT(", (jslstart + jslduration) AS endtime")
wxT(", jsloutput")
wxT(" FROM pgagent.pga_jobsteplog\n")
wxT(" WHERE jsljstid = ") + NumToStr(GetRecId()) +
wxT(" ORDER BY jslstart DESC")
wxT(" LIMIT ") + NumToStr(settings->GetMaxRows());
if (statistics)
{
wxLogInfo(wxT("Displaying statistics for job %s"), GetFullIdentifier().c_str());
// Add the statistics view columns
statistics->ClearAll();
statistics->AddColumn(_("Run"), 50);
statistics->AddColumn(_("Status"), 60);
statistics->AddColumn(_("Result"), 60);
statistics->AddColumn(_("Start time"), 95);
statistics->AddColumn(_("End time"), 95);
statistics->AddColumn(_("Duration"), 70);
statistics->AddColumn(_("Output"), 200);
pgSet *stats = GetConnection()->ExecuteSet(sql);
wxString status;
wxDateTime startTime;
wxDateTime endTime;
if (stats)
{
while (!stats->Eof())
{
if (stats->GetVal(1) == wxT("r"))
status = _("Running");
else if (stats->GetVal(1) == wxT("s"))
status = _("Successful");
else if (stats->GetVal(1) == wxT("f"))
status = _("Failed");
else if (stats->GetVal(1) == wxT("i"))
status = _("Ignored");
else if (stats->GetVal(1) == wxT("d"))
status = _("Aborted");
else
status = _("Unknown");
startTime.ParseDateTime(stats->GetVal(3));
endTime.ParseDateTime(stats->GetVal(5));
long pos = statistics->AppendItem(stats->GetVal(0), status, stats->GetVal(2));
statistics->SetItem(pos, 3, startTime.Format());
if (stats->GetVal(5).Length() > 0)
statistics->SetItem(pos, 4, endTime.Format());
statistics->SetItem(pos, 5, stats->GetVal(4));
statistics->SetItem(pos, 6, stats->GetVal(6));
stats->MoveNext();
}
delete stats;
}
}
}
/////////////////////////////
pgaStepCollection::pgaStepCollection(pgaFactory *factory, pgaJob *job)
: pgaJobObjCollection(factory, job)
{
}
wxString pgaStepCollection::GetTranslatedMessage(int kindOfMessage) const
{
wxString message = wxEmptyString;
switch (kindOfMessage)
{
case RETRIEVINGDETAILS:
message = _("Retrieving details on pgAgent steps");
break;
case REFRESHINGDETAILS:
message = _("Refreshing pgAgent steps");
break;
case OBJECTSLISTREPORT:
message = _("pgAgent steps list report");
break;
}
return message;
}
/////////////////////////////
#include "images/step.pngc"
#include "images/steps.pngc"
pgaStepFactory::pgaStepFactory()
: pgaJobObjFactory(__("Step"), __("New Step"), __("Create a new Step."), step_png_img)
{
metaType = PGM_STEP;
}
pgCollection *pgaStepFactory::CreateCollection(pgObject *obj)
{
return new pgaStepCollection(GetCollectionFactory(), (pgaJob *)obj);
}
pgaStepFactory stepFactory;
static pgaCollectionFactory cf(&stepFactory, __("Steps"), steps_png_img);

519
ctl/calbox.cpp Normal file
View file

@ -0,0 +1,519 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// calbox.cpp - Date-picker control box
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
#include <wx/wx.h>
#include "ctl/calbox.h"
#if !defined(wxUSE_DATEPICKCTRL) || !wxUSE_DATEPICKCTRL
#if defined(__WXMSW__)
#define TXTCTRL_FLAGS wxNO_BORDER
#define CALBORDER 0
#define TXTPOSX 0
#define TXTPOSY 1
#elif defined(__WXGTK__)
#define TXTCTRL_FLAGS 0
#define CALBORDER 4
#define TXTPOSX 0
#define TXTPOSY 0
#else
#define TXTCTRL_FLAGS 0
#define CALBORDER 4
#define TXTPOSX 0
#define TXTPOSY 0
#endif
#define CTRLID_TXT 101
#define CTRLID_CAL 102
#define CTRLID_BTN 103
#define CTRLID_PAN 104
BEGIN_EVENT_TABLE(wxCalendarBox, wxControl)
EVT_BUTTON(CTRLID_BTN, wxCalendarBox::OnClick)
EVT_TEXT(CTRLID_TXT, wxCalendarBox::OnText)
EVT_CHILD_FOCUS(wxCalendarBox::OnChildSetFocus)
EVT_SIZE(wxCalendarBox::OnSize)
END_EVENT_TABLE()
IMPLEMENT_DYNAMIC_CLASS(wxCalendarBox, wxControl)
wxCalendarBox::wxCalendarBox(wxWindow *parent,
wxWindowID id,
const wxDateTime &date,
const wxPoint &pos,
const wxSize &size,
long style,
const wxString &name)
{
Init();
Create(parent, id, date, pos, size, style, name);
}
bool wxCalendarBox::Create(wxWindow *parent,
wxWindowID id,
const wxDateTime &date,
const wxPoint &pos,
const wxSize &size,
long style,
const wxString &name)
{
wxString txt;
if (date.IsValid())
txt = date.FormatISODate();
if ( !wxControl::Create(parent, id, pos, size,
style | wxCLIP_CHILDREN | wxWANTS_CHARS,
wxDefaultValidator, name) )
{
return false;
}
InheritAttributes();
wxSize cs = GetClientSize();
wxSize bs = ConvertDialogToPixels(wxSize(10, 0));
wxBitmap bmp(8, 4);
{
wxMemoryDC dc;
dc.SelectObject(bmp);
dc.SetBrush(wxBrush(GetBackgroundColour()));
dc.SetPen(wxPen(GetBackgroundColour()));
dc.DrawRectangle(0, 0, 8, 4);
dc.SetBrush(wxBrush(GetForegroundColour()));
dc.SetPen(wxPen(GetForegroundColour()));
wxPoint pt[3] = { wxPoint(0, 0), wxPoint(6, 0), wxPoint(3, 3) };
dc.DrawPolygon(3, pt);
dc.SelectObject(wxNullBitmap);
}
m_txt = new wxTextCtrl(this, CTRLID_TXT, txt, wxDefaultPosition, size, TXTCTRL_FLAGS);
m_txt->Connect(wxID_ANY, wxID_ANY, wxEVT_KEY_DOWN, wxKeyEventHandler(wxCalendarBox::OnEditKey), 0, this);
m_txt->Connect(wxID_ANY, wxID_ANY, wxEVT_KILL_FOCUS, wxFocusEventHandler(wxCalendarBox::OnKillFocus), 0, this);
SetFormat(wxT("%Y-%m-%d"));
m_btn = new wxBitmapButton(this, CTRLID_BTN, bmp, wxDefaultPosition, bs);
m_dlg = new wxDialog(this, CTRLID_CAL, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSIMPLE_BORDER);
m_dlg->SetFont(GetFont());
wxPanel *panel = new wxPanel(m_dlg, CTRLID_PAN, wxPoint(0, 0), wxDefaultSize, wxSUNKEN_BORDER | wxCLIP_CHILDREN);
m_cal = new pgCompatCalendarCtrl(panel, CTRLID_CAL, wxDefaultDateTime, wxPoint(0, 0), wxDefaultSize, wxSUNKEN_BORDER);
m_cal->Connect(CTRLID_CAL, CTRLID_CAL, wxEVT_CALENDAR_SEL_CHANGED, wxCalendarEventHandler(wxCalendarBox::OnSelChange), 0, this);
m_cal->Connect(wxID_ANY, wxID_ANY, wxEVT_KEY_DOWN, wxKeyEventHandler(wxCalendarBox::OnCalKey), 0, this);
m_cal->Connect(CTRLID_CAL, CTRLID_CAL, wxEVT_CALENDAR_DOUBLECLICKED, wxCalendarEventHandler(wxCalendarBox::OnSelChange), 0, this);
m_cal->Connect(CTRLID_CAL, CTRLID_CAL, wxEVT_CALENDAR_DAY_CHANGED, wxCalendarEventHandler(wxCalendarBox::OnSelChange), 0, this);
m_cal->Connect(CTRLID_CAL, CTRLID_CAL, wxEVT_CALENDAR_MONTH_CHANGED, wxCalendarEventHandler(wxCalendarBox::OnSelChange), 0, this);
m_cal->Connect(CTRLID_CAL, CTRLID_CAL, wxEVT_CALENDAR_YEAR_CHANGED, wxCalendarEventHandler(wxCalendarBox::OnSelChange), 0, this);
wxWindow *yearControl = m_cal->GetYearControl();
Connect(wxID_ANY, wxID_ANY, wxEVT_SET_FOCUS, wxFocusEventHandler(wxCalendarBox::OnSetFocus));
wxClientDC dc(yearControl);
dc.SetFont(m_font);
wxCoord width, dummy;
dc.GetTextExtent(wxT("2000"), &width, &dummy);
width += ConvertDialogToPixels(wxSize(20, 0)).x;
wxSize calSize = m_cal->GetBestSize();
wxSize yearSize = yearControl->GetSize();
yearSize.x = width;
wxPoint yearPosition = yearControl->GetPosition();
width = yearPosition.x + yearSize.x + 2 + CALBORDER / 2;
if (width < calSize.x - 4)
width = calSize.x - 4;
int calPos = (width - calSize.x) / 2;
if (calPos == -1)
{
calPos = 0;
width += 2;
}
m_cal->SetSize(calPos, 0, calSize.x, calSize.y);
yearControl->SetSize(width - yearSize.x - CALBORDER / 2, yearPosition.y, yearSize.x, yearSize.y);
m_cal->GetMonthControl()->Move(0, 0);
SetInitialSize(size);
panel->SetClientSize(width + CALBORDER / 2, calSize.y - 2 + CALBORDER);
m_dlg->SetClientSize(panel->GetSize());
return true;
}
void wxCalendarBox::Init()
{
m_dlg = NULL;
m_txt = NULL;
m_cal = NULL;
m_btn = NULL;
m_dropped = false;
m_ignoreDrop = false;
}
bool wxCalendarBox::Destroy()
{
if (m_cal)
m_cal->Destroy();
if (m_dlg)
m_dlg->Destroy();
if (m_txt)
m_txt->Destroy();
if (m_btn)
m_btn->Destroy();
m_dlg = NULL;
m_txt = NULL;
m_cal = NULL;
m_btn = NULL;
return wxControl::Destroy();
}
void wxCalendarBox::DoMoveWindow(int x, int y, int w, int h)
{
wxControl::DoMoveWindow(x, y, w, h);
if (m_dropped)
{
DropDown();
}
}
wxSize wxCalendarBox::DoGetBestSize() const
{
int bh = m_btn->GetBestSize().y;
int eh = m_txt->GetBestSize().y;
return wxSize(100, bh > eh ? bh : eh);
}
void wxCalendarBox::OnSize(wxSizeEvent &event)
{
if ( m_btn )
{
wxSize sz = GetClientSize();
wxSize bs = m_btn->GetSize();
int eh = m_txt->GetBestSize().y;
m_txt->SetSize(TXTPOSX, TXTPOSY, sz.x - bs.x - TXTPOSX, sz.y > eh ? eh - TXTPOSY : sz.y - TXTPOSY);
m_btn->SetSize(sz.x - bs.x, 0, bs.x, sz.y);
}
event.Skip();
}
bool wxCalendarBox::Show(bool show)
{
if ( !wxControl::Show(show) )
{
return false;
}
if (!show)
{
if (m_dlg)
{
m_dlg->Hide();
m_dropped = false;
}
}
return true;
}
bool wxCalendarBox::Enable(bool enable)
{
if ( !wxControl::Enable(enable) )
{
return false;
}
if (m_cal)
{
if (enable)
m_cal->Show();
else
m_cal->Hide();
}
if (m_btn)
m_btn->Enable(enable);
return true;
}
bool wxCalendarBox::SetFormat(const wxChar *fmt)
{
wxDateTime dt;
dt.ParseFormat(wxT("2003-10-13"), wxT("%Y-%m-%d"));
wxString str = dt.Format(fmt);
const wxChar *p = (const wxChar *) str;
m_format = wxEmptyString;
while (*p)
{
int n = wxAtoi(p);
if (n == dt.GetDay())
{
m_format.Append(wxT("%d"));
p += 2;
}
else if (n == (int)dt.GetMonth() + 1)
{
m_format.Append(wxT("%m"));
p += 2;
}
else if (n == dt.GetYear())
{
m_format.Append(wxT("%Y"));
p += 4;
}
else
m_format.Append(*p++);
}
if (m_txt)
{
wxArrayString valArray;
wxChar c;
for (c = '0'; c <= '9'; c++)
valArray.Add(wxString(c, 1));
const wxChar *p = (const wxChar *) m_format;
while (*p)
{
if (*p == '%')
p += 2;
else
valArray.Add(wxString(*p++, 1));
}
wxTextValidator tv(wxFILTER_INCLUDE_CHAR_LIST);
tv.SetIncludes(valArray);
m_txt->SetValidator(tv);
}
return true;
}
wxDateTime wxCalendarBox::GetValue()
{
wxDateTime dt;
wxString txt = m_txt->GetValue();
if (!txt.IsEmpty())
dt.ParseFormat(txt, m_format);
return dt;
}
bool wxCalendarBox::SetValue(const wxDateTime &date)
{
bool retval = false;
if (m_cal)
{
if (date.IsValid())
m_txt->SetValue(date.FormatISODate());
else
m_txt->SetValue(wxEmptyString);
}
return retval;
}
void wxCalendarBox::DropDown(bool down)
{
if (m_dlg)
{
if (down)
{
if (m_txt->GetValue().IsEmpty())
m_cal->SetDate(wxDateTime::Today());
else
{
wxDateTime dt;
dt.ParseFormat(m_txt->GetValue(), m_format);
m_cal->SetDate(dt);
}
wxPoint pos = GetParent()->ClientToScreen(GetPosition());
m_dlg->Move(pos.x, pos.y + GetSize().y);
m_dlg->Show();
m_dropped = true;
}
else
{
if (m_dropped)
m_dlg->Hide();
m_dropped = false;
}
}
}
void wxCalendarBox::OnChildSetFocus(wxChildFocusEvent &ev)
{
ev.Skip();
m_ignoreDrop = false;
wxWindow *w = (wxWindow *)ev.GetEventObject();
while (w)
{
if (w == m_dlg)
return;
w = w->GetParent();
}
if (m_dropped)
{
DropDown(false);
if (ev.GetEventObject() == m_btn)
m_ignoreDrop = true;
}
}
void wxCalendarBox::OnClick(wxCommandEvent &event)
{
if (m_ignoreDrop)
{
m_ignoreDrop = false;
m_txt->SetFocus();
}
else
{
DropDown();
m_cal->SetFocus();
}
}
void wxCalendarBox::OnSetFocus(wxFocusEvent &ev)
{
if (m_txt)
{
m_txt->SetFocus();
m_txt->SetSelection(0, 100);
}
}
void wxCalendarBox::OnKillFocus(wxFocusEvent &ev)
{
wxDateTime dt;
dt.ParseFormat(m_txt->GetValue(), m_format);
if (!dt.IsValid())
m_txt->SetValue(wxEmptyString);
else
m_txt->SetValue(dt.Format(m_format));
}
void wxCalendarBox::OnSelChange(wxCalendarEvent &ev)
{
if (m_cal)
{
m_txt->SetValue(m_cal->GetDate().FormatISODate());
if (ev.GetEventType() == wxEVT_CALENDAR_DOUBLECLICKED)
{
DropDown(false);
m_txt->SetFocus();
}
}
ev.SetEventObject(this);
ev.SetId(GetId());
#if wxCHECK_VERSION(2, 9, 0)
GetParent()->GetEventHandler()->ProcessEvent(ev);
#else
GetParent()->ProcessEvent(ev);
#endif
}
void wxCalendarBox::OnText(wxCommandEvent &ev)
{
ev.SetEventObject(this);
ev.SetId(GetId());
#if wxCHECK_VERSION(2, 9, 0)
GetParent()->GetEventHandler()->ProcessEvent(ev);
#else
GetParent()->ProcessEvent(ev);
#endif
// We'll create an additional event if the date is valid.
// If the date isn't valid, the user's probably in the middle of typing
wxString txt = m_txt->GetValue();
wxDateTime dt;
if (!txt.IsEmpty())
dt.ParseFormat(txt, m_format);
#if wxCHECK_VERSION(2, 9, 0)
wxCalendarEvent cev(m_cal, dt, wxEVT_CALENDAR_SEL_CHANGED);
#else
wxCalendarEvent cev(m_cal, wxEVT_CALENDAR_SEL_CHANGED);
cev.SetDate(dt);
#endif
cev.SetEventObject(this);
cev.SetId(GetId());
#if wxCHECK_VERSION(2, 9, 0)
GetParent()->GetEventHandler()->ProcessEvent(cev);
#else
GetParent()->ProcessEvent(cev);
#endif
}
void wxCalendarBox::OnEditKey(wxKeyEvent &ev)
{
if (ev.GetKeyCode() == WXK_DOWN && !ev.HasModifiers())
DropDown();
else
ev.Skip();
}
void wxCalendarBox::OnCalKey(wxKeyEvent &ev)
{
if (ev.GetKeyCode() == WXK_ESCAPE && !ev.HasModifiers())
DropDown(false);
else
ev.Skip();
}
#endif // !defined(wxUSE_DATEPICKCTRL) || !wxUSE_DATEPICKCTRL

41
ctl/ctlAuiNotebook.cpp Normal file
View file

@ -0,0 +1,41 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// ctlAuiNotebook.cpp - Custom AUI Notebook class
//
//////////////////////////////////////////////////////////////////////////
// The primary purpose of this class is to pass child focus events from
// the notebook to the parent window. This is the only way we can grab
// focus events from the page controls.
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
#include <wx/aui/auibook.h>
// App headers
#include <ctl/ctlAuiNotebook.h>
BEGIN_EVENT_TABLE(ctlAuiNotebook, wxAuiNotebook)
EVT_CHILD_FOCUS(ctlAuiNotebook::OnChildFocus)
END_EVENT_TABLE()
// Handle, and pass up child focus events
void ctlAuiNotebook::OnChildFocus(wxChildFocusEvent &event)
{
#if wxCHECK_VERSION(2, 9, 0)
wxAuiNotebook::OnChildFocusNotebook(event);
GetParent()->GetEventHandler()->AddPendingEvent(event);
#else
wxAuiNotebook::OnChildFocus(event);
GetParent()->AddPendingEvent(event);
#endif
}

90
ctl/ctlCheckTreeView.cpp Normal file
View file

@ -0,0 +1,90 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// ctlCheckTreeView.cpp - TreeView with Checkboxes
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
#include <wx/busyinfo.h>
#include <wx/imaglist.h>
#include <wx/wizard.h>
#include <wx/treectrl.h>
// App headers
#include "ctl/ctlCheckTreeView.h"
#include "images/checked.pngc"
#include "images/disabled.pngc"
#include "images/unchecked.pngc"
BEGIN_EVENT_TABLE(ctlCheckTreeView, wxTreeCtrl)
EVT_LEFT_DOWN( ctlCheckTreeView::OnLeftClick)
END_EVENT_TABLE()
ctlCheckTreeView::ctlCheckTreeView(wxWindow *parent, wxWindowID id, const wxPoint &pos, const wxSize &size, long style)
: wxTreeCtrl(parent, id, pos, size, style)
{
wxImageList *treeimages = new wxImageList(16, 16, true, 3);
treeimages->Add(*unchecked_png_img);
treeimages->Add(*checked_png_img);
treeimages->Add(*disabled_png_img);
SetImageList(treeimages);
}
void ctlCheckTreeView::OnLeftClick(wxMouseEvent &evt)
{
int flags;
wxTreeItemId node = HitTest(evt.GetPosition(), flags);
int newimage = 0;
if ((flags & wxTREE_HITTEST_ONITEMLABEL) || (flags & wxTREE_HITTEST_ONITEMICON))
{
if (GetItemImage(node) == 0)
newimage = 1;
else if (GetItemImage(node) == 1)
newimage = 0;
if (newimage == 0 || newimage == 1)
SetParentAndChildImage(node, newimage);
if (newimage == 1)
SetParentImage(node, newimage);
}
evt.Skip();
}
void ctlCheckTreeView::SetParentAndChildImage(wxTreeItemId node, int newimage)
{
SetItemImage(node, newimage);
wxTreeItemIdValue childData;
wxTreeItemId child = GetFirstChild(node, childData);
while (child.IsOk())
{
SetParentAndChildImage(child, newimage);
child = GetNextChild(node, childData);
}
}
void ctlCheckTreeView::SetParentImage(wxTreeItemId node, int newimage)
{
if (node.IsOk())
{
SetItemImage(node, newimage);
SetParentImage(GetItemParent(node), newimage);
}
}
bool ctlCheckTreeView::IsChecked(const wxTreeItemId &node)
{
return (GetItemImage(node) == 1);
}

121
ctl/ctlColourPicker.cpp Normal file
View file

@ -0,0 +1,121 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the BSD Licence
//
// ctlColourPicker.cpp - Colour Picker with a wxBitmapButton
//
//////////////////////////////////////////////////////////////////////////
// wxWindows headers
#include <wx/wx.h>
#include <wx/colour.h>
#include <wx/colordlg.h>
// App headers
#include "pgAdmin3.h"
#include "ctl/ctlColourPicker.h"
void ctlColourPicker::Create(wxWindow *parent, wxWindowID id, const wxPoint &pos, const wxSize &size)
{
// Set Default Title
m_title = _("Choose the colour");
// Create the wxBitmapButton
((wxBitmapButton *)this)->Create(parent, id, wxNullBitmap, pos, size);
// Set the handler for a click
Connect(id, wxEVT_LEFT_DOWN, wxMouseEventHandler(ctlColourPicker::DoProcessLeftClick) );
}
void ctlColourPicker::DoProcessLeftClick(wxMouseEvent &event)
{
wxColourData clrData;
// Initialise custom colours
for (int index = 0; index < 16; index++)
clrData.SetCustomColour(index, settings->GetCustomColour(index));
// If there is an initial colour, set it for wxColourDialog
if (m_colour_clr.IsOk())
clrData.SetColour(m_colour_clr);
// Declare the new dialog
wxColourDialog dialog(this, &clrData);
// and set its title
dialog.SetTitle(m_title);
// Now, show it
if (dialog.ShowModal() == wxID_OK)
{
clrData = dialog.GetColourData();
SetColour(clrData.GetColour());
// Store custom colours
for (int index = 0; index < 16; index++)
settings->SetCustomColour(index, clrData.GetCustomColour(index).GetAsString(wxC2S_HTML_SYNTAX));
}
}
void ctlColourPicker::UpdateColour()
{
if (!m_colour_clr.IsOk())
{
wxLogError(wxT("ohoh"));
wxBitmap empty(1, 1);
SetBitmapLabel(empty);
return;
}
wxSize sz = GetSize();
sz.x -= 2 * GetMarginX();
sz.y -= 2 * GetMarginY();
wxPoint topleft;
if ( sz.x < 1 )
sz.x = 1;
else if ( sz.y < 1 )
sz.y = 1;
wxBitmap bmp(sz.x, sz.y);
wxMemoryDC dc(bmp);
dc.SetBrush(wxBrush(m_colour_clr));
dc.DrawRectangle(topleft, sz);
((wxBitmapButton *)this)->SetBitmapLabel(bmp);
}
wxColour ctlColourPicker::GetColour()
{
return m_colour_clr;
}
wxString ctlColourPicker::GetColourString()
{
if (!m_colour_clr.IsOk())
return wxEmptyString;
return m_colour_clr.GetAsString();
}
void ctlColourPicker::SetColour(const wxColour &colour)
{
m_colour_clr = colour;
UpdateColour();
}
void ctlColourPicker::SetColour(const wxString &colour)
{
m_colour_clr = wxColour(colour);
UpdateColour();
}
void ctlColourPicker::SetTitle(const wxString &title)
{
m_title = title;
}

240
ctl/ctlComboBox.cpp Normal file
View file

@ -0,0 +1,240 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// ctlComboBox.cpp - enhanced combobox control
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// App headers
#include "ctl/ctlComboBox.h"
#include "db/pgConn.h"
#include "db/pgSet.h"
class StringClientData : public wxClientData
{
public:
StringClientData(const wxChar *c)
{
str = c;
}
wxString str;
};
int ctlComboBoxFix::Append(const wxString &item, const wxString &str)
{
return wxComboBox::Append(item, new StringClientData(str));
}
int ctlComboBoxFix::Append(const wxString &item, long l)
{
return wxComboBox::Append(item, (void *)l);
}
int ctlComboBoxFix::Append(const wxString &item, OID oid)
{
return wxComboBox::Append(item, (void *)oid);
}
int ctlComboBoxFix::FillLongKey(pgConn *conn, const wxChar *qry)
{
int cnt = 0;
pgSetIterator set(conn->ExecuteSet(qry));
while (set.RowsLeft())
{
long l = set.GetLong(0);
wxString txt = set.GetVal(1);
Append(txt, l);
cnt++;
}
return cnt;
}
int ctlComboBoxFix::FillOidKey(pgConn *conn, const wxChar *qry)
{
int cnt = 0;
pgSetIterator set(conn->ExecuteSet(qry));
while (set.RowsLeft())
{
OID oid = set.GetOid(0);
wxString txt = set.GetVal(1);
Append(txt, oid);
cnt++;
}
return cnt;
}
int ctlComboBoxFix::FillStringKey(pgConn *conn, const wxChar *qry)
{
int cnt = 0;
pgSetIterator set(conn->ExecuteSet(qry));
while (set.RowsLeft())
{
wxString str = set.GetVal(0);
wxString txt = set.GetVal(1);
Append(txt, str);
cnt++;
}
return cnt;
}
long ctlComboBoxFix::GetLongKey(int sel)
{
if (sel < 0)
sel = GetSelection();
return (long)wxItemContainer::GetClientData(sel);
}
OID ctlComboBoxFix::GetOIDKey(int sel)
{
if (sel < 0)
sel = GetSelection();
return (OID)wxItemContainer::GetClientData(sel);
}
wxString ctlComboBoxFix::GetStringKey(int sel)
{
if (sel < 0)
sel = GetSelection();
StringClientData *scd = (StringClientData *)wxItemContainer::GetClientObject(sel);
if (scd)
return scd->str;
return wxEmptyString;
}
ctlComboBoxFix::ctlComboBoxFix(wxWindow *wnd, int id, wxPoint pos, wxSize siz, long attr)
: wxComboBox(wnd, id, wxEmptyString, pos, siz, 0, NULL, attr)
{
}
bool ctlComboBoxFix::SetKey(long val)
{
unsigned int i;
for (i = 0 ; i < GetCount() ; i++)
{
if (GetLongKey(i) == val)
{
SetSelection(i);
return true;
}
}
SetSelection(wxNOT_FOUND);
return false;
}
bool ctlComboBoxFix::SetKey(OID val)
{
unsigned int i;
for (i = 0 ; i < GetCount() ; i++)
{
if (GetOIDKey(i) == val)
{
SetSelection(i);
return true;
}
}
SetSelection(wxNOT_FOUND);
return false;
}
bool ctlComboBoxFix::SetKey(const wxString &val)
{
unsigned int i;
for (i = 0 ; i < GetCount() ; i++)
{
if (GetStringKey(i) == val)
{
SetSelection(i);
return true;
}
}
SetSelection(wxNOT_FOUND);
return false;
}
////////////////////////////////////////////
ctlComboBox::ctlComboBox(wxWindow *wnd, int id, wxPoint pos, wxSize siz, long attr)
: ctlComboBoxFix(wnd, id, pos, siz, attr)
{
#ifdef __WXGTK__
SetEditable(false);
#endif
}
int ctlComboBox::GuessSelection(wxCommandEvent &ev)
{
if (ev.GetEventType() != wxEVT_COMMAND_TEXT_UPDATED)
return GetGuessedSelection();
wxString str = wxComboBox::GetValue();
if (str.Length())
{
long pos = GetInsertionPoint();
long sel, count = GetCount();
int len = str.Length();
for (sel = 0 ; sel < count ; sel++)
{
if (str == GetString(sel).Left(len))
{
SetSelection(sel);
wxString current = GetString(sel);
SetSelection(pos, current.Length());
return sel;
}
}
}
return -1;
}
int ctlComboBox::GetGuessedSelection() const
{
int sel = GetCurrentSelection();
if (sel < 0)
sel = FindString(GetValue());
return sel;
}
int ctlComboBox::GetSelection() const
{
int sel = 0;
#ifdef __WXMSW__
sel = GetCurrentSelection();
if (sel < 0)
#endif
sel = FindString(GetValue());
return sel;
}
wxString ctlComboBox::GetGuessedStringSelection() const
{
int sel = GetGuessedSelection();
if (sel < 0)
return wxEmptyString;
else
return GetString(sel);
}

View file

@ -0,0 +1,623 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// ctlDefaultSecurityPanel.cpp - Panel with default security information
//
//////////////////////////////////////////////////////////////////////////
// wxWindows headers
#include <wx/wx.h>
#include <wx/settings.h>
#include <wx/imaglist.h>
// App headers
#include "pgAdmin3.h"
#include "ctl/ctlDefaultSecurityPanel.h"
#include "db/pgConn.h"
#include "dlg/dlgProperty.h"
#include "schema/pgGroup.h"
#include "schema/pgUser.h"
#include "utils/sysLogger.h"
#include <wx/arrimpl.cpp>
defaultPrivilegesOn g_defPrivTables('r', wxT("Tables"), wxT("arwdDxt")),
g_defPrivSequences('S', wxT("Sequences"), wxT("rwU")),
g_defPrivFunctions('f', wxT("Functions"), wxT("X")),
g_defPrivTypes('T', wxT("Types"), wxT("U"));
defaultPrivilegesOn::defaultPrivilegesOn(const wxChar privType, const wxString &privOn, const wxString &privileges)
: m_privilegeType(privType), m_privilegesOn(privOn), m_privileges(privileges) {}
ctlDefaultSecurityPanel::ctlDefaultSecurityPanel(pgConn *conn, wxNotebook *nb, wxImageList *imgList)
: wxPanel(nb, -1, wxDefaultPosition, wxDefaultSize), nbNotebook(NULL)
{
nb->AddPage(this, _("Default Privileges"));
wxFlexGridSizer *mainSizer = new wxFlexGridSizer(1, 1, 1, 1);
mainSizer->AddGrowableCol(0);
mainSizer->AddGrowableRow(0);
nbNotebook = new wxNotebook(this, -1, wxDefaultPosition, wxDefaultSize, 0, _("Default ACLs"));
m_defPrivOnTablesPanel = new ctlDefaultPrivilegesPanel(this, nbNotebook, g_defPrivTables, imgList);
m_defPrivOnSeqsPanel = new ctlDefaultPrivilegesPanel(this, nbNotebook, g_defPrivSequences, imgList);
m_defPrivOnFuncsPanel = new ctlDefaultPrivilegesPanel(this, nbNotebook, g_defPrivFunctions, imgList);
if (conn->BackendMinimumVersion(9, 2))
m_defPrivOnTypesPanel = new ctlDefaultPrivilegesPanel(this, nbNotebook, g_defPrivTypes, imgList);
else
m_defPrivOnTypesPanel = NULL;
mainSizer->Add(nbNotebook, 0, wxEXPAND | wxALL, 2);
this->SetSizer(mainSizer);
mainSizer->Fit(this);
}
void ctlDefaultSecurityPanel::UpdatePrivilegePages(bool createDefPrivs, const wxString &defPrivsOnTables,
const wxString &defPrivsOnSeqs, const wxString &defPrivsOnFuncs, const wxString &defPrivsOnTypes)
{
if (!createDefPrivs)
{
nbNotebook->Enable(false);
return;
}
m_defPrivOnTablesPanel->Update(defPrivsOnTables);
m_defPrivOnSeqsPanel->Update(defPrivsOnSeqs);
m_defPrivOnFuncsPanel->Update(defPrivsOnFuncs);
if (m_defPrivOnTypesPanel)
m_defPrivOnTypesPanel->Update(defPrivsOnTypes);
}
wxString ctlDefaultSecurityPanel::GetDefaultPrivileges(const wxString &schemaName)
{
wxString strDefPrivs;
int nPageCount = nbNotebook->GetPageCount();
for (int index = 0; index < nPageCount; index++)
{
strDefPrivs += (dynamic_cast<ctlDefaultPrivilegesPanel *>(nbNotebook->GetPage(index)))->GetDefaultPrivileges(schemaName);
}
return strDefPrivs;
}
///////////////////////////////////////////////////////////////////////////////
// ctlDefaultPrivilegesPanel
///////////////////////////////////////////////////////////////////////////////
BEGIN_EVENT_TABLE(ctlDefaultPrivilegesPanel, wxPanel)
EVT_LIST_ITEM_SELECTED(CTL_DEFLBPRIV, ctlDefaultPrivilegesPanel::OnPrivSelChange)
EVT_BUTTON(CTL_DEFADDPRIV, ctlDefaultPrivilegesPanel::OnAddPriv)
EVT_BUTTON(CTL_DEFDELPRIV, ctlDefaultPrivilegesPanel::OnDelPriv)
EVT_TEXT(CTL_DEFCBGROUP, ctlDefaultPrivilegesPanel::OnGroupChange)
EVT_COMBOBOX(CTL_DEFCBGROUP, ctlDefaultPrivilegesPanel::OnGroupChange)
EVT_CHECKBOX(CTL_DEFALLPRIV, ctlDefaultPrivilegesPanel::OnPrivCheckAll)
EVT_CHECKBOX(CTL_DEFALLPRIVGRANT, ctlDefaultPrivilegesPanel::OnPrivCheckAllGrant)
EVT_CHECKBOX(CTL_DEFPRIVCB, ctlDefaultPrivilegesPanel::OnPrivCheck)
EVT_CHECKBOX(CTL_DEFPRIVCB + 2, ctlDefaultPrivilegesPanel::OnPrivCheck)
EVT_CHECKBOX(CTL_DEFPRIVCB + 4, ctlDefaultPrivilegesPanel::OnPrivCheck)
EVT_CHECKBOX(CTL_DEFPRIVCB + 6, ctlDefaultPrivilegesPanel::OnPrivCheck)
EVT_CHECKBOX(CTL_DEFPRIVCB + 8, ctlDefaultPrivilegesPanel::OnPrivCheck)
EVT_CHECKBOX(CTL_DEFPRIVCB + 10, ctlDefaultPrivilegesPanel::OnPrivCheck)
EVT_CHECKBOX(CTL_DEFPRIVCB + 12, ctlDefaultPrivilegesPanel::OnPrivCheck)
EVT_CHECKBOX(CTL_DEFPRIVCB + 14, ctlDefaultPrivilegesPanel::OnPrivCheck)
EVT_CHECKBOX(CTL_DEFPRIVCB + 16, ctlDefaultPrivilegesPanel::OnPrivCheck)
END_EVENT_TABLE();
DEFINE_LOCAL_EVENT_TYPE(EVT_DEFAULTSECURITYPANEL_CHANGE)
ctlDefaultPrivilegesPanel::ctlDefaultPrivilegesPanel(ctlDefaultSecurityPanel *defSecurityPanel, wxNotebook *nb,
defaultPrivilegesOn &privOn, wxImageList *imgList)
: wxPanel(nb, -1, wxDefaultPosition, wxDefaultSize), m_defPrivChanged(false),
m_privilegeType(privOn), m_defSecurityPanel(defSecurityPanel)
{
nb->AddPage(this, m_privilegeType.m_privilegesOn);
allPrivileges = 0;
privCheckboxes = 0;
m_currentSelectedPriv = NULL;
privilegeCount = m_privilegeType.m_privileges.Length();
wxFlexGridSizer *item0 = new wxFlexGridSizer(3, 1, 5, 5);
item0->AddGrowableCol(0);
item0->AddGrowableRow(0);
privCheckboxes = new wxCheckBox*[privilegeCount * 2];
wxFlexGridSizer *itemSizer1 = new wxFlexGridSizer(1, 1, 5, 5);
itemSizer1->AddGrowableCol(0);
itemSizer1->AddGrowableRow(0);
wxString strGroupLabel = settings->GetShowUsersForPrivileges() ? _("Role/Group") : _("Role");
lbPrivileges = new ctlListView(this, CTL_DEFLBPRIV, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER | wxLC_REPORT);
lbPrivileges->SetImageList(imgList, wxIMAGE_LIST_SMALL);
lbPrivileges->AddColumn(_("Role/Group"), 60, wxLIST_FORMAT_LEFT);
lbPrivileges->AddColumn(_("Privileges"), 60, wxLIST_FORMAT_LEFT);
itemSizer1->Add(lbPrivileges, 0, wxEXPAND | wxALIGN_CENTRE_VERTICAL | wxTOP | wxLEFT | wxRIGHT, 4);
item0->Add(itemSizer1, 0, wxEXPAND | wxALL, 5);
wxBoxSizer *itemSizer2 = new wxBoxSizer(wxHORIZONTAL);
btnAddPriv = new wxButton(this, CTL_DEFADDPRIV, _("Add/Change"));
itemSizer2->Add(btnAddPriv, 0, wxEXPAND | wxALIGN_CENTRE_VERTICAL | wxTOP | wxLEFT | wxRIGHT, 4);
btnDelPriv = new wxButton(this, CTL_DEFDELPRIV, _("Remove"));
itemSizer2->Add(btnDelPriv, 0, wxEXPAND | wxALIGN_CENTRE_VERTICAL | wxTOP | wxLEFT | wxRIGHT, 4);
item0->Add(itemSizer2, 0, wxEXPAND | wxALL, 0);
wxStaticBox *sb = new wxStaticBox(this, -1, _("Privileges"));
wxBoxSizer *itemSizer3 = new wxStaticBoxSizer( sb, wxVERTICAL );
item0->Add(itemSizer3, 0, wxEXPAND | wxALL, 5);
wxBoxSizer *itemSizer4a = new wxBoxSizer(wxHORIZONTAL);
stGroup = new wxStaticText(this, CTL_DEFSTATICGROUP, strGroupLabel);
itemSizer4a->Add(stGroup, 0, wxEXPAND | wxALIGN_CENTRE_VERTICAL | wxTOP | wxLEFT | wxRIGHT, 4);
cbGroups = new ctlComboBox(this, CTL_DEFCBGROUP, wxDefaultPosition, wxDefaultSize);
cbGroups->Append(wxT("public"));
cbGroups->SetSelection(0);
itemSizer4a->Add(cbGroups, wxEXPAND | wxALIGN_CENTRE_VERTICAL | wxTOP | wxLEFT | wxRIGHT);
itemSizer3->Add(itemSizer4a, 0, wxEXPAND | wxALL, 0);
/* border size depends on the plateform */
#ifdef __WXMSW__
int bordersize = 4;
#endif
#ifdef __WXMAC__
int bordersize = 3;
#endif
#ifdef __WXGTK__
int bordersize = 0;
#endif
wxBoxSizer *itemSizer5 = new wxBoxSizer(wxHORIZONTAL);
allPrivileges = new wxCheckBox(this, CTL_DEFALLPRIV, wxT("ALL"));
itemSizer5->Add(allPrivileges, wxEXPAND | wxALIGN_CENTRE_VERTICAL | wxTOP | wxLEFT | wxRIGHT);
allPrivilegesGrant = new wxCheckBox(this, CTL_DEFALLPRIVGRANT, wxT("WITH GRANT OPTION"));
itemSizer5->Add(allPrivilegesGrant, wxEXPAND | wxALIGN_CENTRE_VERTICAL | wxTOP | wxLEFT | wxRIGHT);
allPrivilegesGrant->Disable();
itemSizer3->Add(itemSizer5, 0, wxALL, bordersize);
for (int index = 0; index < privilegeCount; index++)
{
int i = index * 2;
wxChar privilege = m_privilegeType.m_privileges.GetChar(index);
wxString priv = pgObject::GetPrivilegeName(privilege);
wxCheckBox *cb;
wxBoxSizer *itemSizer6 = new wxBoxSizer(wxHORIZONTAL);
cb = new wxCheckBox(this, CTL_DEFPRIVCB + i, priv);
itemSizer6->Add(cb, wxEXPAND | wxALIGN_CENTRE_VERTICAL | wxTOP | wxLEFT | wxRIGHT);
privCheckboxes[i++] = cb;
cb = new wxCheckBox(this, CTL_DEFPRIVCB + i, wxT("WITH GRANT OPTION"));
itemSizer6->Add(cb, wxEXPAND | wxALIGN_CENTRE_VERTICAL | wxTOP | wxLEFT | wxRIGHT);
cb->Disable();
privCheckboxes[i] = cb;
itemSizer3->Add(itemSizer6, 0, wxALL, bordersize);
}
this->SetSizer(item0);
item0->Fit(this);
}
ctlDefaultPrivilegesPanel::~ctlDefaultPrivilegesPanel()
{
cbGroups->Clear();
if (privCheckboxes)
delete[] privCheckboxes;
}
// Find the privileges for the selected user from the map
// and select the check-boxes accordingly
bool ctlDefaultPrivilegesPanel::PrivCheckBoxUpdate(wxString &strRole)
{
int index = 0;
wxString strKey = strRole;
defPrivHash::iterator priv = m_privileges.find(strKey);
bool canGrant = CanGrant();
if (priv == m_privileges.end())
{
m_currentSelectedPriv = NULL;
allPrivileges->SetValue(true);
allPrivilegesGrant->Enable(canGrant);
for (; index < privilegeCount; index++)
{
privCheckboxes[index * 2]->Enable();
privCheckboxes[index * 2]->SetValue(true);
privCheckboxes[index * 2 + 1]->Enable(canGrant);
}
return false;
}
else
{
allPrivileges->SetValue(false);
allPrivilegesGrant->SetValue(false);
allPrivilegesGrant->Enable(false);
}
wxString currentPrivileges = priv->second.m_modified ? priv->second.m_newPriv : priv->second.m_origPriv;
m_currentSelectedPriv = &priv->second;
for (index = 0; index < privilegeCount; index++)
{
int privAt = currentPrivileges.Find(m_privilegeType.m_privileges.GetChar(index));
if (privAt != wxNOT_FOUND)
{
privCheckboxes[index * 2]->SetValue(true);
privCheckboxes[index * 2 + 1]->Enable(canGrant);
if (canGrant && (unsigned int)privAt < currentPrivileges.Length() - 1 && currentPrivileges.GetChar(privAt + 1) == wxT('*'))
privCheckboxes[index * 2 + 1]->SetValue(true);
else
privCheckboxes[index * 2 + 1]->SetValue(false);
}
else
{
privCheckboxes[index * 2]->SetValue(false);
privCheckboxes[index * 2 + 1]->SetValue(false);
privCheckboxes[index * 2 + 1]->Disable();
}
}
return false;
}
bool ctlDefaultPrivilegesPanel::CanGrant()
{
if (cbGroups->GetValue() == wxT("public"))
return false;
return true;
}
void ctlDefaultPrivilegesPanel::OnGroupChange(wxCommandEvent &ev)
{
cbGroups->GuessSelection(ev);
wxString strRole = cbGroups->GetValue();
if (strRole.IsEmpty())
return;
btnAddPriv->Enable(!PrivCheckBoxUpdate(strRole));
}
void ctlDefaultPrivilegesPanel::OnPrivCheckAll(wxCommandEvent &ev)
{
bool all = allPrivileges->GetValue();
allPrivilegesGrant->Enable(all && CanGrant());
for (int i = 0; i < privilegeCount; i++)
{
privCheckboxes[i * 2]->SetValue(all);
CheckGrantOpt(i);
}
}
void ctlDefaultPrivilegesPanel::OnPrivCheckAllGrant(wxCommandEvent &ev)
{
bool grant = allPrivilegesGrant->GetValue();
for (int i = 0 ; i < privilegeCount ; i++)
privCheckboxes[i * 2 + 1]->SetValue(grant);
}
void ctlDefaultPrivilegesPanel::OnPrivCheck(wxCommandEvent &ev)
{
int id = (ev.GetId() - CTL_DEFPRIVCB) / 2;
CheckGrantOpt(id);
btnAddPriv->Enable();
if (!privCheckboxes[id * 2]->GetValue())
allPrivileges->SetValue(false);
}
void ctlDefaultPrivilegesPanel::CheckGrantOpt(int id)
{
bool canGrant = (privCheckboxes[id * 2]->GetValue() && CanGrant());
if (canGrant)
privCheckboxes[id * 2 + 1]->Enable(true);
else
{
privCheckboxes[id * 2 + 1]->SetValue(false);
privCheckboxes[id * 2 + 1]->Disable();
}
btnAddPriv->Enable();
}
void ctlDefaultPrivilegesPanel::OnDelPriv(wxCommandEvent &ev)
{
int pos;
wxString strRole;
if ((pos = lbPrivileges->GetFirstSelected()) == -1)
return;
wxListItem info;
info.m_itemId = pos;
info.m_col = 0;
info.m_mask = wxLIST_MASK_TEXT;
if (lbPrivileges->GetItem(info))
strRole = info.m_text;
wxString strKey = strRole;
defPrivHash::iterator priv = m_privileges.find(strKey);
if (priv != m_privileges.end())
{
priv->second.m_modified = true;
priv->second.m_newPriv = wxT("");
if (m_currentSelectedPriv == &priv->second)
PrivCheckBoxUpdate(strRole);
}
lbPrivileges->DeleteCurrentItem();
m_defPrivChanged = true;
ev.Skip();
}
void ctlDefaultPrivilegesPanel::OnAddPriv(wxCommandEvent &ev)
{
wxString strRole, strPriv;
bool isPresent = (m_currentSelectedPriv != NULL &&
((m_currentSelectedPriv->m_modified &&
!m_currentSelectedPriv->m_newPriv.IsEmpty()) ||
(!m_currentSelectedPriv->m_modified)))
|| lbPrivileges->FindItem(-1, cbGroups->GetGuessedStringSelection()) >= 0;
if (allPrivileges && allPrivileges->GetValue())
{
if (allPrivilegesGrant->GetValue())
{
for (int index = 0; index < privilegeCount; index++)
{
strPriv += m_privilegeType.m_privileges.GetChar(index);
strPriv += wxT('*');
}
}
else
strPriv = m_privilegeType.m_privileges;
}
else
{
for (int index = 0; index < privilegeCount; index++)
{
if (privCheckboxes[index * 2]->GetValue())
{
strPriv += m_privilegeType.m_privileges.GetChar(index);
if(privCheckboxes[index * 2 + 1]->IsEnabled() && privCheckboxes[index * 2 + 1]->GetValue())
strPriv += wxT('*');
}
}
}
if (!m_currentSelectedPriv)
{
strRole = cbGroups->GetGuessedStringSelection();
if (strRole.IsEmpty())
return;
defPrivilege priv;
priv.m_username = strRole;
priv.m_origPriv = wxT("");
priv.m_modified = true;
priv.m_newPriv = strPriv;
wxString strKey = strRole;
m_privileges[strKey] = priv;
defPrivHash::iterator itr = m_privileges.find(strKey);
m_currentSelectedPriv = &itr->second;
}
else
{
if (m_currentSelectedPriv->m_modified ?
m_currentSelectedPriv->m_newPriv == strPriv :
m_currentSelectedPriv->m_origPriv == strPriv)
{
ev.Skip();
return;
}
strRole = m_currentSelectedPriv->m_username;
m_currentSelectedPriv->m_modified = true;
m_currentSelectedPriv->m_newPriv = strPriv;
}
if (!isPresent)
{
int icon;
if (strRole.IsSameAs(wxT("public"), true))
icon = PGICON_PUBLIC;
else if (strRole.StartsWith(wxT("group ")))
icon = groupFactory.GetIconId();
else
icon = userFactory.GetIconId();
long pos = lbPrivileges->GetItemCount();
lbPrivileges->InsertItem(pos, strRole, icon);
lbPrivileges->SetItem(pos, 1, strPriv);
}
else
{
long nCount = lbPrivileges->GetItemCount();
wxListItem info;
for (int index = 0; index < nCount; index++)
{
wxString strTempRole;
info.m_itemId = index;
info.m_col = 0;
info.m_mask = wxLIST_MASK_TEXT;
if (lbPrivileges->GetItem(info))
strTempRole = info.m_text;
if (strTempRole == strRole)
{
lbPrivileges->SetItem(index, 1, strPriv);
break;
}
}
}
m_defPrivChanged = true;
ev.Skip();
}
void ctlDefaultPrivilegesPanel::OnPrivSelChange(wxListEvent &ev)
{
if (!cbGroups)
return;
if (allPrivileges)
{
allPrivileges->SetValue(false);
allPrivilegesGrant->SetValue(false);
allPrivilegesGrant->Disable();
}
long pos = lbPrivileges->GetSelection();
if (pos >= 0)
{
wxString name = lbPrivileges->GetText(pos);
wxString value = lbPrivileges->GetText(pos, 1);
pos = cbGroups->FindString(name);
if (pos < 0)
{
cbGroups->Append(name);
pos = cbGroups->GetCount() - 1;
}
cbGroups->SetSelection(pos);
int i;
for (i = 0 ; i < privilegeCount ; i++)
{
if (privCheckboxes[i * 2]->GetName() != wxT("DISABLE"))
{
privCheckboxes[i * 2]->Enable();
int index = value.Find(m_privilegeType.m_privileges.GetChar(i));
if (index >= 0)
{
privCheckboxes[i * 2]->SetValue(true);
privCheckboxes[i * 2 + 1]->SetValue(value.Mid(index + 1, 1) == wxT("*"));
}
else
privCheckboxes[i * 2]->SetValue(false);
}
CheckGrantOpt(i);
}
PrivCheckBoxUpdate(name);
}
btnAddPriv->Enable();
}
void ctlDefaultPrivilegesPanel::Update(wxString strDefPrivs)
{
unsigned int index = 0;
lbPrivileges->DeleteAllItems();
m_privileges.clear();
cbGroups->Clear();
cbGroups->Append(wxT("public"));
for (; index < m_defSecurityPanel->m_groups.GetCount(); index++)
{
cbGroups->Append(m_defSecurityPanel->m_groups[index]);
}
if (!strDefPrivs.IsEmpty())
{
wxString strRole, strPriv;
strDefPrivs.Replace(wxT("\\\""), wxT("\""), true);
strDefPrivs.Replace(wxT("\\\\"), wxT("\\"), true);
// Removing starting brace '{' and ending brace '}'
strDefPrivs = strDefPrivs.SubString(1, strDefPrivs.Length() - 1);
long pos = 0;
while (pgObject::findUserPrivs(strDefPrivs, strRole, strPriv))
{
int icon;
if (strRole.IsSameAs(wxT("public"), true))
icon = PGICON_PUBLIC;
else if (cbGroups->FindString(strRole) != wxNOT_FOUND)
icon = userFactory.GetIconId();
else if (cbGroups->FindString(wxT("group ") + strRole) != wxNOT_FOUND)
{
icon = groupFactory.GetIconId();
strRole = wxT("group ") + strRole;
}
else
continue;
defPrivilege priv;
priv.m_username = strRole;
priv.m_origPriv = strPriv;
priv.m_modified = false;
priv.m_newPriv = wxT("");
wxString strKey = strRole;
m_privileges[strKey] = priv;
pos = lbPrivileges->GetItemCount();
lbPrivileges->InsertItem(pos, strRole, icon);
lbPrivileges->SetItem(pos, 1, strPriv);
strRole = wxT("");
strPriv = wxT("");
pos++;
}
}
}
wxString ctlDefaultPrivilegesPanel::GetDefaultPrivileges(const wxString &schemaName)
{
if(!m_defPrivChanged)
return wxT("");
wxString strDefPrivs;
defPrivHash::iterator itr = m_privileges.begin();
for (; itr != m_privileges.end(); itr++)
{
if(itr->second.m_modified)
{
wxString strRole = itr->second.m_username;
if (strRole.StartsWith(wxT("group ")))
{
strRole = strRole.Mid(6);
strRole = qtIdent(strRole);
}
else if (strRole != wxT("public"))
strRole = qtIdent(strRole);
wxString strOrigDefPrivs = itr->second.m_origPriv;
wxString strNewDefPrivs = itr->second.m_newPriv;
strDefPrivs += pgObject::GetDefaultPrivileges(m_privilegeType.m_privilegesOn.Upper(), m_privilegeType.m_privileges, schemaName, strOrigDefPrivs, strNewDefPrivs, strRole);
}
}
return strDefPrivs;
}

145
ctl/ctlListView.cpp Normal file
View file

@ -0,0 +1,145 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// ctlListView.cpp - enhanced listview control
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
#include <wx/imaglist.h>
// App headers
#include "ctl/ctlListView.h"
#include "utils/misc.h"
ctlListView::ctlListView(wxWindow *p, int id, wxPoint pos, wxSize siz, long attr)
: wxListView(p, id, pos, siz, attr | wxLC_REPORT)
{
}
long ctlListView::GetSelection()
{
return GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
}
wxString ctlListView::GetText(long row, long col)
{
wxListItem item;
item.SetId(row);
item.SetColumn(col);
item.SetMask(wxLIST_MASK_TEXT);
GetItem(item);
return item.GetText();
};
void ctlListView::AddColumn(const wxString &text, int size, int format)
{
if (size == wxLIST_AUTOSIZE || size == wxLIST_AUTOSIZE_USEHEADER)
{
InsertColumn(GetColumnCount(), text, format, size);
}
else
{
InsertColumn(GetColumnCount(), text, format, ConvertDialogToPixels(wxPoint(size, 0)).x);
}
}
long ctlListView::AppendItem(int icon, const wxString &val, const wxString &val2, const wxString &val3, const wxString &val4)
{
long pos = InsertItem(GetItemCount(), val, icon);
if (!val2.IsEmpty())
SetItem(pos, 1, val2);
if (!val3.IsEmpty())
SetItem(pos, 2, val3);
if (!val4.IsEmpty())
SetItem(pos, 3, val4);
return pos;
}
void ctlListView::CreateColumns(wxImageList *images, const wxString &left, const wxString &right, int leftSize)
{
int rightSize;
if (leftSize < 0)
{
#ifdef __WXMAC__
leftSize = rightSize = (GetParent()->GetSize().GetWidth() - 20) / 2;
#else
leftSize = rightSize = GetSize().GetWidth() / 2;
#endif
}
else
{
if (leftSize)
leftSize = ConvertDialogToPixels(wxPoint(leftSize, 0)).x;
#ifdef __WXMAC__
rightSize = (GetParent()->GetSize().GetWidth() - 20) - leftSize;
#else
rightSize = GetClientSize().GetWidth() - leftSize;
#endif
}
if (!leftSize)
{
InsertColumn(0, left, wxLIST_FORMAT_LEFT, GetClientSize().GetWidth());
}
else
{
InsertColumn(0, left, wxLIST_FORMAT_LEFT, leftSize);
InsertColumn(1, right, wxLIST_FORMAT_LEFT, rightSize);
}
if (images)
SetImageList(images, wxIMAGE_LIST_SMALL);
}
void ctlListView::CreateColumns(wxImageList *images, const wxString &str1, const wxString &str2, const wxString &str3, int leftSize)
{
int rightSize;
if (leftSize < 0)
{
#ifdef __WXMAC__
leftSize = rightSize = (GetParent()->GetSize().GetWidth() - 20) / 2;
#else
leftSize = rightSize = GetSize().GetWidth() / 2;
#endif
}
else
{
if (leftSize)
leftSize = ConvertDialogToPixels(wxPoint(leftSize, 0)).x;
#ifdef __WXMAC__
rightSize = (GetParent()->GetSize().GetWidth() - 20) - leftSize;
#else
rightSize = GetClientSize().GetWidth() - leftSize;
#endif
}
if (!leftSize)
{
InsertColumn(0, str1, wxLIST_FORMAT_LEFT, GetClientSize().GetWidth());
}
else
{
InsertColumn(0, str1, wxLIST_FORMAT_LEFT, leftSize);
InsertColumn(1, str2, wxLIST_FORMAT_LEFT, rightSize / 2);
InsertColumn(2, str3, wxLIST_FORMAT_LEFT, rightSize / 2);
}
if (images)
SetImageList(images, wxIMAGE_LIST_SMALL);
}

193
ctl/ctlMenuToolbar.cpp Normal file
View file

@ -0,0 +1,193 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// ctlMenuToolbar.cpp - Menu tool bar
//
// This code is essentially stolen (with the authors permission) from
// Paul Nelson (http://www.pnelsoncomposer.com/)
//
//////////////////////////////////////////////////////////////////////////
// wxWindows headers
#include <wx/wx.h>
// App headers
#include "pgAdmin3.h"
#include "ctl/ctlMenuToolbar.h"
#include <wx/listimpl.cpp>
WX_DEFINE_LIST(ctlMenuToolList);
/* XPM */
static const char *pulldown_xpm[] =
{
"16 16 2 1",
". c Black",
" c None",
/* pixels */
" ",
" ",
" ",
" ",
" ",
" ",
" ....... ",
" ..... ",
" ... ",
" . ",
" ",
" ",
" ",
" ",
" ",
" "
};
DEFINE_EVENT_TYPE(wxEVT_FILL_MENU)
//////////////////
// ctlMenuButton
//////////////////
void ctlMenuButton::Create(wxWindow *window, wxToolBar *toolBar, int ID, wxMenu *menu)
{
wxBitmap bmpPulldown(pulldown_xpm);
wxSize pulldownSize;
#ifdef __WXMSW__
pulldownSize.Set(12, 16);
#else
#ifdef __WXGTK__
pulldownSize.Set(18, 16);
#else
pulldownSize.Set(10, 16);
#endif
#endif
m_toolBar = toolBar;
m_menu = menu;
((wxBitmapButton *)this)->Create(window, ID, bmpPulldown, wxDefaultPosition, pulldownSize, wxNO_BORDER);
Connect(ID, wxEVT_LEFT_DOWN, wxMouseEventHandler(ctlMenuButton::DoProcessLeftClick) );
}
void ctlMenuButton::DoProcessLeftClick(wxMouseEvent &event)
{
wxPoint menu_pos;
if(m_toolBar)
{
wxSize tool_size = m_toolBar->GetToolSize();
wxSize button_size = GetSize();
// ** Assume that pulldown is to the right of a standard toolbar button,
// so, move the x position back one standard toolbar button's width
menu_pos.x = - tool_size.GetWidth();
menu_pos.y = button_size.GetHeight() / 2 + tool_size.GetHeight() / 2;
#ifdef __WXMAC__
wxSize tbar_size = m_toolBar->GetSize();
wxPoint button_pos = GetPosition();
int iToolSep = m_toolBar->GetToolSeparation();
if(iToolSep == 0) iToolSep = 5;
menu_pos.x += - iToolSep;
menu_pos.y = tbar_size.GetHeight() - button_pos.y / 2;
#endif
}
else
{
wxSize button_size;
button_size = GetSize();
menu_pos.x = 0;
menu_pos.y = button_size.GetHeight();
}
DoPopupMenu(m_menu, menu_pos.x, menu_pos.y);
}
////////////////
// ctlMenuTool
////////////////
ctlMenuTool::ctlMenuTool(wxToolBarToolBase *new_tool, int toolId)
{
m_tool = new_tool;
m_menu = NULL;
m_toolId = toolId;
}
////////////////////
// ctlMenuToolbar
////////////////////
ctlMenuToolbar::ctlMenuToolbar(wxFrame *parent, wxWindowID id, const wxPoint &pos, const wxSize &size, long style, const wxString &name)
: wxToolBar(parent, id, pos, size, style, name)
{
m_frame = parent;
m_menuTools = NULL;
Connect(id, wxEVT_LEFT_DOWN, wxMouseEventHandler(ctlMenuToolbar::DoProcessLeftClick) );
}
ctlMenuToolbar::~ctlMenuToolbar()
{
if(m_menuTools)
delete m_menuTools;
}
ctlMenuButton *ctlMenuToolbar::AddMenuPulldownTool(int toolId, const wxString &label, const wxString &shortHelpString, wxMenu *popupmenu)
{
ctlMenuButton *menu_button = new ctlMenuButton(this, toolId, popupmenu);
AddControl(menu_button);
return menu_button;
}
void ctlMenuToolbar::DoProcessLeftClick(wxMouseEvent &event)
{
ctlMenuToolList::Node *node = NULL;
ctlMenuTool *menu_tool = NULL;
wxToolBarToolBase *clickTool = FindToolForPosition(event.m_x, event.m_y);
if(clickTool == NULL || m_menuTools == NULL)
{
event.Skip();
return;
}
// search for clickTool in the list of menu tools
node = m_menuTools->GetFirst();
for( ; node ; node = node->GetNext())
{
menu_tool = node->GetData();
if(menu_tool->m_tool == clickTool)
break;
}
if(node == NULL)
{
event.Skip();
return;
}
wxSize tbar_size = GetSize();
wxPoint menu_pos;
menu_pos.x = event.m_x - 20;
menu_pos.y = tbar_size.GetHeight() - 2;
PopupMenu(menu_tool->m_menu, menu_pos);
}

View file

@ -0,0 +1,202 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// ctlProgressStatusBar.cpp - A status bar with progress indicator
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
#include <wx/statusbr.h>
#include <wx/gauge.h>
// App headers
#include "ctl/ctlProgressStatusBar.h"
BEGIN_EVENT_TABLE(ctlProgressStatusBar, wxStatusBar)
EVT_SIZE(ctlProgressStatusBar::OnSize)
EVT_TIMER(wxID_ANY, ctlProgressStatusBar::OnTimer)
END_EVENT_TABLE()
const unsigned short ctlProgressStatusBar::ms_increment = 100;
const unsigned short ctlProgressStatusBar::ms_progressbar_width = 70;
const unsigned short ctlProgressStatusBar::ms_progressstatus_width = 130;
ctlProgressStatusBar::ctlProgressStatusBar(wxWindow *parent, bool showProgressInitially, bool autoProgressive, int max)
: wxStatusBar(parent, wxID_ANY), m_progressStopped(!showProgressInitially),
m_autoProgressive(autoProgressive), m_autoValIncrementing(true),
m_hr(0), m_min(0), m_sec(0), m_mil(0), m_val(0)
{
static int widths[] = { -1, ms_progressbar_width, ms_progressstatus_width};
int fields = 0;
if (max <= 0)
max = 100;
if (m_autoProgressive)
fields = 3;
else
fields = 2;
m_progress = new wxGauge(this, -1, max);
wxStatusBar::SetFieldsCount(fields);
wxStatusBar::SetStatusWidths(fields, widths);
m_timer.SetOwner(this->GetEventHandler());
if (!showProgressInitially)
{
m_progressStopped = true;
}
else
{
m_progressStopped = false;
if (m_autoProgressive)
{
m_timer.Start(ms_increment);
m_hr = 0;
m_min = 0;
m_sec = 0;
m_mil = 0;
}
}
// Dummy event to place the progressbar at right place
wxSizeEvent ev;
this->OnSize(ev);
}
ctlProgressStatusBar::~ctlProgressStatusBar()
{
if (m_timer.IsRunning())
{
m_timer.Stop();
}
}
void ctlProgressStatusBar::OnSize(wxSizeEvent &ev)
{
// We should have hide the progress bar
if (GetFieldsCount() <= ProgressBar_field)
return;
wxRect r;
GetFieldRect(ProgressBar_field, r);
m_progress->SetSize(r.x + 1, r.y + 1, r.width - 2, r.height - 2);
ev.Skip();
}
void ctlProgressStatusBar::OnTimer(wxTimerEvent &ev)
{
if (m_progressStopped || !m_autoProgressive)
{
m_timer.Stop();
return;
}
m_val = m_progress->GetValue();
int max = m_progress->GetRange();
if (m_val == max)
{
m_val -= 1;
m_autoValIncrementing = false;
}
else if (m_val == 0)
{
m_val = 1;
m_autoValIncrementing = true;
}
else if (m_autoValIncrementing)
{
m_val += 1;
}
else
{
m_val -= 1;
}
m_progress->SetValue(m_val);
m_mil += ms_increment;
if (m_mil >= 1000)
{
m_mil = m_mil - 1000;
m_sec += 1;
if (m_sec == 60)
{
m_sec = 0;
m_min += 1;
if (m_min == 60)
{
m_min = 0;
m_hr += 1;
}
}
}
wxStatusBar::SetStatusText(wxString::Format(_("%d:%02d:%02d.%03d"), m_hr, m_min, m_sec, m_mil), ProgressStatus_field);
}
void ctlProgressStatusBar::ShowProgress(bool restart)
{
if (m_progressStopped)
{
m_progressStopped = false;
if (!m_timer.IsRunning())
{
if (restart)
{
m_val = 0;
m_hr = m_min = m_sec = m_mil = 0;
}
m_progress->SetValue(m_val);
m_timer.Start(ms_increment);
}
}
}
void ctlProgressStatusBar::StopProgress()
{
if (m_timer.IsRunning())
m_timer.Stop();
m_progress->SetValue(0);
m_progressStopped = true;
}
void ctlProgressStatusBar::SetFieldsCount(int number, const int *widths)
{
m_progress->Show(number > ProgressBar_field);
wxStatusBar::SetFieldsCount( number, widths);
// Dummy Size event (to reposition the progress bar)
wxSizeEvent ev;
this->OnSize(ev);
}
void ctlProgressStatusBar::SetStatusWidths(int n, const int widths_field[])
{
wxStatusBar::SetStatusWidths( n, widths_field);
// Dummy Size event (to reposition the progress bar)
wxSizeEvent ev;
this->OnSize(ev);
}

1044
ctl/ctlSQLBox.cpp Normal file

File diff suppressed because it is too large Load diff

554
ctl/ctlSQLGrid.cpp Normal file
View file

@ -0,0 +1,554 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// ctlSQLGrid.cpp - SQL Query result window
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
#include <wx/clipbrd.h>
#include "db/pgConn.h"
#include "ctl/ctlSQLGrid.h"
#include "utils/sysSettings.h"
#include "frm/frmExport.h"
#define EXTRAEXTENT_HEIGHT 6
#define EXTRAEXTENT_WIDTH 6
BEGIN_EVENT_TABLE(ctlSQLGrid, wxGrid)
EVT_MOUSEWHEEL(ctlSQLGrid::OnMouseWheel)
EVT_GRID_COL_SIZE(ctlSQLGrid::OnGridColSize)
EVT_GRID_LABEL_LEFT_CLICK(ctlSQLGrid::OnLabelClick)
END_EVENT_TABLE()
IMPLEMENT_DYNAMIC_CLASS(ctlSQLGrid, wxGrid)
ctlSQLGrid::ctlSQLGrid()
{
}
ctlSQLGrid::ctlSQLGrid(wxWindow *parent, wxWindowID id, const wxPoint &pos, const wxSize &size)
: wxGrid(parent, id, pos, size, wxWANTS_CHARS | wxVSCROLL | wxHSCROLL)
{
// Set cells font
wxFont fntCells(settings->GetSQLFont());
SetDefaultCellFont(fntCells);
// Set labels font
wxFont fntLabel(settings->GetSystemFont());
fntLabel.SetWeight(wxBOLD);
SetLabelFont(fntLabel);
SetColLabelAlignment(wxALIGN_LEFT, wxALIGN_CENTER);
SetRowLabelSize(50);
SetDefaultRowSize(fntCells.GetPointSize() * 2 + 2);
SetColLabelSize(fntLabel.GetPointSize() * 4);
SetDefaultCellOverflow(false);
Connect(wxID_ANY, wxEVT_GRID_LABEL_LEFT_DCLICK, wxGridEventHandler(ctlSQLGrid::OnLabelDoubleClick));
}
void ctlSQLGrid::OnGridColSize(wxGridSizeEvent &event)
{
// Save key="index:label", value=size
int col = event.GetRowOrCol();
colSizes[GetColKeyValue(col)] = GetColSize(col);
}
void ctlSQLGrid::OnCopy(wxCommandEvent &ev)
{
Copy();
}
void ctlSQLGrid::OnMouseWheel(wxMouseEvent &event)
{
if (event.ControlDown() || event.CmdDown())
{
wxFont fontlabel = GetLabelFont();
wxFont fontcells = GetDefaultCellFont();
if (event.GetWheelRotation() > 0)
{
fontlabel.SetPointSize(fontlabel.GetPointSize() - 1);
fontcells.SetPointSize(fontcells.GetPointSize() - 1);
}
else
{
fontlabel.SetPointSize(fontlabel.GetPointSize() + 1);
fontcells.SetPointSize(fontcells.GetPointSize() + 1);
}
SetLabelFont(fontlabel);
SetDefaultCellFont(fontcells);
SetColLabelSize(fontlabel.GetPointSize() * 4);
SetDefaultRowSize(fontcells.GetPointSize() * 2);
for (int index = 0; index < GetNumberCols(); index++)
SetColSize(index, -1);
ForceRefresh();
}
else
event.Skip();
}
wxString ctlSQLGrid::GetExportLine(int row)
{
return GetExportLine(row, 0, GetNumberCols() - 1);
}
wxString ctlSQLGrid::GetExportLine(int row, int col1, int col2)
{
wxArrayInt cols;
wxString str;
int i;
if (col2 < col1)
return str;
cols.Alloc(col2 - col1 + 1);
for (i = col1; i <= col2; i++)
{
cols.Add(i);
}
return GetExportLine(row, cols);
}
wxString ctlSQLGrid::GetExportLine(int row, wxArrayInt cols)
{
wxString str;
unsigned int col;
if (GetNumberCols() == 0)
return str;
for (col = 0 ; col < cols.Count() ; col++)
{
if (col > 0)
str.Append(settings->GetCopyColSeparator());
wxString text = GetCellValue(row, cols[col]);
bool needQuote = false;
if (settings->GetCopyQuoting() == 1)
{
needQuote = IsColText(cols[col]);
}
else if (settings->GetCopyQuoting() == 2)
/* Quote everything */
needQuote = true;
if (needQuote)
str.Append(settings->GetCopyQuoteChar());
str.Append(text);
if (needQuote)
str.Append(settings->GetCopyQuoteChar());
}
return str;
}
wxString ctlSQLGrid::GetColumnName(int colNum)
{
wxString columnName = GetColLabelValue(colNum);
columnName = columnName.Left(columnName.find(wxT("\n")));
return columnName;
}
void ctlSQLGrid::AppendColumnHeader(wxString &str, int start, int end)
{
size_t i, arrsize;
arrsize = (end - start + 1);
wxArrayInt columns;
for(i = 0; i < arrsize; i++)
{
columns.Add(start + i);
}
AppendColumnHeader(str, columns);
}
void ctlSQLGrid::AppendColumnHeader(wxString &str, wxArrayInt columns)
{
if(settings->GetColumnNames())
{
bool CopyQuoting = (settings->GetCopyQuoting() == 1 || settings->GetCopyQuoting() == 2);
size_t i;
for(i = 0; i < columns.Count() ; i++)
{
long columnPos = columns.Item(i);
if(i > 0)
str.Append(settings->GetCopyColSeparator());
if(CopyQuoting)
str.Append(settings->GetCopyQuoteChar());
str.Append(GetColumnName(columnPos));
if(CopyQuoting)
str.Append(settings->GetCopyQuoteChar());
}
str.Append(END_OF_LINE);
}
}
int ctlSQLGrid::Copy()
{
wxString str;
int copied = 0;
size_t i;
if (GetSelectedRows().GetCount())
{
AppendColumnHeader(str, 0, (GetNumberCols() - 1));
wxArrayInt rows = GetSelectedRows();
for (i = 0 ; i < rows.GetCount() ; i++)
{
str.Append(GetExportLine(rows.Item(i)));
if (rows.GetCount() > 1)
str.Append(END_OF_LINE);
}
copied = rows.GetCount();
}
else if (GetSelectedCols().GetCount())
{
wxArrayInt cols = GetSelectedCols();
size_t numRows = GetNumberRows();
AppendColumnHeader(str, cols);
for (i = 0 ; i < numRows ; i++)
{
str.Append(GetExportLine(i, cols));
if (numRows > 1)
str.Append(END_OF_LINE);
}
copied = numRows;
}
else if (GetSelectionBlockTopLeft().GetCount() > 0 &&
GetSelectionBlockBottomRight().GetCount() > 0)
{
unsigned int x1, x2, y1, y2;
x1 = GetSelectionBlockTopLeft()[0].GetCol();
x2 = GetSelectionBlockBottomRight()[0].GetCol();
y1 = GetSelectionBlockTopLeft()[0].GetRow();
y2 = GetSelectionBlockBottomRight()[0].GetRow();
AppendColumnHeader(str, x1, x2);
for (i = y1; i <= y2; i++)
{
str.Append(GetExportLine(i, x1, x2));
if (y2 > y1)
str.Append(END_OF_LINE);
}
copied = y2 - y1 + 1;
}
else
{
int row, col;
row = GetGridCursorRow();
col = GetGridCursorCol();
AppendColumnHeader(str, col, col);
str.Append(GetExportLine(row, col, col));
copied = 1;
}
if (copied && wxTheClipboard->Open())
{
wxTheClipboard->SetData(new wxTextDataObject(str));
wxTheClipboard->Close();
}
else
{
copied = 0;
}
return copied;
}
void ctlSQLGrid::OnLabelDoubleClick(wxGridEvent &event)
{
int maxHeight, maxWidth;
GetClientSize(&maxWidth, &maxHeight);
int row = event.GetRow();
int col = event.GetCol();
int extent, extentWant = 0;
if (row >= 0)
{
for (col = 0 ; col < GetNumberCols() ; col++)
{
extent = GetBestSize(row, col).GetHeight();
if (extent > extentWant)
extentWant = extent;
}
extentWant += EXTRAEXTENT_HEIGHT;
extentWant = wxMax(extentWant, GetRowMinimalAcceptableHeight());
extentWant = wxMin(extentWant, maxHeight * 3 / 4);
int currentHeight = GetRowHeight(row);
if (currentHeight >= maxHeight * 3 / 4 || currentHeight == extentWant)
extentWant = GetRowMinimalAcceptableHeight();
else if (currentHeight < maxHeight / 4)
extentWant = wxMin(maxHeight / 4, extentWant);
else if (currentHeight < maxHeight / 2)
extentWant = wxMin(maxHeight / 2, extentWant);
else if (currentHeight < maxHeight * 3 / 4)
extentWant = wxMin(maxHeight * 3 / 4, extentWant);
if (extentWant != currentHeight)
{
BeginBatch();
if(IsCellEditControlShown())
{
HideCellEditControl();
SaveEditControlValue();
}
SetRowHeight(row, extentWant);
EndBatch();
}
}
else if (col >= 0)
{
// Holding Ctrl or Meta switches back to automatic column's sizing
if (event.ControlDown() || event.CmdDown())
{
colSizes.erase(GetColKeyValue(col));
BeginBatch();
if(IsCellEditControlShown())
{
HideCellEditControl();
SaveEditControlValue();
}
AutoSizeColumn(col, false);
EndBatch();
}
else // toggle between some predefined sizes
{
if (col < (int)colMaxSizes.GetCount() && colMaxSizes[col] >= 0)
extentWant = colMaxSizes[col];
else
{
for (row = 0 ; row < GetNumberRows() ; row++)
{
if (CheckRowPresent(row))
{
extent = GetBestSize(row, col).GetWidth();
if (extent > extentWant)
extentWant = extent;
}
}
}
extentWant += EXTRAEXTENT_WIDTH;
extentWant = wxMax(extentWant, GetColMinimalAcceptableWidth());
extentWant = wxMin(extentWant, maxWidth * 3 / 4);
int currentWidth = GetColumnWidth(col);
if (currentWidth >= maxWidth * 3 / 4 || currentWidth == extentWant)
extentWant = GetColMinimalAcceptableWidth();
else if (currentWidth < maxWidth / 4)
extentWant = wxMin(maxWidth / 4, extentWant);
else if (currentWidth < maxWidth / 2)
extentWant = wxMin(maxWidth / 2, extentWant);
else if (currentWidth < maxWidth * 3 / 4)
extentWant = wxMin(maxWidth * 3 / 4, extentWant);
if (extentWant != currentWidth)
{
BeginBatch();
if(IsCellEditControlShown())
{
HideCellEditControl();
SaveEditControlValue();
}
SetColumnWidth(col, extentWant);
EndBatch();
colSizes[GetColKeyValue(col)] = extentWant;
}
}
}
}
void ctlSQLGrid::OnLabelClick(wxGridEvent &event)
{
int row = event.GetRow();
int col = event.GetCol();
// add support for (de)selecting multiple rows and cols with Control pressed
if ( row >= 0 && (event.ControlDown() || event.CmdDown()) )
{
if (GetSelectedRows().Index(row) == wxNOT_FOUND)
SelectRow(row, true);
else
DeselectRow(row);
}
else if ( col >= 0 && (event.ControlDown() || event.CmdDown()) )
{
if (GetSelectedCols().Index(col) == wxNOT_FOUND)
SelectCol(col, true);
else
DeselectCol(col);
}
else
event.Skip();
}
void ctlSQLGrid::AutoSizeColumn(int col, bool setAsMin, bool doLimit)
{
ColKeySizeHashMap::iterator it = colSizes.find(GetColKeyValue(col));
if (it != colSizes.end()) // Restore user-specified size
SetColSize(col, it->second);
else
wxGrid::AutoSizeColumn(col, setAsMin);
if (doLimit)
{
int newSize, oldSize;
int maxSize, totalSize = 0, availSize;
oldSize = GetColSize(col);
availSize = GetClientSize().GetWidth() - GetRowLabelSize();
maxSize = availSize / 2;
for (int i = 0 ; i < GetNumberCols() ; i++)
totalSize += GetColSize(i);
if (oldSize > maxSize && totalSize > availSize)
{
totalSize -= oldSize;
/* Shrink wide column to maxSize.
* If the rest of the columns are short, make sure to use all the remaining space,
* but no more than oldSize (which is enough according to AutoSizeColumns())
*/
newSize = wxMin(oldSize, wxMax(maxSize, availSize - totalSize));
SetColSize(col, newSize);
}
}
}
void ctlSQLGrid::AutoSizeColumns(bool setAsMin)
{
wxCoord newSize, oldSize;
wxCoord maxSize, totalSize = 0, availSize;
int col, nCols = GetNumberCols();
int row, nRows = GetNumberRows();
colMaxSizes.Empty();
/* We need to check each cell's width to choose best. wxGrid::AutoSizeColumns()
* is good, but looping through long result sets gives a noticeable slowdown.
* Thus we'll check every first 500 cells for each column.
*/
// First pass: auto-size columns
for (col = 0 ; col < nCols; col++)
{
ColKeySizeHashMap::iterator it = colSizes.find(GetColKeyValue(col));
if (it != colSizes.end()) // Restore user-specified size
{
newSize = it->second;
colMaxSizes.Add(-1);
}
else
{
wxClientDC dc(GetGridWindow());
newSize = 0;
// get cells's width
for (row = 0 ; row < wxMin(nRows, 500) ; row++)
{
wxSize size = GetBestSize(row, col);
if ( size.x > newSize )
newSize = size.x;
}
// get column's label width
wxCoord w, h;
dc.SetFont( GetLabelFont() );
dc.GetMultiLineTextExtent( GetColLabelValue(col), &w, &h );
if ( GetColLabelTextOrientation() == wxVERTICAL )
w = h;
if ( w > newSize )
newSize = w;
if (!newSize)
newSize = GetRowLabelSize();
else
// leave some space around text
newSize += 6;
colMaxSizes.Add(newSize);
}
SetColSize(col, newSize);
totalSize += newSize;
}
availSize = GetClientSize().GetWidth() - GetRowLabelSize();
// Second pass: shrink wide columns if exceeded available width
if (totalSize > availSize)
{
// A wide column shouldn't take up more than 50% of the visible space
maxSize = availSize / 2;
for (col = 0 ; col < nCols ; col++)
{
oldSize = GetColSize(col);
// Is too wide and no user-specified size
if (oldSize > maxSize && !(col < (int)colMaxSizes.GetCount() && colMaxSizes[col] == -1))
{
totalSize -= oldSize;
/* Shrink wide column to maxSize.
* If the rest of the columns are short, make sure to use all the remaining space,
* but no more than oldSize (which is enough according to first pass)
*/
newSize = wxMin(oldSize, wxMax(maxSize, availSize - totalSize));
SetColSize(col, newSize);
totalSize += newSize;
}
}
}
}
wxString ctlSQLGrid::GetColKeyValue(int col)
{
wxString colKey = wxString::Format(wxT("%d:"), col) + GetColLabelValue(col);
return colKey;
}
wxSize ctlSQLGrid::GetBestSize(int row, int col)
{
wxSize size;
wxGridCellAttr *attr = GetCellAttr(row, col);
wxGridCellRenderer *renderer = attr->GetRenderer(this, row, col);
if ( renderer )
{
wxClientDC dc(GetGridWindow());
size = renderer->GetBestSize(*this, *attr, dc, row, col);
renderer->DecRef();
}
attr->DecRef();
return size;
}

480
ctl/ctlSQLResult.cpp Normal file
View file

@ -0,0 +1,480 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// ctlSQLResult.cpp - SQL Query result window
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
#include <wx/clipbrd.h>
#include "db/pgConn.h"
#include "db/pgQueryThread.h"
#include "ctl/ctlSQLResult.h"
#include "utils/sysSettings.h"
#include "frm/frmExport.h"
ctlSQLResult::ctlSQLResult(wxWindow *parent, pgConn *_conn, wxWindowID id, const wxPoint &pos, const wxSize &size)
: ctlSQLGrid(parent, id, pos, size)
{
conn = _conn;
thread = NULL;
SetTable(new sqlResultTable(), true);
EnableEditing(false);
SetSizer(new wxBoxSizer(wxVERTICAL));
Connect(wxID_ANY, wxEVT_GRID_RANGE_SELECT, wxGridRangeSelectEventHandler(ctlSQLResult::OnGridSelect));
}
ctlSQLResult::~ctlSQLResult()
{
Abort();
if (thread)
{
if (thread->IsRunning())
{
thread->CancelExecution();
thread->Wait();
}
delete thread;
thread = NULL;
}
}
void ctlSQLResult::SetConnection(pgConn *_conn)
{
conn = _conn;
}
bool ctlSQLResult::Export()
{
if (NumRows() > 0)
{
frmExport dlg(this);
if (dlg.ShowModal() == wxID_OK)
return dlg.Export(NULL);
}
return false;
}
bool ctlSQLResult::ToFile()
{
if (NumRows() > 0)
{
frmExport dlg(this);
if (dlg.ShowModal() == wxID_OK)
return dlg.Export(thread->DataSet());
}
return false;
}
bool ctlSQLResult::ToFile(frmExport *frm)
{
if (NumRows() > 0)
{
return frm->Export(thread->DataSet());
}
return false;
}
bool ctlSQLResult::IsColText(int col)
{
switch (colTypClasses.Item(col))
{
case PGTYPCLASS_NUMERIC:
case PGTYPCLASS_BOOL:
return false;
}
return true;
}
int ctlSQLResult::Execute(const wxString &query, int resultToRetrieve, wxWindow *caller, long eventId, void *data)
{
wxGridTableMessage *msg;
sqlResultTable *table = (sqlResultTable *)GetTable();
msg = new wxGridTableMessage(table, wxGRIDTABLE_NOTIFY_ROWS_DELETED, 0, GetNumberRows());
ProcessTableMessage(*msg);
delete msg;
msg = new wxGridTableMessage(table, wxGRIDTABLE_NOTIFY_COLS_DELETED, 0, GetNumberCols());
ProcessTableMessage(*msg);
delete msg;
Abort();
colNames.Empty();
colTypes.Empty();
colTypClasses.Empty();
thread = new pgQueryThread(conn, query, resultToRetrieve, caller, eventId, data);
if (thread->Create() != wxTHREAD_NO_ERROR)
{
Abort();
return -1;
}
((sqlResultTable *)GetTable())->SetThread(thread);
sqlquerytext= wxString(query);
thread->Run();
return RunStatus();
}
int ctlSQLResult::Abort()
{
if (thread)
{
((sqlResultTable *)GetTable())->SetThread(0);
if (thread->IsRunning())
{
thread->CancelExecution();
thread->Wait();
}
delete thread;
thread = NULL;
}
return 0;
}
void ctlSQLResult::DisplayData(bool single)
{
if (!thread || !thread->DataValid())
return;
if (thread->ReturnCode() != PGRES_TUPLES_OK)
return;
rowcountSuppressed = single;
Freeze();
/*
* Resize and repopulate by informing it to delete all the rows and
* columns, then append the correct number of them. Probably is a
* better way to do this.
*/
wxGridTableMessage *msg;
sqlResultTable *table = (sqlResultTable *)GetTable();
msg = new wxGridTableMessage(table, wxGRIDTABLE_NOTIFY_ROWS_DELETED, 0, GetNumberRows());
ProcessTableMessage(*msg);
delete msg;
msg = new wxGridTableMessage(table, wxGRIDTABLE_NOTIFY_COLS_DELETED, 0, GetNumberCols());
ProcessTableMessage(*msg);
delete msg;
msg = new wxGridTableMessage(table, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, NumRows());
ProcessTableMessage(*msg);
delete msg;
msg = new wxGridTableMessage(table, wxGRIDTABLE_NOTIFY_COLS_APPENDED, thread->DataSet()->NumCols());
ProcessTableMessage(*msg);
delete msg;
if (NumRows()<1000) {
wxGridCellAttr* pAttr = new wxGridCellAttr;
pAttr->SetBackgroundColour(wxColour(224,255,224));
for(int row = 0; row < NumRows(); ++row) {
if (row%2==0) SetRowAttr(row,pAttr);
}
}
if (single)
{
colNames.Add(thread->DataSet()->ColName(0));
colTypes.Add(wxT(""));
colTypClasses.Add(0L);
AutoSizeColumn(0, false, false);
}
else
{
long col, nCols = thread->DataSet()->NumCols();
AutoSizeColumns(false);
for (col = 0 ; col < nCols ; col++)
{
colNames.Add(thread->DataSet()->ColName(col));
colTypes.Add(thread->DataSet()->ColFullType(col));
colTypClasses.Add(thread->DataSet()->ColTypClass(col));
if (thread->DataSet()->ColTypClass(col) == PGTYPCLASS_NUMERIC)
{
/*
* For numeric columns, set alignment to right.
*/
wxGridCellAttr *attr = new wxGridCellAttr();
attr->SetAlignment(wxALIGN_RIGHT, wxALIGN_TOP);
SetColAttr(col, attr);
}
}
}
Thaw();
}
wxString ctlSQLResult::GetMessagesAndClear()
{
if (thread)
return thread->GetMessagesAndClear();
return wxEmptyString;
}
wxString ctlSQLResult::GetErrorMessage()
{
return conn->GetLastError();
}
pgError ctlSQLResult::GetResultError()
{
if (thread)
return thread->GetResultError();
pgError dummy;
memset(&dummy, 0, sizeof(dummy));
return dummy;
}
long ctlSQLResult::NumRows() const
{
if (thread && thread->DataValid())
return thread->DataSet()->NumRows();
return 0;
}
long ctlSQLResult::InsertedCount() const
{
if (thread)
return thread->RowsInserted();
return -1;
}
OID ctlSQLResult::InsertedOid() const
{
if (thread)
return thread->InsertedOid();
return (OID) - 1;
}
int ctlSQLResult::RunStatus()
{
if (!thread)
return -1;
if (thread->IsRunning())
return CTLSQL_RUNNING;
return thread->ReturnCode();
}
wxString ctlSQLResult::OnGetItemText(long item, long col) const
{
if (thread && thread->DataValid())
{
if (!rowcountSuppressed)
{
if (col)
col--;
else
return NumToStr(item + 1L);
}
if (item >= 0)
{
thread->DataSet()->Locate(item + 1);
return thread->DataSet()->GetVal(col);
}
else
return thread->DataSet()->ColName(col);
}
return wxEmptyString;
}
wxString ctlSQLResult::SummaryColumn()
{
//ce=cells.Item(0);
double sum=0;
size_t i;
if (GetSelectedRows().GetCount())
{
//AppendColumnHeader(str, 0, (GetNumberCols() - 1));
wxArrayInt rows = GetSelectedRows();
for (i = 0 ; i < rows.GetCount() ; i++)
{
//str.Append(GetExportLine(rows.Item(i)));
//if (rows.GetCount() > 1)
// str.Append(END_OF_LINE);
}
//copied = rows.GetCount();
}
else if (GetSelectedCols().GetCount())
{
wxArrayInt cols = GetSelectedCols();
size_t numRows = GetNumberRows();
//AppendColumnHeader(str, cols);
for (i = 0 ; i < numRows ; i++)
{
//str.Append(GetExportLine(i, cols));
for (size_t col = 0 ; col < cols.Count() ; col++)
{
wxString text = GetCellValue(i, cols[col]);
sum=sum+wxAtof(text);
}
}
//copied = numRows;
}
else if (GetSelectionBlockTopLeft().GetCount() > 0 &&
GetSelectionBlockBottomRight().GetCount() > 0)
{
unsigned int x1, x2, y1, y2;
x1 = GetSelectionBlockTopLeft()[0].GetCol();
x2 = GetSelectionBlockBottomRight()[0].GetCol();
y1 = GetSelectionBlockTopLeft()[0].GetRow();
y2 = GetSelectionBlockBottomRight()[0].GetRow();
//AppendColumnHeader(str, x1, x2);
for (i = y1; i <= y2; i++)
{
// str.Append(GetExportLine(i, x1, x2));
wxString text = GetCellValue(i, x1);
sum=sum+wxAtof(text);
//copied = y2 - y1 + 1;
}
}
else
{
int row, col;
row = GetGridCursorRow();
col = GetGridCursorCol();
//AppendColumnHeader(str, col, col);
// str.Append(GetExportLine(row, col, col));
//copied = 1;
}
wxString result;
result.Printf(wxT("%f"), sum);
return result;
}
void ctlSQLResult::OnGridSelect(wxGridRangeSelectEvent &event)
{
SetFocus();
}
wxString sqlResultTable::GetValue(int row, int col)
{
if (thread && thread->DataValid())
{
if (col >= 0)
{
thread->DataSet()->Locate(row + 1);
if (settings->GetIndicateNull() && thread->DataSet()->IsNull(col))
return wxT("<NULL>");
else
{
wxString decimalMark = wxT(".");
wxString s = thread->DataSet()->GetVal(col);
if(thread->DataSet()->ColTypClass(col) == PGTYPCLASS_NUMERIC &&
settings->GetDecimalMark().Length() > 0)
{
decimalMark = settings->GetDecimalMark();
s.Replace(wxT("."), decimalMark);
}
if (thread->DataSet()->ColTypClass(col) == PGTYPCLASS_NUMERIC &&
settings->GetThousandsSeparator().Length() > 0)
{
/* Add thousands separator */
size_t pos = s.find(decimalMark);
if (pos == wxString::npos)
pos = s.length();
while (pos > 3)
{
pos -= 3;
if (pos > 1 || !s.StartsWith(wxT("-")))
s.insert(pos, settings->GetThousandsSeparator());
}
return s;
}
else
{
wxString data = thread->DataSet()->GetVal(col);
if (data.Length() > (size_t)settings->GetMaxColSize())
return thread->DataSet()->GetVal(col).Left(settings->GetMaxColSize()) + wxT(" (...)");
else
return thread->DataSet()->GetVal(col);
}
}
}
else
return thread->DataSet()->ColName(col);
}
return wxEmptyString;
}
sqlResultTable::sqlResultTable()
{
thread = NULL;
}
int sqlResultTable::GetNumberRows()
{
if (thread && thread->DataValid())
return thread->DataSet()->NumRows();
return 0;
}
wxString sqlResultTable::GetColLabelValue(int col)
{
if (thread && thread->DataValid())
return thread->DataSet()->ColName(col) + wxT("\n") +
thread->DataSet()->ColFullType(col);
return wxEmptyString;
}
int sqlResultTable::GetNumberCols()
{
if (thread && thread->DataValid())
return thread->DataSet()->NumCols();
return 0;
}

291
ctl/ctlSeclabelPanel.cpp Normal file
View file

@ -0,0 +1,291 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// ctlSeclabelPanel.cpp - Panel with security label information
//
//////////////////////////////////////////////////////////////////////////
// wxWindows headers
#include <wx/wx.h>
#include <wx/settings.h>
#include <wx/imaglist.h>
// App headers
#include "pgAdmin3.h"
#include "utils/misc.h"
#include "utils/sysLogger.h"
#include "ctl/ctlSeclabelPanel.h"
#include "db/pgConn.h"
#include "schema/pgObject.h"
#include "utils/pgDefs.h"
BEGIN_EVENT_TABLE(ctlSeclabelPanel, wxPanel)
EVT_LIST_ITEM_SELECTED(CTL_LBSECLABEL, ctlSeclabelPanel::OnSeclabelSelChange)
EVT_BUTTON(CTL_ADDSECLABEL, ctlSeclabelPanel::OnAddSeclabel)
EVT_BUTTON(CTL_DELSECLABEL, ctlSeclabelPanel::OnDelSeclabel)
EVT_TEXT(CTL_PROVIDER, ctlSeclabelPanel::OnChange)
EVT_TEXT(CTL_SECLABEL, ctlSeclabelPanel::OnChange)
END_EVENT_TABLE();
DEFINE_LOCAL_EVENT_TYPE(EVT_SECLABELPANEL_CHANGE)
ctlSeclabelPanel::ctlSeclabelPanel(wxNotebook *nb)
: wxPanel(nb, -1, wxDefaultPosition, wxDefaultSize)
{
wxStaticText *label;
nbNotebook = nb;
nbNotebook->AddPage(this, _("Security Labels"));
connection = NULL;
// root sizer
wxFlexGridSizer *sizer0 = new wxFlexGridSizer(4, 1, 5, 5);
sizer0->AddGrowableCol(0);
sizer0->AddGrowableRow(0);
// grid sizer
wxFlexGridSizer *sizer1 = new wxFlexGridSizer(1, 1, 5, 5);
sizer1->AddGrowableCol(0);
sizer1->AddGrowableRow(0);
lbSeclabels = new ctlListView(this, CTL_LBSECLABEL, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER | wxLC_REPORT);
lbSeclabels->AddColumn(_("Provider"), 70, wxLIST_FORMAT_LEFT);
lbSeclabels->AddColumn(_("Security label"), 70, wxLIST_FORMAT_LEFT);
sizer1->Add(lbSeclabels, 0, wxEXPAND | wxALIGN_CENTRE_VERTICAL | wxTOP | wxLEFT | wxRIGHT, 4);
sizer0->Add(sizer1, 0, wxEXPAND | wxALL, 5);
// buttons sizer
wxBoxSizer *sizer2 = new wxBoxSizer(wxHORIZONTAL);
btnAddSeclabel = new wxButton(this, CTL_ADDSECLABEL, _("Add/Change"));
sizer2->Add(btnAddSeclabel, 0, wxEXPAND | wxALIGN_CENTRE_VERTICAL | wxTOP | wxLEFT | wxRIGHT, 4);
btnDelSeclabel = new wxButton(this, CTL_DELSECLABEL, _("Remove"));
sizer2->Add(btnDelSeclabel, 0, wxEXPAND | wxALIGN_CENTRE_VERTICAL | wxTOP | wxLEFT | wxRIGHT, 4);
sizer0->Add(sizer2, 0, wxEXPAND | wxALL, 0);
// textboxes sizer
wxFlexGridSizer *sizer3 = new wxFlexGridSizer(2, 2, 5, 5);
sizer3->AddGrowableCol(1);
label = new wxStaticText(this, 0, _("Provider"));
sizer3->Add(label, 0, wxEXPAND | wxALIGN_CENTRE_VERTICAL | wxTOP | wxLEFT | wxRIGHT, 4);
txtProvider = new wxTextCtrl(this, CTL_PROVIDER);
sizer3->Add(txtProvider, 0, wxEXPAND | wxALIGN_CENTRE_VERTICAL | wxTOP | wxLEFT | wxRIGHT, 4);
label = new wxStaticText(this, 0, _("Security label"));
sizer3->Add(label, 0, wxEXPAND | wxALIGN_CENTRE_VERTICAL | wxTOP | wxLEFT | wxRIGHT, 4);
txtSeclabel = new wxTextCtrl(this, CTL_SECLABEL);
sizer3->Add(txtSeclabel, 0, wxEXPAND | wxALIGN_CENTRE_VERTICAL | wxTOP | wxLEFT | wxRIGHT, 4);
sizer0->Add(sizer3, 0, wxEXPAND | wxALL, 5);
// compute sizes
this->SetSizer(sizer0);
sizer0->Fit(this);
btnAddSeclabel->Enable(false);
}
ctlSeclabelPanel::~ctlSeclabelPanel()
{
}
void ctlSeclabelPanel::Disable()
{
lbSeclabels->Disable();
btnAddSeclabel->Disable();
btnDelSeclabel->Disable();
txtProvider->Disable();
txtSeclabel->Disable();
}
void ctlSeclabelPanel::SetConnection(pgConn *conn)
{
connection = conn;
}
void ctlSeclabelPanel::SetObject(pgObject *obj)
{
object = obj;
if (object && !object->GetLabels().IsEmpty())
{
wxArrayString seclabels = object->GetProviderLabelArray();
if (seclabels.GetCount() > 0)
{
for (unsigned int index = 0 ; index < seclabels.GetCount() - 1 ; index += 2)
{
lbSeclabels->AppendItem(seclabels.Item(index),
seclabels.Item(index + 1));
}
}
}
}
void ctlSeclabelPanel::OnDelSeclabel(wxCommandEvent &ev)
{
if (lbSeclabels->GetFirstSelected() == -1)
return;
lbSeclabels->DeleteCurrentItem();
wxCommandEvent event( EVT_SECLABELPANEL_CHANGE, GetId() );
event.SetEventObject( this );
GetEventHandler()->ProcessEvent( event );
ev.Skip();
}
void ctlSeclabelPanel::OnAddSeclabel(wxCommandEvent &ev)
{
bool found = false;
for (int indexList = 0; indexList < lbSeclabels->GetItemCount(); indexList++)
{
if (lbSeclabels->GetText(indexList) == txtProvider->GetValue())
{
found = true;
lbSeclabels->SetItem(indexList, 1, txtSeclabel->GetValue());
break;
}
}
if (!found)
{
int pos = lbSeclabels->GetItemCount();
lbSeclabels->InsertItem(pos, txtProvider->GetValue());
lbSeclabels->SetItem(pos, 1, txtSeclabel->GetValue());
}
wxCommandEvent event( EVT_SECLABELPANEL_CHANGE, GetId() );
event.SetEventObject( this );
GetEventHandler()->ProcessEvent( event );
ev.Skip();
}
void ctlSeclabelPanel::OnChange(wxCommandEvent &event)
{
wxString provider = txtProvider->GetValue().Trim(true).Trim(false);
wxString label = txtSeclabel->GetValue().Trim(true).Trim(false);
btnAddSeclabel->Enable(!provider.IsEmpty() && !label.IsEmpty());
}
void ctlSeclabelPanel::OnSeclabelSelChange(wxListEvent &ev)
{
if (lbSeclabels->GetFirstSelected() == -1)
return;
txtProvider->SetValue(lbSeclabels->GetText(lbSeclabels->GetSelection()));
txtSeclabel->SetValue(lbSeclabels->GetText(lbSeclabels->GetSelection(), 1));
}
void ctlSeclabelPanel::GetCurrentProviderLabelArray(wxArrayString &secLabels)
{
for(int indexList = 0; indexList < lbSeclabels->GetItemCount(); indexList++)
{
secLabels.Add(lbSeclabels->GetText(indexList));
secLabels.Add(lbSeclabels->GetText(indexList, 1));
}
}
wxString ctlSeclabelPanel::GetSqlForSecLabels(wxString objecttype, wxString objectname)
{
wxASSERT(connection != NULL);
wxString sql;
wxArrayString seclabels;
int indexList;
unsigned int indexArray;
wxString oldprovider, newprovider, oldlabel, newlabel;
bool found;
if (object)
{
seclabels = object->GetProviderLabelArray();
// find new or changed seclabels
for (indexList = 0; indexList < lbSeclabels->GetItemCount(); indexList++)
{
found = false;
newprovider = lbSeclabels->GetText(indexList);
newlabel = lbSeclabels->GetText(indexList, 1);
// try to find the provider in the current providers list
if (object && seclabels.GetCount() > 0)
{
for (indexArray = 0 ; indexArray < seclabels.GetCount() - 1 ; indexArray += 2)
{
oldprovider = seclabels.Item(indexArray);
oldlabel = seclabels.Item(indexArray + 1);
if (oldprovider == newprovider)
{
found = true;
// provider is available on the old and new list
// we should check is the label has changed
if (oldlabel != newlabel)
sql += wxT("SECURITY LABEL FOR ") + newprovider
+ wxT("\n ON ") + objecttype + wxT(" ") + objectname
+ wxT("\n IS ") + object->qtDbString(newlabel) + wxT(";\n");
}
}
}
if (!found)
sql += wxT("SECURITY LABEL FOR ") + newprovider
+ wxT("\n ON ") + objecttype + wxT(" ") + objectname
+ wxT("\n IS ") + object->qtDbString(newlabel) + wxT(";\n");
}
// find old seclabels
if (seclabels.GetCount() > 0)
{
for (indexArray = 0 ; indexArray < seclabels.GetCount() - 1 ; indexArray += 2)
{
found = false;
oldprovider = seclabels.Item(indexArray);
for (indexList = 0; indexList < lbSeclabels->GetItemCount(); indexList++)
{
newprovider = lbSeclabels->GetText(indexList);
if (oldprovider == newprovider)
{
found = true;
}
}
if (!found)
sql += wxT("SECURITY LABEL FOR ") + oldprovider
+ wxT("\n ON ") + objecttype + wxT(" ") + objectname
+ wxT("\n IS NULL;\n");
}
}
}
else
{
// find new or changed seclabels
for (indexList = 0; indexList < lbSeclabels->GetItemCount(); indexList++)
{
newprovider = lbSeclabels->GetText(indexList);
newlabel = lbSeclabels->GetText(indexList, 1);
sql += wxT("SECURITY LABEL FOR ") + newprovider
+ wxT("\n ON ") + objecttype + wxT(" ") + objectname
+ wxT("\n IS ") + connection->qtDbString(newlabel) + wxT(";\n");
}
}
return sql;
}

445
ctl/ctlSecurityPanel.cpp Normal file
View file

@ -0,0 +1,445 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// ctlSecurityPanel.cpp - Panel with security information
//
//////////////////////////////////////////////////////////////////////////
// wxWindows headers
#include <wx/wx.h>
#include <wx/settings.h>
#include <wx/imaglist.h>
// App headers
#include "pgAdmin3.h"
#include "utils/sysLogger.h"
#include "ctl/ctlSecurityPanel.h"
#include "db/pgConn.h"
#include "schema/pgObject.h"
#include "schema/pgGroup.h"
#include "schema/pgUser.h"
BEGIN_EVENT_TABLE(ctlSecurityPanel, wxPanel)
EVT_LIST_ITEM_SELECTED(CTL_LBPRIV, ctlSecurityPanel::OnPrivSelChange)
EVT_BUTTON(CTL_ADDPRIV, ctlSecurityPanel::OnAddPriv)
EVT_BUTTON(CTL_DELPRIV, ctlSecurityPanel::OnDelPriv)
EVT_TEXT(CTL_CBGROUP, ctlSecurityPanel::OnGroupChange)
EVT_COMBOBOX(CTL_CBGROUP, ctlSecurityPanel::OnGroupChange)
EVT_CHECKBOX(CTL_ALLPRIV, ctlSecurityPanel::OnPrivCheckAll)
EVT_CHECKBOX(CTL_ALLPRIVGRANT, ctlSecurityPanel::OnPrivCheckAllGrant)
EVT_CHECKBOX(CTL_PRIVCB, ctlSecurityPanel::OnPrivCheck)
EVT_CHECKBOX(CTL_PRIVCB + 2, ctlSecurityPanel::OnPrivCheck)
EVT_CHECKBOX(CTL_PRIVCB + 4, ctlSecurityPanel::OnPrivCheck)
EVT_CHECKBOX(CTL_PRIVCB + 6, ctlSecurityPanel::OnPrivCheck)
EVT_CHECKBOX(CTL_PRIVCB + 8, ctlSecurityPanel::OnPrivCheck)
EVT_CHECKBOX(CTL_PRIVCB + 10, ctlSecurityPanel::OnPrivCheck)
EVT_CHECKBOX(CTL_PRIVCB + 12, ctlSecurityPanel::OnPrivCheck)
EVT_CHECKBOX(CTL_PRIVCB + 14, ctlSecurityPanel::OnPrivCheck)
EVT_CHECKBOX(CTL_PRIVCB + 16, ctlSecurityPanel::OnPrivCheck)
END_EVENT_TABLE();
DEFINE_LOCAL_EVENT_TYPE(EVT_SECURITYPANEL_CHANGE)
ctlSecurityPanel::ctlSecurityPanel(wxNotebook *nb, const wxString &privList, const char *privChars, wxImageList *imgList)
: wxPanel(nb, -1, wxDefaultPosition, wxDefaultSize)
{
nbNotebook = nb;
nbNotebook->AddPage(this, _("Privileges"));
connection = 0;
privilegeChars = privChars;
allPrivileges = 0;
privCheckboxes = 0;
wxStringTokenizer privileges(privList, wxT(","));
privilegeCount = privileges.CountTokens();
wxFlexGridSizer *item0 = new wxFlexGridSizer(3, 1, 5, 5);
item0->AddGrowableCol(0);
item0->AddGrowableRow(0);
if (privilegeCount)
{
bool needAll = (privilegeCount > 1);
privCheckboxes = new wxCheckBox*[privilegeCount * 2];
int i = 0;
wxFlexGridSizer *itemSizer1 = new wxFlexGridSizer(1, 1, 5, 5);
itemSizer1->AddGrowableCol(0);
itemSizer1->AddGrowableRow(0);
lbPrivileges = new ctlListView(this, CTL_LBPRIV, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER | wxLC_REPORT);
lbPrivileges->SetImageList(imgList, wxIMAGE_LIST_SMALL);
lbPrivileges->AddColumn(_("User/Group"), 70, wxLIST_FORMAT_LEFT);
lbPrivileges->AddColumn(_("Privileges"), 70, wxLIST_FORMAT_LEFT);
itemSizer1->Add(lbPrivileges, 0, wxEXPAND | wxALIGN_CENTRE_VERTICAL | wxTOP | wxLEFT | wxRIGHT, 4);
item0->Add(itemSizer1, 0, wxEXPAND | wxALL, 5);
wxBoxSizer *itemSizer2 = new wxBoxSizer(wxHORIZONTAL);
btnAddPriv = new wxButton(this, CTL_ADDPRIV, _("Add/Change"));
itemSizer2->Add(btnAddPriv, 0, wxEXPAND | wxALIGN_CENTRE_VERTICAL | wxTOP | wxLEFT | wxRIGHT, 4);
btnDelPriv = new wxButton(this, CTL_DELPRIV, _("Remove"));
itemSizer2->Add(btnDelPriv, 0, wxEXPAND | wxALIGN_CENTRE_VERTICAL | wxTOP | wxLEFT | wxRIGHT, 4);
item0->Add(itemSizer2, 0, wxEXPAND | wxALL, 0);
wxStaticBox *sb = new wxStaticBox(this, -1, _("Privileges"));
wxBoxSizer *itemSizer3 = new wxStaticBoxSizer( sb, wxVERTICAL );
item0->Add(itemSizer3, 0, wxEXPAND | wxALL, 5);
wxBoxSizer *itemSizer4 = new wxBoxSizer(wxHORIZONTAL);
stGroup = new wxStaticText(this, CTL_STATICGROUP, _("Group"));
#ifdef __WXMSW__
stGroup->SetMinSize(wxSize(30, 15));
#endif // __WXMSW__
itemSizer4->Add(stGroup, 0, wxEXPAND | wxALIGN_CENTRE_VERTICAL | wxTOP | wxLEFT | wxRIGHT, 4);
cbGroups = new ctlComboBox(this, CTL_CBGROUP, wxDefaultPosition, wxDefaultSize);
cbGroups->Append(wxT("public"));
cbGroups->SetSelection(0);
itemSizer4->Add(cbGroups, wxEXPAND | wxALIGN_CENTRE_VERTICAL | wxTOP | wxLEFT | wxRIGHT);
itemSizer3->Add(itemSizer4, 0, wxEXPAND | wxALL, 0);
/* border size depends on the plateform */
#ifdef __WXMSW__
int bordersize = 4;
#endif
#ifdef __WXMAC__
int bordersize = 3;
#endif
#ifdef __WXGTK__
int bordersize = 0;
#endif
if (needAll)
{
wxBoxSizer *itemSizer5 = new wxBoxSizer(wxHORIZONTAL);
allPrivileges = new wxCheckBox(this, CTL_ALLPRIV, wxT("ALL"));
itemSizer5->Add(allPrivileges, wxEXPAND | wxALIGN_CENTRE_VERTICAL | wxTOP | wxLEFT | wxRIGHT);
allPrivilegesGrant = new wxCheckBox(this, CTL_ALLPRIVGRANT, wxT("WITH GRANT OPTION"));
itemSizer5->Add(allPrivilegesGrant, wxEXPAND | wxALIGN_CENTRE_VERTICAL | wxTOP | wxLEFT | wxRIGHT);
allPrivilegesGrant->Disable();
itemSizer3->Add(itemSizer5, 0, wxALL, bordersize);
}
while (privileges.HasMoreTokens())
{
wxString priv = privileges.GetNextToken();
wxCheckBox *cb;
wxBoxSizer *itemSizer6 = new wxBoxSizer(wxHORIZONTAL);
cb = new wxCheckBox(this, CTL_PRIVCB + i, priv);
itemSizer6->Add(cb, wxEXPAND | wxALIGN_CENTRE_VERTICAL | wxTOP | wxLEFT | wxRIGHT);
privCheckboxes[i++] = cb;
cb = new wxCheckBox(this, CTL_PRIVCB + i, wxT("WITH GRANT OPTION"));
itemSizer6->Add(cb, wxEXPAND | wxALIGN_CENTRE_VERTICAL | wxTOP | wxLEFT | wxRIGHT);
cb->Disable();
privCheckboxes[i++] = cb;
itemSizer3->Add(itemSizer6, 0, wxALL, bordersize);
}
}
this->SetSizer(item0);
item0->Fit(this);
}
ctlSecurityPanel::~ctlSecurityPanel()
{
if (privCheckboxes)
delete[] privCheckboxes;
}
void ctlSecurityPanel::SetConnection(pgConn *conn)
{
connection = conn;
if (connection && stGroup && connection->BackendMinimumVersion(8, 1))
stGroup->SetLabel(_("Role"));
}
wxString ctlSecurityPanel::GetUserPrivileges()
{
wxString strPrivilages = wxEmptyString;
int cnt = lbPrivileges->GetItemCount();
int pos;
if(cnt > 0)
{
strPrivilages += wxT("{");
for (pos = 0 ; pos < cnt ; pos++)
{
if (pos != 0)
strPrivilages += wxT(",");
strPrivilages +=
lbPrivileges->GetText(pos) + wxT("=") + lbPrivileges->GetText(pos, 1) + wxT("/") + connection->GetUser();
}
strPrivilages += wxT("}");
}
return strPrivilages;
}
wxString ctlSecurityPanel::GetGrant(const wxString &allPattern, const wxString &grantObject, wxArrayString *currentAcl, wxString column)
{
wxArrayString tmpAcl;
if (currentAcl)
tmpAcl = *currentAcl;
wxString sql;
int cnt = lbPrivileges->GetItemCount();
int pos;
unsigned int i;
for (pos = 0 ; pos < cnt ; pos++)
{
wxString name = lbPrivileges->GetText(pos);
wxString value = lbPrivileges->GetText(pos, 1);
int nameLen = name.Length();
bool privWasAssigned = false;
bool privPartiallyAssigned = false;
for (i = 0 ; i < tmpAcl.GetCount() ; i++)
{
if (tmpAcl.Item(i).Left(nameLen + 1) == name + wxT("="))
{
privPartiallyAssigned = true;
if (tmpAcl.Item(i).Mid(nameLen + 1) == value)
privWasAssigned = true;
tmpAcl.RemoveAt(i);
break;
}
}
if (name.Left(6).IsSameAs(wxT("group "), false))
name = wxT("GROUP ") + qtIdent(name.Mid(6));
else
name = qtIdent(name);
if (!privWasAssigned)
{
if (privPartiallyAssigned)
sql += pgObject::GetPrivileges(allPattern, wxT(""), grantObject, name, column);
sql += pgObject::GetPrivileges(allPattern, value, grantObject, name, column);
}
}
for (i = 0 ; i < tmpAcl.GetCount() ; i++)
{
wxString name = tmpAcl.Item(i).BeforeLast('=');
if (name.Left(6).IsSameAs(wxT("group "), false))
name = wxT("GROUP ") + qtIdent(name.Mid(6));
else
name = qtIdent(name);
sql += pgObject::GetPrivileges(allPattern, wxT(""), grantObject, name, column);
}
return sql;
}
void ctlSecurityPanel::OnGroupChange(wxCommandEvent &ev)
{
cbGroups->GuessSelection(ev);
wxString name = cbGroups->GetGuessedStringSelection();
btnAddPriv->Enable(!name.Strip(wxString::both).IsEmpty());
}
void ctlSecurityPanel::OnPrivCheckAll(wxCommandEvent &ev)
{
bool all = allPrivileges->GetValue();
int i;
for (i = 0 ; i < privilegeCount ; i++)
{
if (all)
{
// We use the Name property of the checkboxes as a flag to note if that
// box should remain disabled (eg. CONNECT for a DB on PG < 8.2
if (privCheckboxes[i * 2]->GetName() != wxT("DISABLE"))
{
privCheckboxes[i * 2]->SetValue(true);
privCheckboxes[i * 2]->Disable();
privCheckboxes[i * 2 + 1]->Disable();
allPrivilegesGrant->Enable(GrantAllowed());
}
}
else
{
if (privCheckboxes[i * 2]->GetName() != wxT("DISABLE"))
{
allPrivilegesGrant->Disable();
allPrivilegesGrant->SetValue(false);
privCheckboxes[i * 2]->Enable();
CheckGrantOpt(i);
}
}
}
}
void ctlSecurityPanel::OnPrivCheckAllGrant(wxCommandEvent &ev)
{
bool grant = allPrivilegesGrant->GetValue();
int i;
for (i = 0 ; i < privilegeCount ; i++)
privCheckboxes[i * 2 + 1]->SetValue(grant);
}
void ctlSecurityPanel::OnPrivCheck(wxCommandEvent &ev)
{
int id = (ev.GetId() - CTL_PRIVCB) / 2;
CheckGrantOpt(id);
}
void ctlSecurityPanel::CheckGrantOpt(int id)
{
bool canGrant = (GrantAllowed() && privCheckboxes[id * 2]->GetValue());
if (canGrant)
privCheckboxes[id * 2 + 1]->Enable();
else
{
privCheckboxes[id * 2 + 1]->SetValue(false);
privCheckboxes[id * 2 + 1]->Disable();
}
}
void ctlSecurityPanel::OnDelPriv(wxCommandEvent &ev)
{
if (lbPrivileges->GetFirstSelected() == -1)
return;
lbPrivileges->DeleteCurrentItem();
wxCommandEvent event( EVT_SECURITYPANEL_CHANGE, GetId() );
event.SetEventObject( this );
GetEventHandler()->ProcessEvent( event );
ev.Skip();
}
void ctlSecurityPanel::OnAddPriv(wxCommandEvent &ev)
{
wxString name = cbGroups->GetGuessedStringSelection();
long pos = lbPrivileges->FindItem(-1, name);
if (pos < 0)
{
pos = lbPrivileges->GetItemCount();
int icon = userFactory.GetIconId();
if (name.Left(6).IsSameAs(wxT("group "), false))
icon = groupFactory.GetIconId();
else if (name.IsSameAs(wxT("public"), false))
icon = PGICON_PUBLIC;
lbPrivileges->InsertItem(pos, name, icon);
}
wxString value;
int i;
for (i = 0 ; i < privilegeCount ; i++)
{
if (privCheckboxes[i * 2]->GetValue())
{
value += privilegeChars[i];
if (privCheckboxes[i * 2 + 1]->GetValue())
value += '*';
}
}
lbPrivileges->SetItem(pos, 1, value);
wxCommandEvent event( EVT_SECURITYPANEL_CHANGE, GetId() );
event.SetEventObject( this );
GetEventHandler()->ProcessEvent( event );
ev.Skip();
}
void ctlSecurityPanel::OnPrivSelChange(wxListEvent &ev)
{
if (!cbGroups)
return;
if (allPrivileges)
{
allPrivileges->SetValue(false);
allPrivilegesGrant->SetValue(false);
allPrivilegesGrant->Disable();
}
long pos = lbPrivileges->GetSelection();
if (pos >= 0)
{
wxString name = lbPrivileges->GetText(pos);
wxString value = lbPrivileges->GetText(pos, 1);
pos = cbGroups->FindString(name);
if (pos < 0)
{
cbGroups->Append(name);
pos = cbGroups->GetCount() - 1;
}
cbGroups->SetSelection(pos);
int i;
for (i = 0 ; i < privilegeCount ; i++)
{
if (privCheckboxes[i * 2]->GetName() != wxT("DISABLE"))
{
privCheckboxes[i * 2]->Enable();
int index = value.Find(privilegeChars[i]);
if (index >= 0)
{
privCheckboxes[i * 2]->SetValue(true);
privCheckboxes[i * 2 + 1]->SetValue(value.Mid(index + 1, 1) == wxT("*"));
}
else
privCheckboxes[i * 2]->SetValue(false);
}
CheckGrantOpt(i);
}
}
btnAddPriv->Enable();
}
bool ctlSecurityPanel::GrantAllowed() const
{
if (!connection->BackendMinimumVersion(7, 4))
return false;
wxString user = cbGroups->GetValue();
if (user.IsSameAs(wxT("public"), false))
return false;
if (!connection->BackendMinimumVersion(8, 1) &&
user.Left(6).IsSameAs(wxT("group "), false))
return false;
return true;
}
bool ctlSecurityPanel::DisablePrivilege(const wxString &priv)
{
for (int i = 0; i < privilegeCount; i++)
{
if (privCheckboxes[i * 2]->GetLabel() == priv)
{
// Use the Name property as a flag so we don't accidently reenable the control later
privCheckboxes[i * 2]->SetName(wxT("DISABLE"));
privCheckboxes[i * 2]->Disable();
return true;
}
}
return false;
}

398
ctl/ctlTree.cpp Normal file
View file

@ -0,0 +1,398 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// ctlTree.cpp - wxTreeCtrl containing pgObjects
//
//////////////////////////////////////////////////////////////////////////
// wxWindows headers
#include <wx/wx.h>
// App headers
#include "pgAdmin3.h"
#include "ctl/ctlTree.h"
#include "schema/pgObject.h"
#include "schema/pgCollection.h"
#include "schema/pgServer.h"
BEGIN_EVENT_TABLE(ctlTree, wxTreeCtrl)
EVT_CHAR(ctlTree::OnChar)
END_EVENT_TABLE()
wxTreeItemId ctlTree::FindItem(const wxTreeItemId &idParent, const wxString &prefixOrig)
{
// match is case insensitive as this is more convenient to the user: having
// to press Shift-letter to go to the item starting with a capital letter
// would be too bothersome
wxString prefix = prefixOrig.Lower();
// determine the starting point: we shouldn't take the current item (this
// allows to switch between two items starting with the same letter just by
// pressing it) but we shouldn't jump to the next one if the user is
// continuing to type as otherwise he might easily skip the item he wanted
wxTreeItemId id = idParent;
if ( prefix.length() == 1 )
{
wxCookieType cookie;
if (HasChildren(id))
id = GetFirstChild(id, cookie);
else
{
// Try a sibling of this or ancestor instead
wxTreeItemId p = id;
wxTreeItemId toFind;
do
{
toFind = GetNextSibling(p);
p = GetItemParent(p);
}
while (p.IsOk() && !toFind.IsOk());
id = toFind;
}
}
// look for the item starting with the given prefix after it
while ( id.IsOk() &&
( ( GetItemText(id) == wxT("Dummy") && !GetItemData(id) ) ||
!GetItemText(id).Lower().StartsWith(prefix) ))
{
wxCookieType cookie;
if ( HasChildren(id) )
id = GetFirstChild(id, cookie);
else
{
// Try a sibling of this or ancestor instead
wxTreeItemId p = id;
wxTreeItemId toFind;
do
{
toFind = GetNextSibling(p);
p = GetItemParent(p);
}
while (p.IsOk() && !toFind.IsOk());
id = toFind;
}
}
// if we haven't found anything...
if ( !id.IsOk() )
{
// ... wrap to the beginning
id = GetRootItem();
if ( HasFlag(wxTR_HIDE_ROOT) )
{
wxCookieType cookie;
// can't select virtual root
if ( HasChildren(id) )
id = GetFirstChild(id, cookie);
else
{
// Try a sibling of this or ancestor instead
wxTreeItemId p = id;
wxTreeItemId toFind;
do
{
toFind = GetNextSibling(p);
p = GetItemParent(p);
}
while (p.IsOk() && !toFind.IsOk());
id = toFind;
}
}
// and try all the items (stop when we get to the one we started from)
while ( id.IsOk() && id != idParent &&
(( GetItemText(id) == wxT("Dummy") && !GetItemData(id) ) ||
!GetItemText(id).Lower().StartsWith(prefix) ))
{
wxCookieType cookie;
if ( HasChildren(id) )
id = GetFirstChild(id, cookie);
else
{
// Try a sibling of this or ancestor instead
wxTreeItemId p = id;
wxTreeItemId toFind;
do
{
toFind = GetNextSibling(p);
p = GetItemParent(p);
}
while (p.IsOk() && !toFind.IsOk());
id = toFind;
}
}
// If we haven't found the item, id.IsOk() will be false, as per
// documentation
}
return id;
}
void ctlTree::OnChar(wxKeyEvent &event)
{
int keyCode = event.GetKeyCode();
if ( !event.HasModifiers() &&
((keyCode >= '0' && keyCode <= '9') ||
(keyCode >= 'a' && keyCode <= 'z') ||
(keyCode >= 'A' && keyCode <= 'Z')))
{
wxTreeItemId currItem = GetSelection();
if (!currItem.IsOk())
return;
wxTreeItemId matchItem = FindItem(currItem, m_findPrefix + (wxChar)keyCode);
if ( matchItem.IsOk() )
{
EnsureVisible(matchItem);
SelectItem(matchItem);
m_findPrefix += (wxChar)keyCode;
// also start the timer to reset the current prefix if the user
// doesn't press any more alnum keys soon -- we wouldn't want
// to use this prefix for a new item search
if ( !m_findTimer )
{
m_findTimer = new ctlTreeFindTimer(this);
}
m_findTimer->Start(ctlTreeFindTimer::CTLTREE_DELAY, wxTIMER_ONE_SHOT);
return;
}
}
else
{
event.Skip(true);
}
}
ctlTree::ctlTree(wxWindow *parent, wxWindowID id, const wxPoint &pos, const wxSize &size, long style)
: wxTreeCtrl(parent, id, pos, size, style), m_findTimer(NULL)
{
}
ctlTree::~ctlTree()
{
if ( m_findTimer )
delete m_findTimer;
m_findTimer = NULL;
}
void ctlTree::RemoveDummyChild(pgObject *obj)
{
wxCookieType cookie;
wxTreeItemId childItem = GetFirstChild(obj->GetId(), cookie);
if (childItem && !GetItemData(childItem))
{
// The child was a dummy item, which will be replaced by the following ShowTreeDetail by true items
Delete(childItem);
}
}
pgObject *ctlTree::GetObject(wxTreeItemId id)
{
if (id)
return (pgObject *)GetItemData(id);
return 0;
}
pgCollection *ctlTree::GetParentCollection(wxTreeItemId id)
{
pgCollection *coll = (pgCollection *)GetParentObject(id);
if (coll && coll->IsCollection())
return coll;
return 0;
}
void ctlTree::SetItemImage(const wxTreeItemId &item, int image, wxTreeItemIcon which)
{
wxTreeCtrl::SetItemImage(item, image, which);
wxTreeItemData *data = GetItemData(item);
// Set the item colour
if (data)
{
if (((pgObject *)data)->GetMetaType() == PGM_SERVER)
{
if (!((pgServer *)data)->GetColour().IsEmpty())
SetItemBackgroundColour(item, wxColour(((pgServer *)data)->GetColour()));
}
else if (((pgObject *)data)->GetServer())
{
if (!((pgObject *)data)->GetServer()->GetColour().IsEmpty())
SetItemBackgroundColour(item, wxColour(((pgObject *)data)->GetServer()->GetColour()));
}
}
}
wxTreeItemId ctlTree::AppendItem(const wxTreeItemId &parent, const wxString &text, int image, int selImage, wxTreeItemData *data)
{
wxTreeItemId itm = wxTreeCtrl::AppendItem(parent, text, image, selImage, data);
// Set the item colour
if (data)
{
if (((pgObject *)data)->GetMetaType() == PGM_SERVER)
{
if (!((pgServer *)data)->GetColour().IsEmpty())
SetItemBackgroundColour(itm, wxColour(((pgServer *)data)->GetColour()));
}
else if (((pgObject *)data)->GetServer())
{
if (!((pgObject *)data)->GetServer()->GetColour().IsEmpty())
SetItemBackgroundColour(itm, wxColour(((pgObject *)data)->GetServer()->GetColour()));
}
}
return itm;
}
wxTreeItemId ctlTree::AppendObject(pgObject *parent, pgObject *object)
{
wxString label;
wxTreeItemId item;
if (object->IsCollection())
label = object->GetTypeName();
else
label = object->GetDisplayName();
item = AppendItem(parent->GetId(), label, object->GetIconId(), -1, object);
if (object->IsCollection())
object->ShowTreeDetail(this);
else if (object->WantDummyChild())
AppendItem(object->GetId(), wxT("Dummy"));
return item;
}
pgCollection *ctlTree::AppendCollection(pgObject *parent, pgaFactory &factory)
{
pgCollection *collection = factory.CreateCollection(parent);
AppendObject(parent, collection);
return collection;
}
pgObject *ctlTree::FindObject(pgaFactory &factory, wxTreeItemId parent)
{
wxCookieType cookie;
wxTreeItemId item = GetFirstChild(parent, cookie);
while (item)
{
pgObject *obj = (pgObject *)GetItemData(item);
if (obj && obj->IsCreatedBy(factory))
return obj;
item = GetNextChild(parent, cookie);
}
return 0;
}
pgCollection *ctlTree::FindCollection(pgaFactory &factory, wxTreeItemId parent)
{
pgaFactory *cf = factory.GetCollectionFactory();
if (!cf)
return 0;
pgCollection *collection = (pgCollection *)FindObject(*cf, parent);
if (!collection || !collection->IsCollection())
return 0;
return collection;
}
void ctlTree::NavigateTree(int keyCode)
{
switch(keyCode)
{
case WXK_LEFT:
{
//If tree item has children and is expanded, collapse it, otherwise select it's parent if has one
wxTreeItemId currItem = GetSelection();
if (ItemHasChildren(currItem) && IsExpanded(currItem))
{
Collapse(currItem);
}
else
{
wxTreeItemId parent = GetItemParent(currItem);
if (parent.IsOk())
{
SelectItem(currItem, false);
SelectItem(parent, true);
}
}
}
break;
case WXK_RIGHT:
{
//If tree item do not have any children ignore it,
//otherwise expand it if not expanded, and select first child if already expanded
wxTreeItemId currItem = GetSelection();
if(ItemHasChildren(currItem))
{
if (!IsExpanded(currItem))
{
Expand(currItem);
}
else
{
wxCookieType cookie;
wxTreeItemId firstChild = GetFirstChild(currItem, cookie);
SelectItem(currItem, false);
SelectItem(firstChild, true);
}
}
}
break;
default:
wxASSERT_MSG(false, _("Currently handles only right and left arrow key, other keys are working"));
break;
}
}
//////////////////////
treeObjectIterator::treeObjectIterator(ctlTree *brow, pgObject *obj)
{
browser = brow;
object = obj;
}
pgObject *treeObjectIterator::GetNextObject()
{
if (!object || !browser)
return 0;
if (!lastItem)
lastItem = browser->GetFirstChild(object->GetId(), cookie);
else
lastItem = browser->GetNextChild(object->GetId(), cookie);
if (lastItem)
return browser->GetObject(lastItem);
else
object = 0;
return 0;
}

651
ctl/explainCanvas.cpp Normal file
View file

@ -0,0 +1,651 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// explainCanvas.cpp - Explain Canvas
//
//////////////////////////////////////////////////////////////////////////
// wxWindows headers
#include <wx/wx.h>
// App headers
#include "pgAdmin3.h"
#include "ctl/explainCanvas.h"
BEGIN_EVENT_TABLE(ExplainCanvas, wxShapeCanvas)
EVT_MOTION(ExplainCanvas::OnMouseMotion)
END_EVENT_TABLE()
ExplainCanvas::ExplainCanvas(wxWindow *parent)
: wxShapeCanvas(parent), rootShape(NULL)
{
SetDiagram(new wxDiagram);
GetDiagram()->SetCanvas(this);
SetBackgroundColour(*wxWHITE);
popup = NULL;
}
ExplainCanvas::~ExplainCanvas()
{
}
void ExplainCanvas::Clear()
{
GetDiagram()->DeleteAllShapes();
rootShape = NULL;
}
void ExplainCanvas::SetExplainString(const wxString &str)
{
// filtred block never executed
wxString rez=wxT("");
wxString flt=wxT("");
wxStringTokenizer tmpstr(str, wxT("\n"));
int p=-1;
while (tmpstr.HasMoreTokens())
{
wxString tmp = tmpstr.GetNextToken();
if (p>-1)
{
// áëîê never executed
const wxChar *cp = tmp.c_str();
int pp=0;
while (pp<=p)
{
if (*cp != ' ')
{
break;
}
cp++;
pp++;
}
if (pp<=p) p=-1;
}
if (p==-1)
{
p=tmp.Find(wxT("->"));
if (p>-1) {
int p2=tmp.Find(wxT("(never executed)"));
if (p2==-1) {
p=-1;
} else
{
continue;
}
}
flt=flt+tmp+wxT("\n");
}
}
Clear();
// We can have multiple plans in a single explain string
// Add a empty root shape, which will never get drawn, but it will help us
// to keep track of all these plans
rootShape = ExplainShape::Create(0, NULL, wxEmptyString);
AddShape(rootShape);
ExplainShape *last = rootShape;
int maxLevel = 0;
wxStringTokenizer lines(flt, wxT("\n"));
while (lines.HasMoreTokens())
{
wxString tmp = lines.GetNextToken();
wxString line = tmp.Strip(wxString::both);
int braceCount = 0;
do
{
const wxChar *cp = line.c_str();
while (*cp)
{
if (*cp == '(')
braceCount++;
else if (*cp == ')')
braceCount--;
cp++;
}
if (braceCount > 0)
{
wxString tmp = lines.GetNextToken();
line += wxT(" ") + tmp.Strip(wxString::both);
braceCount = 0;
}
else
break;
}
while (lines.HasMoreTokens());
long level = ((tmp.Length() - line.Length() + 4) / 6) + 1;
if (last)
{
if (level != 1)
{
if (line.Left(4) == wxT("-> "))
line = line.Mid(4);
else
{
last->SetCondition(line);
continue;
}
}
while (last != rootShape && level <= last->GetLevel())
last = last->GetUpper();
}
ExplainShape *s = ExplainShape::Create(level, last, line);
if (!s)
continue;
s->SetCanvas(this);
InsertShape(s);
s->Show(true);
if (level > maxLevel)
maxLevel = level;
last = s;
}
int x0 = (int)(rootShape->GetWidth() * 3);
int y0 = (int)(rootShape->GetHeight() * 3 / 2);
int xoffs = (int)(rootShape->GetWidth() * 3);
int yoffs = (int)(rootShape->GetHeight() * 5 / 4);
wxNode *current = GetDiagram()->GetShapeList()->GetFirst();
while (current)
{
ExplainShape *s = (ExplainShape *)current->GetData();
if (!s->totalShapes)
s->totalShapes = 1;
if (s->GetUpper())
s->GetUpper()->totalShapes += s->totalShapes;
current = current->GetNext();
}
current = GetDiagram()->GetShapeList()->GetLast();
while (current)
{
ExplainShape *s = (ExplainShape *)current->GetData();
s->SetX(y0 + (maxLevel - s->GetLevel()) * xoffs);
ExplainShape *upper = s->GetUpper();
if (upper)
{
s->SetY(upper->GetY() + upper->usedShapes * yoffs);
upper->usedShapes += s->totalShapes;
// We don't require to draw a line from the root shape to its
// childrens
if (upper != rootShape)
{
wxLineShape *l = new ExplainLine(s, upper);
l->Show(true);
AddShape(l);
}
}
else
{
s->SetY(y0);
}
current = current->GetPrevious();
}
#define PIXPERUNIT 20
int w = (maxLevel * xoffs + x0 * 2 + PIXPERUNIT - 1) / PIXPERUNIT;
int h = (rootShape->totalShapes * yoffs + y0 * 2 + PIXPERUNIT - 1) / PIXPERUNIT;
SetScrollbars(PIXPERUNIT, PIXPERUNIT, w, h);
}
void ExplainCanvas::OnMouseMotion(wxMouseEvent &ev)
{
ev.Skip(true);
if (ev.Dragging())
return;
wxClientDC dc(this);
PrepareDC(dc);
wxPoint logPos(ev.GetLogicalPosition(dc));
double x, y;
x = (double) logPos.x;
y = (double) logPos.y;
// Find the nearest object
int attachment = 0;
ExplainShape *nearestObj = dynamic_cast<ExplainShape *>(FindShape(x, y, &attachment));
if (nearestObj)
{
ShowPopup(nearestObj);
}
}
void ExplainCanvas::ShowPopup(ExplainShape *s)
{
if (popup || s == NULL)
return;
popup = new ExplainPopup(this, s, &popup);
}
void ExplainCanvas::SaveAsImage(const wxString &fileName, wxBitmapType imageType)
{
if (GetDiagram()->GetCount() == 0)
{
wxMessageBox(_("Nothing to be saved!"), _("Save As an image"), wxOK | wxICON_INFORMATION);
return;
}
int width = 0, height = 0;
GetVirtualSize(&width, &height);
/*
* Create the bitmap from the Explain window
*/
wxMemoryDC memDC;
wxBitmap tempBitmap(width, height);
memDC.SelectObject(tempBitmap);
memDC.Clear();
// Draw the diagram on the bitmap (Memory Device Context)
GetDiagram()->Redraw(memDC);
memDC.SelectObject(wxNullBitmap);
if (!tempBitmap.SaveFile(fileName, imageType))
{
wxLogError(_("Could not write file \"%s\": error code %d."), fileName.c_str(), wxSysErrorCode());
}
}
class ExplainText : public wxWindow
{
public:
ExplainText(ExplainPopup *parent, ExplainShape *s);
protected:
void OnMouseMove(wxMouseEvent &ev);
#ifdef wxUSE_POPUPWIN
void OnMouseLost(wxMouseCaptureLostEvent &ev);
#endif
private:
ExplainPopup *popup;
void OnPaint(wxPaintEvent &ev);
wxString m_desc, m_detail, m_condition, m_cost, m_actual;
DECLARE_EVENT_TABLE()
};
BEGIN_EVENT_TABLE(ExplainText, wxWindow)
EVT_PAINT(ExplainText::OnPaint)
EVT_MOTION(ExplainText::OnMouseMove)
#ifdef wxUSE_POPUPWIN
EVT_MOUSE_CAPTURE_LOST(ExplainText::OnMouseLost)
#endif
END_EVENT_TABLE()
ExplainText::ExplainText(ExplainPopup *parent, ExplainShape *s) : wxWindow(parent, -1)
{
SetBackgroundColour(wxColour(255, 255, 224));
popup = parent;
wxWindowDC dc(this);
dc.SetFont(settings->GetSystemFont());
m_desc = s->description;
m_detail = s->detail;
m_condition = s->condition;
m_cost = s->cost;
m_actual = s->actual;
int w1, w2, h;
dc.GetTextExtent(m_desc, &w1, &h);
dc.GetTextExtent(m_detail, &w2, &h);
if (w1 < w2) w1 = w2;
dc.GetTextExtent(m_condition, &w2, &h);
if (w1 < w2) w1 = w2;
dc.GetTextExtent(m_cost, &w2, &h);
if (w1 < w2) w1 = w2;
dc.GetTextExtent(m_actual, &w2, &h);
if (w1 < w2) w1 = w2;
int n = 2;
if (!m_detail.IsEmpty())
n++;
if (!m_condition.IsEmpty())
n++;
if (!m_cost.IsEmpty())
n++;
if (!m_actual.IsEmpty())
n++;
if (!h)
h = GetCharHeight();
SetSize(GetCharHeight() + w1, GetCharHeight() + h * n + h / 3);
}
void ExplainText::OnMouseMove(wxMouseEvent &ev)
{
popup->OnMouseMove(ev);
}
#ifdef wxUSE_POPUPWIN
void ExplainText::OnMouseLost(wxMouseCaptureLostEvent &ev)
{
/*
* We will not do anything here.
* But - in order to resolve a weird bug on window, when using
* wxPopupTransientWindow, related to loosing the mouse control,
* was not taken care properly, we have to introduce this function
* to avoid the crash.
*
* Please refer:
* http://article.gmane.org/gmane.comp.lib.wxwidgets.devel/82376
*/
}
#endif
void ExplainText::OnPaint(wxPaintEvent &ev)
{
wxPaintDC dc(this);
wxFont stdFont = settings->GetSystemFont();
wxFont boldFont = stdFont;
boldFont.SetWeight(wxBOLD);
int x = GetCharHeight() / 2;
int y = GetCharHeight() / 2;
int w, yoffs;
dc.GetTextExtent(wxT("Dummy"), &w, &yoffs);
dc.SetFont(boldFont);
dc.DrawText(m_desc, x, y);
dc.SetFont(stdFont);
if (!m_detail.IsEmpty())
{
y += yoffs;
dc.DrawText(m_detail, x, y);
}
y += yoffs / 3;
if (!m_condition.IsEmpty())
{
y += yoffs;
dc.DrawText(m_condition, x, y);
}
if (!m_cost.IsEmpty())
{
y += yoffs;
dc.DrawText(m_cost, x, y);
}
if (!m_actual.IsEmpty())
{
y += yoffs;
dc.DrawText(m_actual, x, y);
}
#if wxUSE_POPUPWIN
wxPen pen1 = wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE)),
pen2 = wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DDKSHADOW)),
pen3 = wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DHIGHLIGHT)),
pen4 = wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW));
wxRect rect(wxPoint(0, 0), GetSize());
// draw the rectangle
dc.SetPen(pen1);
dc.DrawLine(rect.GetLeft(), rect.GetTop(), rect.GetLeft(), rect.GetBottom());
dc.DrawLine(rect.GetLeft() + 1, rect.GetTop(), rect.GetRight(), rect.GetTop());
dc.SetPen(pen2);
dc.DrawLine(rect.GetRight(), rect.GetTop(), rect.GetRight(), rect.GetBottom());
dc.DrawLine(rect.GetLeft(), rect.GetBottom(), rect.GetRight() + 1, rect.GetBottom());
// adjust the rect
rect.Inflate(-1);
// draw the rectangle
dc.SetPen(pen3);
dc.DrawLine(rect.GetLeft(), rect.GetTop(), rect.GetLeft(), rect.GetBottom());
dc.DrawLine(rect.GetLeft() + 1, rect.GetTop(), rect.GetRight(), rect.GetTop());
dc.SetPen(pen4);
dc.DrawLine(rect.GetRight(), rect.GetTop(), rect.GetRight(), rect.GetBottom());
dc.DrawLine(rect.GetLeft(), rect.GetBottom(), rect.GetRight() + 1, rect.GetBottom());
// adjust the rect
rect.Inflate(-1);
dc.SetPen(pen1);
dc.SetBrush(*wxTRANSPARENT_BRUSH);
dc.DrawRectangle(rect);
#endif
}
BEGIN_EVENT_TABLE(ExplainPopup, pgTipWindowBase)
EVT_MOTION(ExplainPopup::OnMouseMove)
EVT_LEFT_DOWN(ExplainPopup::OnMouseClick)
EVT_RIGHT_DOWN(ExplainPopup::OnMouseClick)
EVT_MIDDLE_DOWN(ExplainPopup::OnMouseClick)
#if wxUSE_POPUPWIN
EVT_MOUSE_CAPTURE_LOST(ExplainPopup::OnMouseLost)
#else
EVT_KILL_FOCUS(ExplainPopup::OnKillFocus)
EVT_ACTIVATE(ExplainPopup::OnActivate)
#endif // !wxUSE_POPUPWIN
END_EVENT_TABLE()
ExplainPopup::ExplainPopup(ExplainCanvas *parent, ExplainShape *shape, ExplainPopup **popup)
#if wxUSE_POPUPWIN
: wxPopupTransientWindow(parent, wxNO_BORDER)
#else
: wxFrame(parent, wxID_ANY, wxEmptyString,
wxDefaultPosition, wxDefaultSize,
wxNO_BORDER | wxFRAME_NO_TASKBAR )
#endif
{
wxASSERT(parent != NULL);
wxASSERT(shape != NULL);
if (popup)
m_ptr = popup;
else
m_ptr = NULL;
m_explainText = new ExplainText(this, shape);
#if !wxUSE_POPUPWIN
m_creationTime = wxGetLocalTime();
#endif
double width = 0.0, height = 0.0;
shape->GetBoundingBoxMin(&width, &height);
if (fabs(width) < 4.0) width = 4.0;
if (fabs(height) < 4.0) height = 4.0;
width += (double)4.0;
height += (double)4.0; // Allowance for inaccurate mousing
int x = (int)(shape->GetX() - (width / 2.0));
int y = (int)(shape->GetY() - (height / 2.0));
int sx, sy;
parent->CalcScrolledPosition(x, y, &sx, &sy);
parent->ClientToScreen(&sx, &sy);
m_rectBound.x = sx;
m_rectBound.y = sy;
m_rectBound.width = WXROUND(width);
m_rectBound.height = WXROUND(height);
wxSize popupSize;
popupSize = m_explainText->GetSize();
popupSize.DecTo(wxGetDisplaySize());
SetSize(popupSize);
if (sy > GetClientSize().y * 2 / 3)
sy -= GetSize().y;
sx -= GetSize().x / 2;
if (sx < 5) sx = 5;
if (sx < 0) x = 0;
#if wxUSE_POPUPWIN
Position(wxPoint(sx, sy), wxSize(0, 0));
Popup(m_explainText);
m_explainText->SetFocus();
#ifdef __WXGTK__
m_explainText->CaptureMouse();
#endif
#else
Move(sx, sy);
Show(true);
m_explainText->SetFocus();
m_explainText->CaptureMouse();
#endif
}
void ExplainPopup::OnMouseMove(wxMouseEvent &ev)
{
if ( m_rectBound.width &&
!m_rectBound.Contains(ClientToScreen(ev.GetPosition())) )
{
// mouse left the bounding rect, disappear
Close();
}
else
{
ev.Skip();
}
}
void ExplainPopup::OnMouseClick(wxMouseEvent &ev)
{
Close();
}
ExplainPopup::~ExplainPopup()
{
if (m_ptr)
{
*m_ptr = NULL;
}
#ifdef wxUSE_POPUPWIN
#ifdef __WXGTK__
if (m_explainText->HasCapture() )
m_explainText->ReleaseMouse();
#endif
#endif
if(HasCapture())
ReleaseMouse();
}
void ExplainPopup::Close()
{
if (m_ptr)
{
*m_ptr = NULL;
m_ptr = NULL;
}
if (m_explainText->HasCapture())
m_explainText->ReleaseMouse();
if(HasCapture())
ReleaseMouse();
#if wxUSE_POPUPWIN
Show(false);
#ifdef __WXGTK__
#endif
Destroy();
#else
wxFrame::Close();
#endif
}
#if wxUSE_POPUPWIN
void ExplainPopup::OnDismiss()
{
Close();
}
void ExplainPopup::OnMouseLost(wxMouseCaptureLostEvent &ev)
{
/* Do Nothing */
}
#else
void ExplainPopup::OnKillFocus(wxFocusEvent &event)
{
// Workaround the kill focus event happening just after creation in wxGTK
if (wxGetLocalTime() > m_creationTime + 1 &&
m_rectBound.width &&
!m_rectBound.Contains(::wxGetMousePosition()))
{
Close();
return;
}
m_explainText->SetFocus();
m_explainText->CaptureMouse();
}
void ExplainPopup::OnActivate(wxActivateEvent &event)
{
if (!event.GetActive())
{
if ( m_rectBound.width &&
!m_rectBound.Contains(::wxGetMousePosition()) )
{
Close();
return;
}
m_explainText->SetFocus();
m_explainText->CaptureMouse();
}
}
#endif // !wxUSE_POPUPWIN

581
ctl/explainShape.cpp Normal file
View file

@ -0,0 +1,581 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// explainShape.cpp - Explain Shapes
//
//////////////////////////////////////////////////////////////////////////
// wxWindows headers
#include <wx/wx.h>
// App headers
#include "pgAdmin3.h"
#include "ctl/explainCanvas.h"
#include <wx/docview.h>
#include "images/ex_aggregate.pngc"
#include "images/ex_append.pngc"
#include "images/ex_bmp_and.pngc"
#include "images/ex_bmp_heap.pngc"
#include "images/ex_bmp_index.pngc"
#include "images/ex_bmp_or.pngc"
#include "images/ex_cte_scan.pngc"
#include "images/ex_delete.pngc"
#include "images/ex_foreign_scan.pngc"
#include "images/ex_group.pngc"
#include "images/ex_hash.pngc"
#include "images/ex_hash_anti_join.pngc"
#include "images/ex_hash_semi_join.pngc"
#include "images/ex_hash_setop_except.pngc"
#include "images/ex_hash_setop_except_all.pngc"
#include "images/ex_hash_setop_intersect.pngc"
#include "images/ex_hash_setop_intersect_all.pngc"
#include "images/ex_hash_setop_unknown.pngc"
#include "images/ex_index_only_scan.pngc"
#include "images/ex_index_scan.pngc"
#include "images/ex_insert.pngc"
#include "images/ex_lock_rows.pngc"
#include "images/ex_join.pngc"
#include "images/ex_limit.pngc"
#include "images/ex_materialize.pngc"
#include "images/ex_merge.pngc"
#include "images/ex_merge_anti_join.pngc"
#include "images/ex_merge_append.pngc"
#include "images/ex_merge_semi_join.pngc"
#include "images/ex_gatcher.pngc"
#include "images/ex_nested.pngc"
#include "images/ex_nested_loop_anti_join.pngc"
#include "images/ex_nested_loop_semi_join.pngc"
#include "images/ex_recursive_union.pngc"
#include "images/ex_result.pngc"
#include "images/ex_scan.pngc"
#include "images/ex_pscan.pngc"
#include "images/ex_seek.pngc"
#include "images/ex_setop.pngc"
#include "images/ex_sort.pngc"
#include "images/ex_subplan.pngc"
#include "images/ex_tid_scan.pngc"
#include "images/ex_unique.pngc"
#include "images/ex_unknown.pngc"
#include "images/ex_update.pngc"
#include "images/ex_values_scan.pngc"
#include "images/ex_window_aggregate.pngc"
#include "images/ex_worktable_scan.pngc"
// Greenplum images
#include "images/ex_broadcast_motion.pngc"
#include "images/ex_redistribute_motion.pngc"
#include "images/ex_gather_motion.pngc"
#define BMP_BORDER 3
ExplainShape::ExplainShape(const wxImage &bmp, const wxString &description, long tokenNo, long detailNo)
{
SetBitmap(wxBitmap(bmp));
SetLabel(description, tokenNo, detailNo);
kidCount = 0;
totalShapes = 0;
usedShapes = 0;
m_rootShape = false;
}
void ExplainShape::SetLabel(const wxString &str, int tokenNo, int detailNo)
{
if (tokenNo < 0)
{
description = str;
label = str;
}
else
{
wxStringTokenizer tokens(str, wxT(" "));
while (tokenNo-- >= 0)
{
label = tokens.GetNextToken();
if (detailNo <= 0)
{
if (!description.IsEmpty())
description.Append(wxT(" "));
description.Append(label);
}
}
if (detailNo > 0)
{
tokens.SetString(str, wxT(" "));
while (detailNo--)
{
if (!description.IsEmpty())
description.Append(wxT(" "));
description.Append(tokens.GetNextToken());
}
detail = tokens.GetString();
}
}
}
void ExplainShape::OnDraw(wxDC &dc)
{
wxBitmap &bmp = GetBitmap();
if (!bmp.Ok())
return;
// We do not draw the root shape
if (m_rootShape)
return;
int x, y;
x = WXROUND(m_xpos - bmp.GetWidth() / 2.0);
y = WXROUND(m_ypos - GetHeight() / 2.0);
dc.DrawBitmap(bmp, x, y, true);
int w, h;
dc.SetFont(GetCanvas()->GetFont());
dc.GetTextExtent(label, &w, &h);
x = WXROUND(m_xpos - w / 2.0);
y += bmp.GetHeight() + BMP_BORDER;
dc.DrawText(label, x, y);
}
void ExplainShape::OnLeftClick(double x, double y, int keys, int attachment)
{
((ExplainCanvas *)GetCanvas())->ShowPopup(this);
}
#define ARROWMARGIN 5
wxRealPoint ExplainShape::GetStartPoint()
{
wxRealPoint rp(GetX() + GetBitmap().GetWidth() / 2.0 + ARROWMARGIN, GetY() - (GetHeight() - GetBitmap().GetHeight()) / 2.);
return rp;
}
wxRealPoint ExplainShape::GetEndPoint(int kidNo)
{
wxRealPoint rp(GetX() - GetBitmap().GetWidth() / 2.0 - ARROWMARGIN, GetY() - (GetHeight() - GetBitmap().GetHeight()) / 2. + (kidCount > 1 ? GetBitmap().GetHeight() * 2. / 3. * kidNo / (2 * kidCount - 2) : 0 ));
return rp;
}
ExplainShape *ExplainShape::Create(long level, ExplainShape *last, const wxString &str)
{
ExplainShape *s = 0;
int costPos = str.Find(wxT("(cost="));
int actPos = str.Find(wxT("(actual"));
bool parallel=false;
int inc=0;
wxStringTokenizer tokens(str, wxT(" "));
wxString token = tokens.GetNextToken();
if (token == wxT("Parallel"))
{
token = tokens.GetNextToken();
parallel=true;
inc=1;
}
wxString token2 = tokens.GetNextToken();
wxString token3 = tokens.GetNextToken();
wxString token4;
if (tokens.HasMoreTokens())
token4 = tokens.GetNextToken();
wxString descr;
if (costPos > 0)
descr = str.Left(costPos);
else if (actPos > 0)
descr = str.Left(actPos);
else
descr = str;
// Requested an empty shape, which can be treated as a root shape
if (level == 0)
{
s = new ExplainShape(*ex_unknown_png_img, wxEmptyString);
s->SetDraggable(false);
s->m_rootShape = true;
s->level = level;
int w = 50, h = 20;
wxBitmap &bmp = s->GetBitmap();
if (w < bmp.GetWidth())
w = bmp.GetWidth();
s->SetHeight(bmp.GetHeight() + BMP_BORDER + h);
s->SetWidth(w);
s->upperShape = NULL;
s->kidNo = 0;
return s;
}
// possible keywords can be found in postgresql/src/backend/commands/explain.c
if (token == wxT("Total")) return 0;
else if (token == wxT("Trigger")) return 0;
else if (token == wxT("Settings:")) return 0; /* Greenplum */
else if (token == wxT("Slice")) return 0; /* Greenplum */
else if (token.Mid(0, 6) == wxT("(slice")) return 0; /* Greenplum */
else if (token == wxT("Result")) s = new ExplainShape(*ex_result_png_img, descr);
else if (token == wxT("Append")) s = new ExplainShape(*ex_append_png_img, descr);
else if (token == wxT("Gather")) s = new ExplainShape(*ex_gatcher_png_img, descr);
else if (token == wxT("Nested"))
{
if (token2 == wxT("Loop") && token4 == wxT("Join"))
{
// Nested Loop Anti Join
if (token3 == wxT("Anti"))
{
s = new ExplainShape(*ex_nested_loop_anti_join_png_img, descr);
}
// Nested Loop Semi Join
else
{
s = new ExplainShape(*ex_nested_loop_semi_join_png_img, descr);
}
}
if (!s)
s = new ExplainShape(*ex_nested_png_img, descr);
}
else if (token == wxT("Merge"))
{
if (token3 == wxT("Join"))
{
// Merge Anti Join
if (token2 == wxT("Anti"))
{
s = new ExplainShape(*ex_merge_anti_join_png_img, descr);
}
// Merge Semi Join
else
{
s = new ExplainShape(*ex_merge_semi_join_png_img, descr);
}
}
// Merge Append
else if (token2 == wxT("Append"))
{
s = new ExplainShape(*ex_merge_append_png_img, descr);
}
else
{
s = new ExplainShape(*ex_merge_png_img, descr);
}
}
else if (token == wxT("Hash"))
{
if (token2 == wxT("Join"))
{
s = new ExplainShape(*ex_join_png_img, descr);
}
else
{
if (token3 == wxT("Join"))
{
// Hash Anti Join
if (token2 == wxT("Anti"))
{
s = new ExplainShape(*ex_hash_anti_join_png_img, descr);
}
// Hash Semi Join
else if (token2 == wxT("Semi"))
{
s = new ExplainShape(*ex_hash_semi_join_png_img, descr);
}
else
{
s = new ExplainShape(*ex_hash_png_img, descr);
}
}
else
{
s = new ExplainShape(*ex_hash_png_img, descr);
}
}
}
else if (token == wxT("HashSetOp"))
{
if (token2 == wxT("Except"))
{
// HashSetOp Except ALL
if (token3 == wxT("ALL"))
{
s = new ExplainShape(*ex_hash_setop_except_all_png_img, descr);
}
// HashSetOp Except
else
{
s = new ExplainShape(*ex_hash_setop_except_png_img, descr);
}
}
else if (token2 == wxT("Intersect"))
{
// HashSetOp Intersect ALL
if (token3 == wxT("ALL"))
{
s = new ExplainShape(*ex_hash_setop_intersect_all_png_img, descr);
}
// HashSetOp Intersect
else
{
s = new ExplainShape(*ex_hash_setop_intersect_png_img, descr);
}
}
else
{
// HashSetOp ???
s = new ExplainShape(*ex_hash_setop_unknown_png_img, descr);
}
}
else if (token == wxT("Subquery")) s = new ExplainShape(*ex_subplan_png_img, descr, 0, 2);
else if (token == wxT("Function")) s = new ExplainShape(*ex_result_png_img, descr, 0, 2);
else if (token == wxT("Materialize")) s = new ExplainShape(*ex_materialize_png_img, descr);
else if (token == wxT("Sort")) s = new ExplainShape(*ex_sort_png_img, descr);
else if (token == wxT("Group")) s = new ExplainShape(*ex_group_png_img, descr);
else if (token == wxT("Aggregate") || token == wxT("GroupAggregate") || token == wxT("HashAggregate"))
s = new ExplainShape(*ex_aggregate_png_img, descr);
else if (token == wxT("Unique")) s = new ExplainShape(*ex_unique_png_img, descr);
else if (token == wxT("SetOp")) s = new ExplainShape(*ex_setop_png_img, descr);
else if (token == wxT("Limit")) s = new ExplainShape(*ex_limit_png_img, descr);
else if (token == wxT("LockRows")) s = new ExplainShape(*ex_lock_rows_png_img, descr);
else if (token == wxT("Bitmap"))
{
if (token2 == wxT("Index")) s = new ExplainShape(*ex_bmp_index_png_img, descr, 4+inc, 3+inc);
else s = new ExplainShape(*ex_bmp_heap_png_img, descr, 4+inc, 3+inc);
}
else if (token == wxT("BitmapAnd")) s = new ExplainShape(*ex_bmp_and_png_img, descr);
else if (token == wxT("BitmapOr")) s = new ExplainShape(*ex_bmp_or_png_img, descr);
else if (token2 == wxT("Scan"))
{
if (token == wxT("Index"))
// Scan Index Backward
if (token3 == wxT("Backward"))
s = new ExplainShape(*ex_index_scan_png_img, descr, 4+inc, 3+inc);
else
s = new ExplainShape(*ex_index_scan_png_img, descr, 3+inc, 2+inc);
// Tid Scan
else if (token == wxT("Tid"))
s = new ExplainShape(*ex_tid_scan_png_img, descr, 3+inc, 2+inc);
// WorkTable Scan
else if (token == wxT("WorkTable"))
s = new ExplainShape(*ex_worktable_scan_png_img, descr, 3+inc, 2+inc);
// CTE Scan
else if (token == wxT("CTE"))
s = new ExplainShape(*ex_cte_scan_png_img, descr, 3, 2);
// Foreign Scan
else if (token == wxT("Foreign"))
s = new ExplainShape(*ex_foreign_scan_png_img, descr, 3, 2);
// Values Scan
else if (token == wxT("Values"))
s = new ExplainShape(*ex_values_scan_png_img, descr, 3, 2);
else if (parallel) s = new ExplainShape(*ex_pscan_png_img, descr, 4, 3);
else
s = new ExplainShape(*ex_scan_png_img, descr, 3+inc, 2+inc);
}
else if (token == wxT("Index"))
{
// Index Only Scan
if (token2 == wxT("Only") && token3 == wxT("Scan"))
{
s = new ExplainShape(*ex_index_only_scan_png_img, descr, 4+inc, 3+inc);
}
}
else if (token2 == wxT("Seek")) s = new ExplainShape(*ex_seek_png_img, descr, 3, 2);
// Recursive Union
else if (token == wxT("Recursive") && token2 == wxT("Union"))
s = new ExplainShape(*ex_recursive_union_png_img, descr);
else if (token == wxT("WindowAgg"))
s = new ExplainShape(*ex_window_aggregate_png_img, descr);
// DML
else if (token == wxT("Insert"))
s = new ExplainShape(*ex_insert_png_img, descr, 2, 1);
else if (token == wxT("Update"))
s = new ExplainShape(*ex_update_png_img, descr, 2, 1);
else if (token == wxT("Delete"))
s = new ExplainShape(*ex_delete_png_img, descr, 2, 1);
// Greenplum additions
else if (token == wxT("Gather") && token2 == wxT("Motion"))
s = new ExplainShape(*ex_gather_motion_png_img, descr);
else if (token == wxT("Broadcast") && token2 == wxT("Motion"))
s = new ExplainShape(*ex_broadcast_motion_png_img, descr);
else if (token == wxT("Redistribute") && token2 == wxT("Motion"))
s = new ExplainShape(*ex_redistribute_motion_png_img, descr);
if (!s)
s = new ExplainShape(*ex_unknown_png_img, descr);
s->SetDraggable(false);
s->level = level;
if (costPos > 0)
{
if (actPos > 0)
{
s->actual = str.Mid(actPos);
s->cost = str.Mid(costPos, actPos - costPos);
}
else
s->cost = str.Mid(costPos);
}
else if (actPos > 0)
s->actual = str.Mid(actPos);
int w = 50, h = 20;
wxBitmap &bmp = s->GetBitmap();
if (w < bmp.GetWidth())
w = bmp.GetWidth();
s->SetHeight(bmp.GetHeight() + BMP_BORDER + h);
s->SetWidth(w);
s->upperShape = last;
if (last)
{
s->kidNo = last->kidCount;
last->kidCount++;
}
else
s->kidNo = 0;
if (costPos > 0)
{
wxChar *cl = const_cast<wxChar *>((const wxChar *)str + costPos + 6);
wxChar *ch = wxStrstr(cl, wxT(".."));
if (ch)
{
*ch = 0;
ch += 2;
}
s->costLow = StrToDouble(cl);
if (ch)
{
wxChar *r = wxStrstr(ch, wxT(" rows="));
if (r)
{
*r = 0;
r += 6;
}
s->costHigh = StrToDouble(ch);
if (r)
{
wxChar *w = wxStrstr(r, wxT(" width="));
if (w)
{
*w = 0;
w += 7;
}
s->rows = StrToLong(r);
if (w)
s->width = StrToLong(w);
}
}
}
return s;
}
ExplainLine::ExplainLine(ExplainShape *from, ExplainShape *to, double weight)
{
SetCanvas(from->GetCanvas());
from->AddLine(this, to);
MakeLineControlPoints(4);
width = (int) log(from->GetAverageCost());
if (width > 10)
width = 10;
// If we got an average cost of 0, width is probably negative
// which will look pretty darn ugly as an arrow width!
// This may happen on Greenplum.
if (width < 1)
width = 1;
wxNode *first = GetLineControlPoints()->GetFirst();
wxNode *last = GetLineControlPoints()->GetLast();
*(wxRealPoint *)first->GetData() = from->GetStartPoint();
*(wxRealPoint *)last->GetData() = to->GetEndPoint(from->GetKidno());
wxRealPoint *p1 = (wxRealPoint *)first->GetNext()->GetData();
wxRealPoint *p2 = (wxRealPoint *)last->GetPrevious()->GetData();
*p1 = from->GetStartPoint();
*p2 = to->GetEndPoint(from->GetKidno());
p1->x -= (p1->x - p2->x) / 3. + 8;
p2->x += (p1->x - p2->x) / 3. - 8;
Initialise();
}
#define ARROWWIDTH 4
void ExplainLine::OnDraw(wxDC &dc)
{
if (m_lineControlPoints)
{
dc.SetPen(*wxThePenList->FindOrCreatePen(*wxBLACK, 1, wxSOLID));
dc.SetBrush(*wxTheBrushList->FindOrCreateBrush(*wxLIGHT_GREY, wxSOLID));
wxPoint *points = new wxPoint[11];
wxRealPoint *point0 = (wxRealPoint *) m_lineControlPoints->Item(0)->GetData();
wxRealPoint *point1 = (wxRealPoint *) m_lineControlPoints->Item(1)->GetData();
wxRealPoint *point2 = (wxRealPoint *) m_lineControlPoints->Item(2)->GetData();
wxRealPoint *point3 = (wxRealPoint *) m_lineControlPoints->Item(3)->GetData();
double phi = atan2(point2->y - point1->y, point2->x - point1->x);
double offs = width * tan(phi / 2);
points[0].x = WXROUND(point0->x);
points[0].y = WXROUND(point0->y) - width;
points[10].x = WXROUND(point0->x);
points[10].y = WXROUND(point0->y) + width;
points[1].x = WXROUND(point1->x + offs);
points[1].y = WXROUND(point1->y) - width;
points[9].x = WXROUND(point1->x - offs);
points[9].y = WXROUND(point1->y) + width;
points[2].x = WXROUND(point2->x + offs);
points[2].y = WXROUND(point2->y) - width;
points[8].x = WXROUND(point2->x - offs);
points[8].y = WXROUND(point2->y) + width;
points[3].x = WXROUND(point3->x) - width - ARROWWIDTH;
points[3].y = WXROUND(point3->y) - width;
points[7].x = WXROUND(point3->x) - width - ARROWWIDTH;
points[7].y = WXROUND(point3->y) + width;
points[4].x = points[3].x;
points[4].y = points[3].y - ARROWWIDTH;
points[6].x = points[7].x;
points[6].y = points[7].y + ARROWWIDTH;
points[5].x = WXROUND(point3->x);
points[5].y = WXROUND(point3->y);
dc.DrawPolygon(11, points, 0, 0);
}
}

42
ctl/module.mk Normal file
View file

@ -0,0 +1,42 @@
#######################################################################
#
# pgAdmin III - PostgreSQL Tools
#
# Copyright (C) 2002 - 2016, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
# module.mk - pgadmin/ctl/ Makefile fragment
#
#######################################################################
pgadmin3_SOURCES += \
ctl/calbox.cpp \
ctl/ctlAuiNotebook.cpp \
ctl/ctlCheckTreeView.cpp \
ctl/ctlColourPicker.cpp \
ctl/ctlComboBox.cpp \
ctl/ctlListView.cpp \
ctl/ctlMenuToolbar.cpp \
ctl/ctlSQLBox.cpp \
ctl/ctlSQLGrid.cpp \
ctl/ctlSQLResult.cpp \
ctl/ctlDefaultSecurityPanel.cpp \
ctl/ctlSeclabelPanel.cpp \
ctl/ctlSecurityPanel.cpp \
ctl/ctlTree.cpp \
ctl/ctlProgressStatusBar.cpp \
ctl/explainCanvas.cpp \
ctl/explainShape.cpp \
ctl/timespin.cpp \
ctl/xh_calb.cpp \
ctl/xh_ctlcolourpicker.cpp \
ctl/xh_ctlcombo.cpp \
ctl/xh_ctlchecktreeview.cpp \
ctl/xh_ctltree.cpp \
ctl/xh_sqlbox.cpp \
ctl/xh_timespin.cpp
EXTRA_DIST += \
ctl/module.mk

461
ctl/timespin.cpp Normal file
View file

@ -0,0 +1,461 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// timespin.cpp - timeSpin SpinCtrl
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
#include <wx/wx.h>
#include "ctl/timespin.h"
#define CTRLID_TXT 101
#define CTRLID_SPN 102
BEGIN_EVENT_TABLE(wxTimeSpinCtrl, wxControl)
EVT_TEXT(CTRLID_TXT, wxTimeSpinCtrl::OnText)
EVT_NAVIGATION_KEY(wxTimeSpinCtrl::OnNavigate)
EVT_SPIN_UP(CTRLID_SPN, wxTimeSpinCtrl::OnSpinUp)
EVT_SPIN_DOWN(CTRLID_SPN, wxTimeSpinCtrl::OnSpinDown)
EVT_SPIN(CTRLID_SPN, wxTimeSpinCtrl::OnSpin)
EVT_SET_FOCUS(wxTimeSpinCtrl::OnSetFocus)
EVT_SIZE(wxTimeSpinCtrl::OnSize)
END_EVENT_TABLE()
IMPLEMENT_DYNAMIC_CLASS(wxTimeSpinCtrl, wxControl)
wxTimeSpinCtrl::wxTimeSpinCtrl(wxWindow *parent,
wxWindowID id,
const wxPoint &pos,
const wxSize &size,
long style,
const wxString &name)
{
Init();
Create(parent, id, pos, size, style, name);
}
bool wxTimeSpinCtrl::Create(wxWindow *parent,
wxWindowID id,
const wxPoint &pos,
const wxSize &size,
long style,
const wxString &name)
{
wxControl::Create(parent, id, pos, size, style & ~(wxSP_WRAP | wxSP_ARROW_KEYS), wxDefaultValidator, name);
SetFont(parent->GetFont());
m_spn = new wxSpinButton(this, CTRLID_SPN, wxDefaultPosition, wxDefaultSize, wxSP_WRAP | wxSP_VERTICAL | (style & wxSP_ARROW_KEYS));
wxSize cs = GetClientSize();
wxSize ss = m_spn->GetBestSize();
m_txt = new wxTextCtrl(this, CTRLID_TXT, wxEmptyString, wxPoint(0, 0), wxSize(cs.x - ss.x, cs.y), wxWANTS_CHARS);
m_txt->Connect(wxID_ANY, wxID_ANY, wxEVT_KEY_DOWN, wxKeyEventHandler(wxTimeSpinCtrl::OnEditKey), 0, this);
m_txt->Connect(wxID_ANY, wxID_ANY, wxEVT_KILL_FOCUS, wxFocusEventHandler(wxTimeSpinCtrl::OnKillFocus), 0, this);
wxArrayString valArray;
wxChar c;
for (c = '0'; c <= '9'; c++)
valArray.Add(wxString(c, 1));
valArray.Add(wxT(":"));
wxTextValidator tv(wxFILTER_INCLUDE_CHAR_LIST);
tv.SetIncludes(valArray);
m_txt->SetValidator(tv);
m_spn->SetSize(ss.x, cs.y);
m_spn->SetPosition(wxPoint(cs.x - ss.x, 0));
canWrap = (style & wxSP_WRAP) != 0;
SetMax(24 * 60 * 60 - 1);
SetInitialSize(size);
return true;
}
#define TXTPOSX 0
#define TXTPOSY 0
void wxTimeSpinCtrl::OnSize(wxSizeEvent &event)
{
if ( m_spn )
{
wxSize sz = GetClientSize();
wxSize ss = m_spn->GetSize();
int eh = m_txt->GetBestSize().y;
m_txt->SetSize(TXTPOSX, TXTPOSY, sz.x - ss.x - TXTPOSX, sz.y > eh ? eh - TXTPOSY : sz.y - TXTPOSY);
m_spn->SetSize(sz.x - ss.x, 0, ss.x, sz.y);
}
event.Skip();
}
bool wxTimeSpinCtrl::Destroy()
{
if (m_txt)
{
m_txt->Destroy();
m_txt = NULL;
}
if (m_spn)
{
m_spn->Destroy();
m_spn = NULL;
}
return true;
}
void wxTimeSpinCtrl::Init()
{
m_txt = NULL;
m_spn = NULL;
spinValue = 0;
}
wxSize wxTimeSpinCtrl::DoGetBestSize() const
{
#ifdef __WXGTK__
return wxSize(100, m_spn->GetBestSize().y);
#else
return wxSize(100, m_txt->GetBestSize().y);
#endif
}
bool wxTimeSpinCtrl::Enable(bool enable)
{
m_txt->Enable(enable);
m_spn->Enable(enable);
return true;
}
void wxTimeSpinCtrl::SetMax(long seconds, bool useDay)
{
hasDay = useDay;
if (hasDay)
m_format = wxT("%D:%H:%M:%S");
else
m_format = wxT("%H:%M:%S");
maxSpinValue = seconds + 1;
if (maxSpinValue < 2)
maxSpinValue = 2;
m_spn->SetRange(0, 32767);
}
bool wxTimeSpinCtrl::SetTime(const wxDateTime &time)
{
if (time.IsValid())
{
wxDateTime tt(time);
tt.ResetTime();
return SetValue(time - tt);
}
else
{
m_txt->SetValue(wxEmptyString);
return false;
}
}
bool wxTimeSpinCtrl::SetValue(const wxTimeSpan &span)
{
m_txt->SetValue(span.Format(m_format));
spinValue = span.GetSeconds().GetLo();
return true;
}
wxTimeSpan wxTimeSpinCtrl::GetValue()
{
return wxTimeSpan(0, 0, spinValue);
}
int wxTimeSpinCtrl::GetTimePart()
{
wxString strAfter = m_txt->GetRange(m_txt->GetInsertionPoint(), 9999);
int cnt = 0;
const wxChar *p = (const wxChar *)strAfter;
while (*p)
{
if (*p++ == ':')
cnt++;
}
return cnt;
}
void wxTimeSpinCtrl::Highlight(int tp)
{
wxString txt = m_txt->GetValue();
long from = 0, to;
int i;
for (i = 3 - tp - (hasDay ? 0 : 1) ; i > 0 ; i--)
from += txt.Mid(from).Find(':') + 1;
to = txt.Mid(from).Find(':') + from;
if (to < from)
to = 999;
m_txt->SetSelection(from, to);
}
void wxTimeSpinCtrl::OnSpinUp(wxSpinEvent &ev)
{
#ifdef __WXMSW__
m_txt->SetFocus();
#endif
DoSpin(1);
}
void wxTimeSpinCtrl::OnSpinDown(wxSpinEvent &ev)
{
#ifdef __WXMSW__
m_txt->SetFocus();
#endif
DoSpin(-1);
}
void wxTimeSpinCtrl::OnSpin(wxSpinEvent &ev)
{
wxCommandEvent parentSpinEvt(ev.GetEventType(), this->GetId());
this->GetEventHandler()->AddPendingEvent(parentSpinEvt);
}
void wxTimeSpinCtrl::DoSpin(int diff)
{
int tp = GetTimePart();
long oldValue = 0, maxValue = 0, mult = 0;
switch (tp)
{
case 0:
oldValue = spinValue;
maxValue = 60;
mult = 1;
break;
case 1:
oldValue = spinValue / 60;
maxValue = 60;
mult = 60;
break;
case 2:
oldValue = spinValue / 60 / 60;
if (hasDay)
maxValue = 24;
else
maxValue = maxSpinValue / 60 / 60;
mult = 60 * 60;
break;
case 3:
oldValue = spinValue / 60 / 60 / 24;
maxValue = maxSpinValue / 60 / 60 / 24;
mult = 60 * 60 * 24;
break;
default:
// can't happen
break;
}
oldValue %= maxValue;
int newValue = oldValue + diff;
if (!canWrap)
{
if (newValue < 0)
diff += maxValue;
else if (newValue >= maxValue)
diff -= maxValue;
}
long newSpinValue = spinValue + diff * mult;
if (newSpinValue < 0)
newSpinValue = maxSpinValue - 1;
else if (newSpinValue > maxSpinValue)
newSpinValue = 0;
if (spinValue != newSpinValue)
{
spinValue = newSpinValue;
wxTimeSpan span(0, 0, spinValue);
wxString txt = span.Format(m_format);
m_txt->SetValue(txt);
Highlight(tp);
wxSpinEvent ev;
ev.SetEventObject(this);
ev.SetId(GetId());
#if wxCHECK_VERSION(2, 9, 0)
GetParent()->GetEventHandler()->ProcessEvent(ev);
#else
GetParent()->ProcessEvent(ev);
#endif
}
}
void wxTimeSpinCtrl::OnText(wxCommandEvent &ev)
{
long time = GetTextTime();
if (time >= 0)
{
spinValue = time;
ev.SetEventObject(this);
ev.SetId(GetId());
#if wxCHECK_VERSION(2, 9, 0)
GetParent()->GetEventHandler()->ProcessEvent(ev);
#else
GetParent()->ProcessEvent(ev);
#endif
}
}
void wxTimeSpinCtrl::OnNavigate(wxNavigationKeyEvent &ev)
{
if (wxWindow::FindFocus() == m_txt)
{
int tp = GetTimePart();
if (ev.GetDirection())
tp++;
else
tp--;
if ((tp >= 0 && tp < 3) || (hasDay && tp == 3))
{
Highlight(tp);
return;
}
}
ev.Skip();
}
void wxTimeSpinCtrl::OnEditKey(wxKeyEvent &ev)
{
if (!ev.HasModifiers())
{
switch(ev.GetKeyCode())
{
case WXK_LEFT:
{
if (!ev.ShiftDown())
{
int tp = GetTimePart() + 1;
if (tp < 3 || (hasDay && tp == 3))
{
Highlight(tp);
return;
}
}
break;
}
case WXK_RIGHT:
{
if (!ev.ShiftDown())
{
int tp = GetTimePart() - 1;
if (tp >= 0)
{
Highlight(tp);
return;
}
}
break;
}
case WXK_UP:
DoSpin(1);
return;
break;
case WXK_DOWN:
DoSpin(-1);
return;
break;
default:
break;
}
}
ev.Skip();
}
long wxTimeSpinCtrl::GetTextTime()
{
int t1, t2, t3, t4;
int scanned = wxSscanf(m_txt->GetValue(), wxT("%d:%d:%d:%d"), &t1, &t2, &t3, &t4);
switch (scanned)
{
case 1:
if (t1 >= 0)
return t1;
break;
case 2:
if (t1 >= 0 && t2 >= 0 && t2 < 60)
return t1 * 60 + t2;
break;
case 3:
if (t1 >= 0 && t2 >= 0 && t2 < 60 && t3 >= 0 && t3 < 60)
return (t1 * 60 + t2) * 60 + t3;
break;
case 4:
if (t1 >= 0 && t2 >= 0 && t2 < 24 && t3 >= 0 && t3 < 60 && t4 >= 0 && t4 < 60)
return ((t1 * 24 + t2) * 60 + t3) * 60 + t4;
break;
default:
break;
}
return -1;
}
void wxTimeSpinCtrl::OnKillFocus(wxFocusEvent &ev)
{
#ifdef __WXGTK__
ev.Skip();
#endif
long time = GetTextTime();
if (time < 0)
{
m_txt->SetValue(wxEmptyString);
spinValue = 0;
m_spn->SetValue(0);
}
else
{
int tp = GetTimePart();
SetValue(wxTimeSpan(0, 0, time));
Highlight(tp);
}
}
void wxTimeSpinCtrl::OnSetFocus(wxFocusEvent &ev)
{
m_txt->SetFocus();
Highlight(hasDay ? 3 : 2);
}

77
ctl/xh_calb.cpp Normal file
View file

@ -0,0 +1,77 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// xh_calb.cpp - wxCalendarBox handler
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
#include "wx/wx.h"
#include "ctl/xh_calb.h"
#include "ctl/calbox.h"
IMPLEMENT_DYNAMIC_CLASS(wxCalendarBoxXmlHandler, wxXmlResourceHandler)
wxCalendarBoxXmlHandler::wxCalendarBoxXmlHandler()
: wxXmlResourceHandler()
{
/*
* Only available with the wxDatePickerCtrl
*/
XRC_ADD_STYLE(wxDP_DEFAULT);
XRC_ADD_STYLE(wxDP_SPIN);
XRC_ADD_STYLE(wxDP_DROPDOWN);
XRC_ADD_STYLE(wxDP_ALLOWNONE);
XRC_ADD_STYLE(wxDP_SHOWCENTURY);
AddWindowStyles();
}
wxObject *wxCalendarBoxXmlHandler::DoCreateResource()
{
XRC_MAKE_INSTANCE(calendar, wxCalendarBox);
#if pgUSE_WX_CAL
calendar->Create(m_parentAsWindow,
GetID(),
wxDefaultDateTime,
GetPosition(), GetSize(),
wxDP_DEFAULT | wxDP_SHOWCENTURY | wxDP_ALLOWNONE,
wxDefaultValidator,
GetName());
#else // pgUSE_WX_CAL
#if !defined(wxUSE_DATEPICKCTRL) || !wxUSE_DATEPICKCTRL
calendar->Create(m_parentAsWindow,
GetID(),
wxDefaultDateTime,
GetPosition(), GetSize(),
GetStyle(),
GetName());
#else // !defined(wxUSE_DATEPICKCTRL) || !wxUSE_DATEPICKCTRL
calendar->Create(m_parentAsWindow,
(wxWindowID)GetID(),
wxDefaultDateTime,
GetPosition(), GetSize(),
(long int)GetStyle(),
wxDefaultValidator,
GetName());
#endif // !defined(wxUSE_DATEPICKCTRL) || !wxUSE_DATEPICKCTRL
#endif // pgUSE_WX_CAL
SetupWindow(calendar);
return calendar;
}
bool wxCalendarBoxXmlHandler::CanHandle(wxXmlNode *node)
{
return IsOfClass(node, wxT("wxCalendarBox"));
}

View file

@ -0,0 +1,33 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// xh_ctlchecktreeview.cpp - ctlCheckTreeView handler
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
#include "wx/wx.h"
#include "ctl/ctlCheckTreeView.h"
#include "ctl/xh_ctlchecktreeview.h"
IMPLEMENT_DYNAMIC_CLASS(ctlCheckTreeViewXmlHandler, wxTreeCtrlXmlHandler)
wxObject *ctlCheckTreeViewXmlHandler::DoCreateResource()
{
ctlCheckTreeView *ctl = new ctlCheckTreeView(m_parentAsWindow, GetID(), GetPosition(), GetSize(), GetStyle());
SetupWindow(ctl);
return ctl;
}
bool ctlCheckTreeViewXmlHandler::CanHandle(wxXmlNode *node)
{
return IsOfClass(node, wxT("ctlCheckTreeView"));
}

View file

@ -0,0 +1,33 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the BSD Licence
//
// xh_ctlcolourpicker.cpp - ctlColourPicker handler
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
#include "wx/wx.h"
#include "ctl/ctlColourPicker.h"
#include "ctl/xh_ctlcolourpicker.h"
IMPLEMENT_DYNAMIC_CLASS(ctlColourPickerXmlHandler, wxBitmapButtonXmlHandler)
wxObject *ctlColourPickerXmlHandler::DoCreateResource()
{
ctlColourPicker *ctl = new ctlColourPicker(m_parentAsWindow, GetID(), GetPosition(), GetSize());
SetupWindow(ctl);
return ctl;
}
bool ctlColourPickerXmlHandler::CanHandle(wxXmlNode *node)
{
return IsOfClass(node, wxT("ctlColourPicker"));
}

33
ctl/xh_ctlcombo.cpp Normal file
View file

@ -0,0 +1,33 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// xh_ctlcombo.cpp - ctlComboBox handler
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
#include "wx/wx.h"
#include "ctl/ctlComboBox.h"
#include "ctl/xh_ctlcombo.h"
IMPLEMENT_DYNAMIC_CLASS(ctlComboBoxXmlHandler, wxComboBoxXmlHandler)
wxObject *ctlComboBoxXmlHandler::DoCreateResource()
{
ctlComboBox *ctl = new ctlComboBox(m_parentAsWindow, GetID(), GetPosition(), GetSize(), GetStyle());
SetupWindow(ctl);
return ctl;
}
bool ctlComboBoxXmlHandler::CanHandle(wxXmlNode *node)
{
return IsOfClass(node, wxT("ctlComboBox"));
}

35
ctl/xh_ctltree.cpp Normal file
View file

@ -0,0 +1,35 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// xh_ctltree.cpp - ctlTree handler
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
#include "wx/wx.h"
#include "utils/misc.h"
#include "ctl/ctlTree.h"
#include "ctl/xh_ctltree.h"
IMPLEMENT_DYNAMIC_CLASS(ctlTreeXmlHandler, wxTreeCtrlXmlHandler)
wxObject *ctlTreeXmlHandler::DoCreateResource()
{
ctlTree *ctl = new ctlTree(m_parentAsWindow, GetID(), GetPosition(), GetSize(), GetStyle());
SetupWindow(ctl);
return ctl;
}
bool ctlTreeXmlHandler::CanHandle(wxXmlNode *node)
{
return IsOfClass(node, wxT("ctlTree"));
}

45
ctl/xh_sqlbox.cpp Normal file
View file

@ -0,0 +1,45 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// xh_sqlbox.cpp - ctlSQLBox handler
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
#include "wx/wx.h"
#include "ctl/xh_sqlbox.h"
#include "ctl/ctlSQLBox.h"
IMPLEMENT_DYNAMIC_CLASS(ctlSQLBoxXmlHandler, wxXmlResourceHandler)
ctlSQLBoxXmlHandler::ctlSQLBoxXmlHandler()
: wxXmlResourceHandler()
{
XRC_ADD_STYLE(wxTE_MULTILINE);
XRC_ADD_STYLE(wxSIMPLE_BORDER);
XRC_ADD_STYLE(wxSUNKEN_BORDER);
XRC_ADD_STYLE(wxTE_RICH2);
AddWindowStyles();
}
wxObject *ctlSQLBoxXmlHandler::DoCreateResource()
{
ctlSQLBox *sqlbox = new ctlSQLBox(m_parentAsWindow, GetID(), GetPosition(), GetSize(), GetStyle());
SetupWindow(sqlbox);
return sqlbox;
}
bool ctlSQLBoxXmlHandler::CanHandle(wxXmlNode *node)
{
return IsOfClass(node, wxT("ctlSQLBox"));
}

49
ctl/xh_timespin.cpp Normal file
View file

@ -0,0 +1,49 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// xh_timespin.cpp - wxTimeSpinCtrl handler
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
#include "wx/wx.h"
#include "ctl/timespin.h"
#include "ctl/xh_timespin.h"
IMPLEMENT_DYNAMIC_CLASS(wxTimeSpinXmlHandler, wxXmlResourceHandler)
wxTimeSpinXmlHandler::wxTimeSpinXmlHandler()
: wxXmlResourceHandler()
{
XRC_ADD_STYLE(wxSP_WRAP);
XRC_ADD_STYLE(wxSP_ARROW_KEYS);
AddWindowStyles();
}
wxObject *wxTimeSpinXmlHandler::DoCreateResource()
{
XRC_MAKE_INSTANCE(timespin, wxTimeSpinCtrl);
timespin->Create(m_parentAsWindow,
GetID(),
GetPosition(), GetSize(),
GetStyle(),
GetName());
SetupWindow(timespin);
return timespin;
}
bool wxTimeSpinXmlHandler::CanHandle(wxXmlNode *node)
{
return IsOfClass(node, wxT("wxTimeSpinCtrl"));
}

152
db/keywords.c Normal file
View file

@ -0,0 +1,152 @@
/*-------------------------------------------------------------------------
*
* keywords.c
* lexical token lookup for reserved words in PostgreSQL
*
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.177 2006/10/07 21:51:02 petere Exp $
*
*-------------------------------------------------------------------------
*/
///////////////////////////////////////////////////////////////////////////
//
// pgAdmin note: This file is based on src/backend/parser/keywords.c and
// src/backend/parser/kwlookup.c from PostgreSQL, but extended
// to support EntepriseDB and Greenplum.
//
// This file is under the BSD licence, per PostgreSQL.
///////////////////////////////////////////////////////////////////////////
#include "postgres.h"
#include "parser/keywords.h"
/*
* List of (keyword-name, keyword-token-value) pairs.
*/
#define PG_KEYWORD(a,b,c) {a,c},
const ScanKeyword ScanKeywords[] = {
#include <parser/kwlist.h>
};
const int NumScanKeywords = lengthof(ScanKeywords);
/*
* Additional pairs here. They need to live in a separate array since
* the ScanKeywords array needs to be sorted!
*
* !!WARNING!!: This list must be sorted, because binary
* search is used to locate entries.
*/
#define PG_KEYWORD2(a,b) {a,b},
const ScanKeyword ScanKeywordsExtra[] = {
PG_KEYWORD2("connect", RESERVED_KEYWORD)
PG_KEYWORD2("convert", RESERVED_KEYWORD)
PG_KEYWORD2("distributed", UNRESERVED_KEYWORD)
PG_KEYWORD2("exec", RESERVED_KEYWORD)
PG_KEYWORD2("log", UNRESERVED_KEYWORD)
PG_KEYWORD2("long", RESERVED_KEYWORD)
PG_KEYWORD2("minus", RESERVED_KEYWORD)
PG_KEYWORD2("nocache", RESERVED_KEYWORD)
PG_KEYWORD2("number", RESERVED_KEYWORD)
PG_KEYWORD2("package", RESERVED_KEYWORD)
PG_KEYWORD2("pls_integer", RESERVED_KEYWORD)
PG_KEYWORD2("raw", RESERVED_KEYWORD)
PG_KEYWORD2("return", RESERVED_KEYWORD)
PG_KEYWORD2("smalldatetime", RESERVED_KEYWORD)
PG_KEYWORD2("smallfloat", RESERVED_KEYWORD)
PG_KEYWORD2("smallmoney", RESERVED_KEYWORD)
PG_KEYWORD2("sysdate", RESERVED_KEYWORD)
PG_KEYWORD2("systimestap", RESERVED_KEYWORD)
PG_KEYWORD2("tinyint", RESERVED_KEYWORD)
PG_KEYWORD2("tinytext", RESERVED_KEYWORD)
PG_KEYWORD2("varchar2", RESERVED_KEYWORD)
};
const int NumScanKeywordsExtra = lengthof(ScanKeywordsExtra);
/*
* ScanKeywordLookup - see if a given word is a keyword
*
* Returns a pointer to the ScanKeyword table entry, or NULL if no match.
*
* The match is done case-insensitively. Note that we deliberately use a
* dumbed-down case conversion that will only translate 'A'-'Z' into 'a'-'z',
* even if we are in a locale where tolower() would produce more or different
* translations. This is to conform to the SQL99 spec, which says that
* keywords are to be matched in this way even though non-keyword identifiers
* receive a different case-normalization mapping.
*/
const ScanKeyword *
ScanKeywordLookup(const char *text)
{
int len,
i;
char word[NAMEDATALEN];
const ScanKeyword *low;
const ScanKeyword *high;
len = strlen(text);
/* We assume all keywords are shorter than NAMEDATALEN. */
if (len >= NAMEDATALEN)
return NULL;
/*
* Apply an ASCII-only downcasing. We must not use tolower() since it may
* produce the wrong translation in some locales (eg, Turkish).
*/
for (i = 0; i < len; i++)
{
char ch = text[i];
if (ch >= 'A' && ch <= 'Z')
ch += 'a' - 'A';
word[i] = ch;
}
word[len] = '\0';
/*
* Now do a binary search using plain strcmp() comparison.
*/
low = &ScanKeywords[0];
high = endof(ScanKeywords) - 1;
while (low <= high)
{
const ScanKeyword *middle;
int difference;
middle = low + (high - low) / 2;
difference = strcmp(middle->name, word);
if (difference == 0)
return middle;
else if (difference < 0)
low = middle + 1;
else
high = middle - 1;
}
/*
* If not found, also do a binary search in the list of extra
* keywords.
*/
low = &ScanKeywordsExtra[0];
high = endof(ScanKeywordsExtra) - 1;
while (low <= high)
{
const ScanKeyword *middle;
int difference;
middle = low + (high - low) / 2;
difference = strcmp(middle->name, word);
if (difference == 0)
return middle;
else if (difference < 0)
low = middle + 1;
else
high = middle - 1;
}
return NULL;
}

20
db/module.mk Normal file
View file

@ -0,0 +1,20 @@
#######################################################################
#
# pgAdmin III - PostgreSQL Tools
#
# Copyright (C) 2002 - 2016, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
# module.mk - pgadmin/db/ Makefile fragment
#
#######################################################################
pgadmin3_SOURCES += \
db/keywords.c \
db/pgConn.cpp \
db/pgSet.cpp \
db/pgQueryThread.cpp
EXTRA_DIST += \
db/module.mk

1334
db/pgConn.cpp Normal file

File diff suppressed because it is too large Load diff

901
db/pgQueryThread.cpp Normal file
View file

@ -0,0 +1,901 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// pgQueryThread.cpp - PostgreSQL threaded query class
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
// PostgreSQL headers
#include <libpq-fe.h>
// App headers
#include "db/pgSet.h"
#include "db/pgConn.h"
#include "db/pgQueryThread.h"
#include "db/pgQueryResultEvent.h"
#include "utils/pgDefs.h"
#include "utils/sysLogger.h"
const wxEventType PGQueryResultEvent = wxNewEventType();
// default notice processor for the pgQueryThread
// we do assume that the argument passed will be always the
// object of pgQueryThread
void pgNoticeProcessor(void *_arg, const char *_message)
{
wxString str(_message, wxConvUTF8);
wxLogNotice(wxT("%s"), str.Trim().c_str());
if (_arg)
{
((pgQueryThread *)_arg)->AppendMessage(str);
}
}
// support for multiple queries support
pgQueryThread::pgQueryThread(pgConn *_conn, wxEvtHandler *_caller,
PQnoticeProcessor _processor, void *_noticeHandler) :
wxThread(wxTHREAD_JOINABLE), m_currIndex(-1), m_conn(_conn),
m_cancelled(false), m_multiQueries(true), m_useCallable(false),
m_caller(_caller), m_processor(pgNoticeProcessor), m_noticeHandler(NULL),
m_eventOnCancellation(true)
{
// check if we can really use the enterprisedb callable statement and
// required
#ifdef __WXMSW__
if (PQiGetOutResult && PQiPrepareOut && PQiSendQueryPreparedOut)
{
// we do not need all of pqi stuff as90 onwards
if (m_conn && m_conn->conn && m_conn->GetIsEdb()
&& !m_conn->EdbMinimumVersion(9, 0))
{
m_useCallable = true;
}
}
#else // __WXMSW__
#ifdef EDB_LIBPQ
// use callable statement only with enterprisedb advanced servers
// we do not need all of pqi stuff as90 onwards
if (m_conn && m_conn->conn && m_conn->GetIsEdb()
&& !m_conn->EdbMinimumVersion(9, 0))
{
m_useCallable = true;
}
#endif // EDB_LIBPQ
#endif // __WXMSW__
if (m_conn && m_conn->conn)
{
PQsetnonblocking(m_conn->conn, 1);
}
if (_processor != NULL)
{
m_processor = _processor;
if (_noticeHandler)
m_noticeHandler = _noticeHandler;
else
m_noticeHandler = (void *)this;
}
else
{
m_noticeHandler = (void *)this;
}
}
pgQueryThread::pgQueryThread(pgConn *_conn, const wxString &_qry,
int _resultToRetrieve, wxWindow *_caller, long _eventId, void *_data)
: wxThread(wxTHREAD_JOINABLE), m_currIndex(-1), m_conn(_conn),
m_cancelled(false), m_multiQueries(false), m_useCallable(false),
m_caller(NULL), m_processor(pgNoticeProcessor), m_noticeHandler(NULL),
m_eventOnCancellation(true)
{
if (m_conn && m_conn->conn)
{
PQsetnonblocking(m_conn->conn, 1);
}
m_queries.Add(
new pgBatchQuery(_qry, (pgParamsArray *)NULL, _eventId, _data, false,
_resultToRetrieve));
wxLogInfo(wxT("queueing : %s"), _qry.c_str());
m_noticeHandler = (void *)this;
if (_caller)
{
m_caller = _caller->GetEventHandler();
}
}
void pgQueryThread::SetEventOnCancellation(bool eventOnCancelled)
{
m_eventOnCancellation = eventOnCancelled;
}
void pgQueryThread::AddQuery(const wxString &_qry, pgParamsArray *_params,
long _eventId, void *_data, bool _useCallable, int _resultToRetrieve)
{
m_queries.Add(
new pgBatchQuery(_qry, _params, _eventId, _data,
// use callable statement only if supported
m_useCallable && _useCallable, _resultToRetrieve));
wxLogInfo(wxT("queueing (%ld): %s"), GetId(), _qry.c_str());
}
pgQueryThread::~pgQueryThread()
{
m_conn->RegisterNoticeProcessor(0, 0);
WX_CLEAR_ARRAY(m_queries);
}
wxString pgQueryThread::GetMessagesAndClear(int idx)
{
wxString msg;
if (idx == -1)
idx = m_currIndex;
if (idx >= 0 && idx <= m_currIndex)
{
wxCriticalSectionLocker lock(m_criticalSection);
msg = m_queries[idx]->m_message;
m_queries[idx]->m_message = wxT("");
}
return msg;
}
void pgQueryThread::AppendMessage(const wxString &str)
{
if (m_queries[m_currIndex]->m_message.IsEmpty())
{
wxCriticalSectionLocker lock(m_criticalSection);
if (str != wxT("\n"))
m_queries[m_currIndex]->m_message.Append(str);
}
else
{
wxCriticalSectionLocker lock(m_criticalSection);
m_queries[m_currIndex]->m_message.Append(wxT("\n")).Append(str);
}
}
int pgQueryThread::Execute()
{
wxMutexLocker lock(m_queriesLock);
PGresult *result = NULL;
wxMBConv &conv = *(m_conn->conv);
wxString &query = m_queries[m_currIndex]->m_query;
int &resultToRetrieve = m_queries[m_currIndex]->m_resToRetrieve;
long &rowsInserted = m_queries[m_currIndex]->m_rowsInserted;
Oid &insertedOid = m_queries[m_currIndex]->m_insertedOid;
// using the alias for the pointer here, in order to save the result back
// in the pgBatchQuery object
pgSet *&dataSet = m_queries[m_currIndex]->m_resultSet;
int &rc = m_queries[m_currIndex]->m_returnCode;
pgParamsArray *params = m_queries[m_currIndex]->m_params;
bool useCallable = m_queries[m_currIndex]->m_useCallable;
pgError &err = m_queries[m_currIndex]->m_err;
wxCharBuffer queryBuf = query.mb_str(conv);
if (PQstatus(m_conn->conn) != CONNECTION_OK)
{
rc = pgQueryResultEvent::PGQ_CONN_LOST;
err.msg_primary = _("Connection to the database server lost");
return(RaiseEvent(rc));
}
if (!queryBuf && !query.IsEmpty())
{
rc = pgQueryResultEvent::PGQ_STRING_INVALID;
m_conn->SetLastResultError(NULL, _("the query could not be converted to the required encoding."));
err.msg_primary = _("Query string is empty");
return(RaiseEvent(rc));
}
// Honour the parameters (if any)
if (params && params->GetCount() > 0)
{
int pCount = params->GetCount();
int ret = 0,
idx = 0;
Oid *pOids = (Oid *)malloc(pCount * sizeof(Oid));
const char **pParams = (const char **)malloc(pCount * sizeof(const char *));
int *pLens = (int *)malloc(pCount * sizeof(int));
int *pFormats = (int *)malloc(pCount * sizeof(int));
// modes are used only by enterprisedb callable statement
#if defined (__WXMSW__) || (EDB_LIBPQ)
int *pModes = (int *)malloc(pCount * sizeof(int));
#endif
for (; idx < pCount; idx++)
{
pgParam *param = (*params)[idx];
pOids[idx] = param->m_type;
pParams[idx] = (const char *)param->m_val;
pLens[idx] = param->m_len;
pFormats[idx] = param->GetFormat();
#if defined (__WXMSW__) || (EDB_LIBPQ)
pModes[idx] = param->m_mode;
#endif
}
if (useCallable)
{
#if defined (__WXMSW__) || (EDB_LIBPQ)
wxLogInfo(wxString::Format(
_("using an enterprisedb callable statement (queryid:%ld, threadid:%ld)"),
(long)m_currIndex, (long)GetId()));
wxString stmt = wxString::Format(wxT("pgQueryThread-%ld-%ld"), this->GetId(), m_currIndex);
PGresult *res = PQiPrepareOut(m_conn->conn, stmt.mb_str(wxConvUTF8),
queryBuf, pCount, pOids, pModes);
if( PQresultStatus(res) != PGRES_COMMAND_OK)
{
rc = pgQueryResultEvent::PGQ_ERROR_PREPARE_CALLABLE;
err.SetError(res, &conv);
PQclear(res);
goto return_with_error;
}
ret = PQiSendQueryPreparedOut(m_conn->conn, stmt.mb_str(wxConvUTF8),
pCount, pParams, pLens, pFormats, 1);
if (ret != 1)
{
rc = pgQueryResultEvent::PGQ_ERROR_EXECUTE_CALLABLE;
m_conn->SetLastResultError(NULL, _("Failed to run PQsendQuery in pgQueryThread"));
err.msg_primary = wxString(PQerrorMessage(m_conn->conn), conv);
PQclear(res);
res = NULL;
goto return_with_error;
}
PQclear(res);
res = NULL;
#else
rc = -1;
wxASSERT_MSG(false,
_("the program execution flow must not reach to this point in pgQueryThread"));
goto return_with_error;
#endif
}
else
{
// assumptions: we will need the results in text format only
ret = PQsendQueryParams(m_conn->conn, queryBuf, pCount, pOids, pParams, pLens, pFormats, 0);
if (ret != 1)
{
rc = pgQueryResultEvent::PGQ_ERROR_SEND_QUERY;
m_conn->SetLastResultError(NULL,
_("Failed to run PQsendQueryParams in pgQueryThread"));
err.msg_primary = _("Failed to run PQsendQueryParams in pgQueryThread.\n") +
wxString(PQerrorMessage(m_conn->conn), conv);
goto return_with_error;
}
}
goto continue_without_error;
return_with_error:
{
free(pOids);
free(pParams);
free(pLens);
free(pFormats);
#if defined (__WXMSW__) || (EDB_LIBPQ)
free(pModes);
#endif
return (RaiseEvent(rc));
}
}
else
{
// use the PQsendQuery api in case, we don't have any parameters to
// pass to the server
if (!PQsendQuery(m_conn->conn, queryBuf))
{
rc = pgQueryResultEvent::PGQ_ERROR_SEND_QUERY;
err.msg_primary = _("Failed to run PQsendQueryParams in pgQueryThread.\n") +
wxString(PQerrorMessage(m_conn->conn), conv);
return(RaiseEvent(rc));
}
}
continue_without_error:
int resultsRetrieved = 0;
PGresult *lastResult = 0;
bool connExecutionCancelled = false;
while (true)
{
// This is a 'joinable' thread, it is not advisable to call 'delete'
// function on this.
// Hence - it does not make sense to use the function 'testdestroy' here.
// We introduced the 'CancelExecution' function for the same purpose.
//
// We will have to call the CancelExecution function of pgConn to
// inform the backend that the user has cancelled the execution.
//
// We will need to consume all the results before quiting from the thread.
// Otherwise - the connection object will become unusable, and throw
// error - because libpq connections expects application to consume all
// the result, before executing another query
//
if (m_cancelled && rc != pgQueryResultEvent::PGQ_EXECUTION_CANCELLED)
{
// We shouldn't be calling cancellation multiple time
if (!connExecutionCancelled)
{
m_conn->CancelExecution();
connExecutionCancelled = true;
}
rc = pgQueryResultEvent::PGQ_EXECUTION_CANCELLED;
err.msg_primary = _("Execution Cancelled");
if (lastResult)
{
PQclear(lastResult);
lastResult = NULL;
}
}
if ((rc = PQconsumeInput(m_conn->conn)) != 1)
{
if (m_cancelled)
{
rc = pgQueryResultEvent::PGQ_EXECUTION_CANCELLED;
err.msg_primary = _("Execution Cancelled");
if (lastResult)
{
PQclear(lastResult);
lastResult = NULL;
}
// There is nothing more to consume.
// We can quit the thread now.
//
// Raise the event - if the component asked for it on
// execution cancellation.
if (m_eventOnCancellation)
RaiseEvent(rc);
return rc;
}
if (rc == 0)
{
err.msg_primary = wxString(PQerrorMessage(m_conn->conn), conv);
}
if (PQstatus(m_conn->conn) == CONNECTION_BAD)
{
err.msg_primary = _("Connection to the database server lost");
rc = pgQueryResultEvent::PGQ_CONN_LOST;
}
else
{
rc = pgQueryResultEvent::PGQ_ERROR_CONSUME_INPUT;
}
return(RaiseEvent(rc));
}
if (PQisBusy(m_conn->conn))
{
Yield();
this->Sleep(10);
continue;
}
// if resultToRetrieve is given, the nth result will be returned,
// otherwise the last result set will be returned.
// all others are discarded
PGresult *res = PQgetResult(m_conn->conn);
if (!res)
break;
if((PQresultStatus(res) == PGRES_NONFATAL_ERROR) ||
(PQresultStatus(res) == PGRES_FATAL_ERROR) ||
(PQresultStatus(res) == PGRES_BAD_RESPONSE))
{
result = res;
err.SetError(res, &conv);
// Wait for the execution to be finished
// We need to fetch all the results, before sending the error
// message
do
{
if (PQconsumeInput(m_conn->conn) != 1)
{
if (m_cancelled)
{
rc = pgQueryResultEvent::PGQ_EXECUTION_CANCELLED;
// Release the result as the query execution has been cancelled by the
// user
if (result)
PQclear(result);
result = NULL;
if (m_eventOnCancellation)
RaiseEvent(rc);
return rc;
}
goto out_of_consume_input_loop;
}
if ((res = PQgetResult(m_conn->conn)) == NULL)
{
goto out_of_consume_input_loop;
}
// Release the temporary results
PQclear(res);
res = NULL;
if (PQisBusy(m_conn->conn))
{
Yield();
this->Sleep(10);
}
}
while (true);
break;
}
#if defined (__WXMSW__) || (EDB_LIBPQ)
// there should be 2 results in the callable statement - the first is the
// dummy, the second contains our out params.
if (useCallable)
{
PQclear(res);
result = PQiGetOutResult(m_conn->conn);
}
#endif
if (PQresultStatus(res) == PGRES_COPY_IN)
{
rc = PGRES_COPY_IN;
PQputCopyEnd(m_conn->conn, "not supported by pgadmin");
}
if (PQresultStatus(res) == PGRES_COPY_OUT)
{
int copyRc;
char *buf;
int copyRows = 0;
int lastCopyRc = 0;
rc = PGRES_COPY_OUT;
AppendMessage(_("query returned copy data:\n"));
while((copyRc = PQgetCopyData(m_conn->conn, &buf, 1)) >= 0)
{
// Ignore processing the query result, when it has already been
// cancelled by the user
if (m_cancelled)
{
if (!connExecutionCancelled)
{
m_conn->CancelExecution();
connExecutionCancelled = true;
}
continue;
}
if (buf != NULL)
{
if (copyRows < 100)
{
wxString str(buf, conv);
wxCriticalSectionLocker cs(m_criticalSection);
m_queries[m_currIndex]->m_message.Append(str);
}
else if (copyRows == 100)
AppendMessage(_("Query returned more than 100 copy rows, discarding the rest...\n"));
PQfreemem(buf);
}
if (copyRc > 0)
copyRows++;
if (lastCopyRc == 0 && copyRc == 0)
{
Yield();
this->Sleep(10);
}
if (copyRc == 0)
{
if (!PQconsumeInput(m_conn->conn))
{
// It might be the case - it is a result of the
// execution cancellation.
if (m_cancelled)
{
rc = pgQueryResultEvent::PGQ_EXECUTION_CANCELLED;
// Release the result as the query execution has been cancelled by the
// user
if (result)
PQclear(result);
result = NULL;
if (m_eventOnCancellation)
RaiseEvent(rc);
return rc;
}
if (PQstatus(m_conn->conn) == CONNECTION_BAD)
{
err.msg_primary = _("Connection to the database server lost");
rc = pgQueryResultEvent::PGQ_CONN_LOST;
}
else
{
rc = pgQueryResultEvent::PGQ_ERROR_CONSUME_INPUT;
err.msg_primary = wxString(PQerrorMessage(m_conn->conn), conv);
}
return(RaiseEvent(rc));
}
}
lastCopyRc = copyRc;
}
res = PQgetResult(m_conn->conn);
if (!res)
break;
}
resultsRetrieved++;
// Save the current result, as asked by the component
// But - only if the execution is not cancelled
if (!m_cancelled && resultsRetrieved == resultToRetrieve)
{
result = res;
insertedOid = PQoidValue(res);
if (insertedOid && insertedOid != (Oid) - 1)
AppendMessage(wxString::Format(_("query inserted one row with oid %d.\n"), insertedOid));
else
AppendMessage(wxString::Format(wxPLURAL("query result with %d row will be returned.\n", "query result with %d rows will be returned.\n",
PQntuples(result)), PQntuples(result)));
continue;
}
if (lastResult)
{
if (!m_cancelled && PQntuples(lastResult))
AppendMessage(wxString::Format(wxPLURAL("query result with %d row discarded.\n", "query result with %d rows discarded.\n",
PQntuples(lastResult)), PQntuples(lastResult)));
PQclear(lastResult);
}
lastResult = res;
}
out_of_consume_input_loop:
if (m_cancelled)
{
rc = pgQueryResultEvent::PGQ_EXECUTION_CANCELLED;
// Release the result as the query execution has been cancelled by the
// user
if (result)
PQclear(result);
result = NULL;
if (m_eventOnCancellation)
RaiseEvent(rc);
return rc;
}
if (!result)
result = lastResult;
err.SetError(result, &conv);
AppendMessage(wxT("\n"));
rc = PQresultStatus(result);
if (rc == PGRES_TUPLES_OK)
{
dataSet = new pgSet(result, m_conn, conv, m_conn->needColQuoting);
dataSet->MoveFirst();
}
else if (rc == PGRES_COMMAND_OK)
{
char *s = PQcmdTuples(result);
if (*s)
rowsInserted = atol(s);
}
else if (rc == PGRES_FATAL_ERROR ||
rc == PGRES_NONFATAL_ERROR ||
rc == PGRES_BAD_RESPONSE)
{
if (result)
{
AppendMessage(wxString(PQresultErrorMessage(result), conv));
PQclear(result);
result = NULL;
}
else
{
AppendMessage(wxString(PQerrorMessage(m_conn->conn), conv));
}
return(RaiseEvent(rc));
}
insertedOid = PQoidValue(result);
if (insertedOid == (Oid) - 1)
insertedOid = 0;
return(RaiseEvent(1));
}
int pgQueryThread::RaiseEvent(int _retval)
{
#if !defined(PGSCLI)
if (m_caller)
{
pgQueryResultEvent resultEvent(GetId(), m_queries[m_currIndex], m_queries[m_currIndex]->m_eventID);
// client data
resultEvent.SetClientData(m_queries[m_currIndex]->m_data);
resultEvent.SetInt(_retval);
m_caller->AddPendingEvent(resultEvent);
}
#endif
return _retval;
}
void *pgQueryThread::Entry()
{
do
{
if (m_currIndex < (((int)m_queries.GetCount()) - 1))
{
// Create the PGcancel object to enable cancelling the running
// query
m_conn->SetConnCancel();
m_currIndex++;
m_queries[m_currIndex]->m_returnCode = -2;
m_queries[m_currIndex]->m_rowsInserted = -1l;
wxLogSql(wxT("Thread executing query (%d:%s:%d): %s"),
m_currIndex + 1, m_conn->GetHost().c_str(), m_conn->GetPort(),
m_queries[m_currIndex]->m_query.c_str());
// register the notice processor for the current query
m_conn->RegisterNoticeProcessor(m_processor, m_noticeHandler);
// execute the current query now
Execute();
// remove the notice processor now
m_conn->RegisterNoticeProcessor(0, 0);
// reset the PGcancel object
m_conn->ResetConnCancel();
}
if (!m_multiQueries || m_cancelled)
break;
wxThread::Sleep(10);
}
while (true);
return(NULL);
}
int pgQueryThread::DeleteReleasedQueries()
{
int res = 0,
idx = 0;
if (m_queriesLock.TryLock() == wxMUTEX_BUSY)
return res;
for (; idx <= m_currIndex; idx++)
{
if (m_queries[idx]->m_resultSet != NULL)
{
pgSet *set = m_queries[idx]->m_resultSet;
m_queries[idx]->m_resultSet = NULL;
delete set;
set = NULL;
res++;
}
}
m_queriesLock.Unlock();
return res;
}
pgError pgQueryThread::GetResultError(int idx)
{
wxMutexLocker lock(m_queriesLock);
if (idx == -1)
idx = m_currIndex;
return m_queries[idx]->m_err;
}
const wxString &pgBatchQuery::GetErrorMessage()
{
return m_err.msg_primary;
}
pgBatchQuery::~pgBatchQuery()
{
if (m_resultSet)
{
delete m_resultSet;
m_resultSet = NULL;
}
if (m_params)
{
WX_CLEAR_ARRAY((*m_params));
delete m_params;
m_params = NULL;
}
}
bool pgBatchQuery::Release()
{
bool res = false;
if (m_resultSet != NULL)
{
res = true;
pgSet *set = m_resultSet;
m_resultSet = NULL;
if (set)
delete set;
set = NULL;
}
if (m_params)
{
res = true;
WX_CLEAR_ARRAY((*m_params));
delete m_params;
m_params = NULL;
}
return res;
}
pgQueryResultEvent::pgQueryResultEvent(
unsigned long _thrdId, pgBatchQuery *_qry, int _id) :
wxCommandEvent(PGQueryResultEvent, _id), m_thrdId(_thrdId),
m_query(_qry) { }
pgQueryResultEvent::pgQueryResultEvent(const pgQueryResultEvent &_ev)
: wxCommandEvent(_ev), m_thrdId(_ev.m_thrdId), m_query(_ev.m_query) { }
pgParam::pgParam(Oid _type, void *_val, int _len, short _mode)
: m_type(_type), m_val(_val), m_len(_len), m_mode(_mode)
{
switch(m_type)
{
case PGOID_TYPE_CHAR:
case PGOID_TYPE_NAME:
case PGOID_TYPE_TEXT:
case PGOID_TYPE_VARCHAR:
case PGOID_TYPE_CSTRING:
m_format = 0;
default:
m_format = 1;
}
}
// wxString data
pgParam::pgParam(Oid _oid, wxString *_val, wxMBConv *_conv, short _mode)
: m_mode(_mode)
{
if (m_mode == PG_PARAM_OUT || !_val)
{
m_len = 0;
}
else
{
m_len = _val->Len();
}
if (_val)
{
char *str = (char *)malloc(m_len + 1);
if (!_val->IsEmpty() && _mode != PG_PARAM_OUT)
{
strncpy(str,
(const char *)_val->mb_str(
*(_conv != NULL ? _conv : &wxConvLocal)), m_len);
str[m_len] = '\0';
}
else
{
str[0] = '\0';
}
m_val = (void *)(str);
}
else
{
m_val = NULL;
}
m_type = _oid;
// text format
m_format = 0;
}
pgParam::~pgParam()
{
if (m_val)
free(m_val);
m_val = NULL;
}
int pgParam::GetFormat()
{
return m_format;
}

443
db/pgSet.cpp Normal file
View file

@ -0,0 +1,443 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// pgSet.cpp - PostgreSQL ResultSet class
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
// PostgreSQL headers
#include <libpq-fe.h>
// App headers
#include "db/pgSet.h"
#include "db/pgConn.h"
#include "utils/sysLogger.h"
#include "utils/pgDefs.h"
pgSet::pgSet()
: conv(wxConvLibc)
{
conn = 0;
res = 0;
nCols = 0;
nRows = 0;
pos = 0;
}
pgSet::pgSet(PGresult *newRes, pgConn *newConn, wxMBConv &cnv, bool needColQt)
: conv(cnv)
{
needColQuoting = needColQt;
conn = newConn;
res = newRes;
// Make sure we have tuples
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
nCols = 0;
nRows = 0;
pos = 0;
}
else
{
nCols = PQnfields(res);
for (int x = 0; x < nCols + 1; x++)
{
colTypes.Add(wxT(""));
colFullTypes.Add(wxT(""));
colClasses.Add(0);
}
nRows = PQntuples(res);
MoveFirst();
}
}
pgSet::~pgSet()
{
PQclear(res);
}
OID pgSet::ColTypeOid(const int col) const
{
wxASSERT(col < nCols && col >= 0);
return PQftype(res, col);
}
long pgSet::ColTypeMod(const int col) const
{
wxASSERT(col < nCols && col >= 0);
return PQfmod(res, col);
}
long pgSet::GetInsertedCount() const
{
char *cnt = PQcmdTuples(res);
if (!*cnt)
return -1;
else
return atol(cnt);
}
pgTypClass pgSet::ColTypClass(const int col) const
{
wxASSERT(col < nCols && col >= 0);
if (colClasses[col] != 0)
return (pgTypClass)colClasses[col];
wxString typoid = ExecuteScalar(
wxT("SELECT CASE WHEN typbasetype=0 THEN oid else typbasetype END AS basetype\n")
wxT(" FROM pg_type WHERE oid=") + NumToStr(ColTypeOid(col)));
switch (StrToLong(typoid))
{
case PGOID_TYPE_BOOL:
colClasses[col] = PGTYPCLASS_BOOL;
break;
case PGOID_TYPE_INT8:
case PGOID_TYPE_INT2:
case PGOID_TYPE_INT4:
case PGOID_TYPE_OID:
case PGOID_TYPE_XID:
case PGOID_TYPE_TID:
case PGOID_TYPE_CID:
case PGOID_TYPE_FLOAT4:
case PGOID_TYPE_FLOAT8:
case PGOID_TYPE_MONEY:
case PGOID_TYPE_BIT:
case PGOID_TYPE_NUMERIC:
colClasses[col] = PGTYPCLASS_NUMERIC;
break;
case PGOID_TYPE_BYTEA:
case PGOID_TYPE_CHAR:
case PGOID_TYPE_NAME:
case PGOID_TYPE_TEXT:
case PGOID_TYPE_VARCHAR:
colClasses[col] = PGTYPCLASS_STRING;
break;
case PGOID_TYPE_TIMESTAMP:
case PGOID_TYPE_TIMESTAMPTZ:
case PGOID_TYPE_TIME:
case PGOID_TYPE_TIMETZ:
case PGOID_TYPE_INTERVAL:
colClasses[col] = PGTYPCLASS_DATE;
break;
default:
colClasses[col] = PGTYPCLASS_OTHER;
break;
}
return (pgTypClass)colClasses[col];
}
wxString pgSet::ColType(const int col) const
{
wxASSERT(col < nCols && col >= 0);
if (!colTypes[col].IsEmpty())
return colTypes[col];
wxString szSQL, szResult;
szSQL.Printf(wxT("SELECT format_type(oid,NULL) as typname FROM pg_type WHERE oid = %d"), (int)ColTypeOid(col));
szResult = ExecuteScalar(szSQL);
colTypes[col] = szResult;
return szResult;
}
wxString pgSet::ColFullType(const int col) const
{
wxASSERT(col < nCols && col >= 0);
if (!colFullTypes[col].IsEmpty())
return colFullTypes[col];
wxString szSQL, szResult;
szSQL.Printf(wxT("SELECT format_type(oid,%d) as typname FROM pg_type WHERE oid = %d"), (int)ColTypeMod(col), (int)ColTypeOid(col));
szResult = ExecuteScalar(szSQL);
colFullTypes[col] = szResult;
return szResult;
}
int pgSet::ColScale(const int col) const
{
wxASSERT(col < nCols && col >= 0);
// TODO
return 0;
}
wxString pgSet::ColName(const int col) const
{
wxASSERT(col < nCols && col >= 0);
return wxString(PQfname(res, col), conv);
}
int pgSet::ColNumber(const wxString &colname) const
{
int col;
if (needColQuoting)
{
wxString quotedColName = colname;
quotedColName.Replace(wxT("\""), wxT("\"\""));
col = PQfnumber(res, (wxT("\"") + quotedColName + wxT("\"")).mb_str(conv));
}
else
col = PQfnumber(res, colname.mb_str(conv));
if (col < 0)
{
wxLogError(__("Column not found in pgSet: %s"), colname.c_str());
}
return col;
}
bool pgSet::HasColumn(const wxString &colname) const
{
if (needColQuoting)
{
wxString quotedColName = colname;
quotedColName.Replace(wxT("\""), wxT("\"\""));
return (PQfnumber(res, (wxT("\"") + quotedColName + wxT("\"")).mb_str(conv)) < 0 ? false : true);
}
else
return (PQfnumber(res, colname.mb_str(conv)) < 0 ? false : true);
}
char *pgSet::GetCharPtr(const int col) const
{
wxASSERT(col < nCols && col >= 0);
return PQgetvalue(res, pos - 1, col);
}
char *pgSet::GetCharPtr(const wxString &col) const
{
return PQgetvalue(res, pos - 1, ColNumber(col));
}
wxString pgSet::GetVal(const int col) const
{
wxASSERT(col < nCols && col >= 0);
return wxString(GetCharPtr(col), conv);
}
wxString pgSet::GetVal(const wxString &colname) const
{
return GetVal(ColNumber(colname));
}
long pgSet::GetLong(const int col) const
{
wxASSERT(col < nCols && col >= 0);
char *c = PQgetvalue(res, pos - 1, col);
if (c)
return atol(c);
else
return 0;
}
long pgSet::GetLong(const wxString &col) const
{
char *c = PQgetvalue(res, pos - 1, ColNumber(col));
if (c)
return atol(c);
else
return 0;
}
bool pgSet::GetBool(const int col) const
{
wxASSERT(col < nCols && col >= 0);
char *c = PQgetvalue(res, pos - 1, col);
if (c)
{
if (*c == 't' || *c == '1' || !strcmp(c, "on"))
return true;
}
return false;
}
bool pgSet::GetBool(const wxString &col) const
{
return GetBool(ColNumber(col));
}
wxDateTime pgSet::GetDateTime(const int col) const
{
wxASSERT(col < nCols && col >= 0);
wxDateTime dt;
wxString str = GetVal(col);
/* This hasn't just been used. ( Is not infinity ) */
if (!str.IsEmpty())
dt.ParseDateTime(str);
return dt;
}
wxDateTime pgSet::GetDateTime(const wxString &col) const
{
return GetDateTime(ColNumber(col));
}
wxDateTime pgSet::GetDate(const int col) const
{
wxASSERT(col < nCols && col >= 0);
wxDateTime dt;
wxString str = GetVal(col);
/* This hasn't just been used. ( Is not infinity ) */
if (!str.IsEmpty())
dt.ParseDate(str);
return dt;
}
wxDateTime pgSet::GetDate(const wxString &col) const
{
return GetDate(ColNumber(col));
}
double pgSet::GetDouble(const int col) const
{
wxASSERT(col < nCols && col >= 0);
return StrToDouble(GetVal(col));
}
double pgSet::GetDouble(const wxString &col) const
{
return GetDouble(ColNumber(col));
}
wxULongLong pgSet::GetLongLong(const int col) const
{
wxASSERT(col < nCols && col >= 0);
char *c = PQgetvalue(res, pos - 1, col);
if (c)
return atolonglong(c);
else
return 0;
}
wxULongLong pgSet::GetLongLong(const wxString &col) const
{
return GetLongLong(ColNumber(col));
}
OID pgSet::GetOid(const int col) const
{
wxASSERT(col < nCols && col >= 0);
char *c = PQgetvalue(res, pos - 1, col);
if (c)
return (OID)strtoul(c, 0, 10);
else
return 0;
}
OID pgSet::GetOid(const wxString &col) const
{
return GetOid(ColNumber(col));
}
wxString pgSet::ExecuteScalar(const wxString &sql) const
{
return conn->ExecuteScalar(sql);
}
//////////////////////////////////////////////////////////////////
pgSetIterator::pgSetIterator(pgConn *conn, const wxString &qry)
{
set = conn->ExecuteSet(qry);
first = true;
}
pgSetIterator::pgSetIterator(pgSet *s)
{
set = s;
first = true;
}
pgSetIterator::~pgSetIterator()
{
if (set)
delete set;
}
bool pgSetIterator::RowsLeft()
{
if (!set)
return false;
if (first)
{
if (!set->NumRows())
return false;
first = false;
}
else
set->MoveNext();
return !set->Eof();
}
bool pgSetIterator::MovePrev()
{
if (!set)
return false;
set->MovePrevious();
return true;
}

View file

@ -0,0 +1,538 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// ddColumnFigure.cpp - Minimal Composite Figure for a column of a table
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
#include <wx/dcbuffer.h>
#include <wx/pen.h>
// App headers
#include "dd/dditems/figures/ddColumnFigure.h"
#include "dd/dditems/figures/ddTextTableItemFigure.h"
#include "dd/dditems/tools/ddColumnFigureTool.h"
#include "dd/dditems/figures/ddColumnKindIcon.h"
#include "dd/dditems/figures/ddColumnOptionIcon.h"
#include "dd/dditems/utilities/ddDataType.h"
#include "dd/dditems/figures/ddTableFigure.h"
#include "dd/dditems/figures/ddRelationshipItem.h"
void ddColumnFigure::Init(wxString &columnName, ddTableFigure *owner, ddRelationshipItem *sourceFk)
{
setKindId(DDCOLUMNFIGURE);
fkSource = sourceFk;
usedAsFkDestFor = NULL;
setOwnerTable(owner);
deactivateGenFkName(); //initializae by default at not generate auto fk name
columnText = new ddTextTableItemFigure(columnName, dt_null, this);
kindImage = new ddColumnKindIcon(this);
optionImage = new ddColumnOptionIcon(this);
//Initialize displaybox and image coords
basicDisplayBox.SetPosition(0, wxPoint(0, 0));
basicDisplayBox.SetSize( columnText->displayBox().GetSize());
if(kindImage && optionImage)
{
basicDisplayBox.width += kindImage->displayBox().GetSize().GetWidth() + 3;
basicDisplayBox.width += optionImage->displayBox().GetSize().GetWidth() + 3;
columnText->displayBox().x[0] += kindImage->displayBox().GetSize().GetWidth() + 2;
columnText->displayBox().x[0] += optionImage->displayBox().GetSize().GetWidth() + 3;
}
else
{
basicDisplayBox.width += 22; //default value = 1 + 8 + 3 + 8 + 2
columnText->displayBox().x[0] += 22;
}
//Set Value default Attributes
fontAttribute->font().SetPointSize(owner->fontAttribute->font().GetPointSize());
pgAttNumColNumber = -1;
}
ddColumnFigure::ddColumnFigure(wxString &columnName, ddTableFigure *owner, ddRelationshipItem *sourceFk)
{
Init(columnName, owner, sourceFk);
}
//Constructor for creation of existing column from storage
ddColumnFigure::ddColumnFigure(wxString &columnName, ddTableFigure *owner, ddColumnOptionType option, bool isGenFk, bool isPkColumn, wxString colDataType, int p, int s, int ukIdx, ddRelationshipItem *srcFk, ddRelationshipItem *usedAsFk )
{
Init(columnName, owner, srcFk);
setColumnOption(option);
generateFkName = isGenFk;
kindImage->setPrimaryKey(isPkColumn);
int dtype = columnText->dataTypes().Index(colDataType, false);
if(dtype != wxNOT_FOUND)
{
setDataType((ddDataType)dtype);
}
else
{
wxMessageBox(wxString::Format(_("%s column has a non compatible data type (%s).\n%s will now have the default data type."),
columnName.c_str(), colDataType.c_str(), columnName.c_str()));
setDataType((ddDataType)0);
}
columnText->setPrecision(p);
columnText->setScale(s);
kindImage->setUkIndex(ukIdx);
usedAsFkDestFor = usedAsFk;
if(ownerTable)
ownerTable->updateTableSize();
}
ddColumnFigure::~ddColumnFigure()
{
if(columnText)
delete columnText;
if(kindImage)
delete kindImage;
if(optionImage)
delete optionImage;
}
void ddColumnFigure::AddPosForNewDiagram()
{
//Add position for new displaybox at new diagram
hdAttributeFigure::AddPosForNewDiagram();
//Add position to each figure inside this composite figure
if(kindImage)
kindImage->AddPosForNewDiagram();
if(optionImage)
optionImage->AddPosForNewDiagram();
if(columnText)
columnText->AddPosForNewDiagram();
}
void ddColumnFigure::RemovePosOfDiagram(int posIdx)
{
//Remove this position at displaybox of this figure
hdAttributeFigure::RemovePosOfDiagram(posIdx);
//Remove this position at each figure inside this composite figure
if(kindImage)
kindImage->RemovePosOfDiagram(posIdx);
if(optionImage)
optionImage->RemovePosOfDiagram(posIdx);
if(columnText)
columnText->RemovePosOfDiagram(posIdx);
}
void ddColumnFigure::basicMoveBy(int posIdx, int x, int y)
{
hdAbstractFigure::basicMoveBy(posIdx, x, y);
if(kindImage)
kindImage->moveBy(posIdx, x, y);
if(optionImage)
optionImage->moveBy(posIdx, x, y);
columnText->moveBy(posIdx, x, y);
}
void ddColumnFigure::moveTo(int posIdx, int x, int y)
{
hdAbstractFigure::moveTo(posIdx, x, y);
int distance = 0;
if(kindImage)
{
kindImage->moveTo(posIdx, x, y);
distance += kindImage->displayBox().GetSize().GetWidth() + 3;
}
else
{
distance += 11; //value = 8 + 3
}
if(optionImage)
{
optionImage->moveTo(posIdx, x + distance, y);
distance += optionImage->displayBox().GetSize().GetWidth() + 3;
}
else
{
distance += 11; //value = 8 + 3
}
columnText->moveTo(posIdx, x + distance, y);
}
bool ddColumnFigure::containsPoint(int posIdx, int x, int y)
{
bool out = false;
if(columnText->containsPoint(posIdx, x, y))
out = true;
else if(kindImage->containsPoint(posIdx, x, y))
out = true;
else if(optionImage->containsPoint(posIdx, x, y))
out = true;
return out;
}
hdMultiPosRect &ddColumnFigure::getBasicDisplayBox()
{
return basicDisplayBox;
}
void ddColumnFigure::basicDraw(wxBufferedDC &context, hdDrawingView *view)
{
columnText->draw(context, view);
if(kindImage)
kindImage->draw(context, view);
if(optionImage)
optionImage->draw(context, view);
}
void ddColumnFigure::basicDrawSelected(wxBufferedDC &context, hdDrawingView *view)
{
columnText->drawSelected(context, view);
if(kindImage)
kindImage->drawSelected(context, view);
if(optionImage)
optionImage->drawSelected(context, view);
}
hdIFigure *ddColumnFigure::findFigure(int posIdx, int x, int y)
{
hdIFigure *out = NULL;
if(columnText->containsPoint(posIdx, x, y))
out = columnText;
if(kindImage && kindImage->containsPoint(posIdx, x, y))
out = kindImage;
if(optionImage && optionImage->containsPoint(posIdx, x, y))
out = optionImage;
return out;
}
hdITool *ddColumnFigure::CreateFigureTool(hdDrawingView *view, hdITool *defaultTool)
{
return new ddColumnFigureTool(view, this, defaultTool);
}
hdIFigure *ddColumnFigure::getFigureAt(int pos)
{
if(pos == 0)
return (hdIFigure *) kindImage;
if(pos == 1)
return (hdIFigure *) optionImage;
if(pos == 2)
return (hdIFigure *) columnText;
return NULL;
}
ddTableFigure *ddColumnFigure::getOwnerTable()
{
return ownerTable;
}
void ddColumnFigure::setOwnerTable(ddTableFigure *table)
{
ownerTable = table;
}
void ddColumnFigure::displayBoxUpdate()
{
if(kindImage && optionImage)
{
columnText->displayBoxUpdate();
basicDisplayBox.width = columnText->displayBox().GetSize().GetWidth();
basicDisplayBox.width += kindImage->displayBox().GetSize().GetWidth() + 3;
basicDisplayBox.width += optionImage->displayBox().GetSize().GetWidth() + 3;
}
else
{
columnText->displayBoxUpdate();
basicDisplayBox.width += 22; //default value = 1 + 8 + 3 + 8 +2
}
}
bool ddColumnFigure::isNull()
{
return optionImage->getOption() == null;
}
bool ddColumnFigure::isNotNull()
{
return optionImage->getOption() == notnull;
}
bool ddColumnFigure::isNone()
{
return kindImage->isNone();
}
bool ddColumnFigure::isPrimaryKey()
{
return kindImage->isPrimaryKey();
}
void ddColumnFigure::disablePrimaryKey()
{
kindImage->disablePrimaryKey();
}
void ddColumnFigure::enablePrimaryKey()
{
kindImage->enablePrimaryKey();
}
bool ddColumnFigure::isUniqueKey()
{
return kindImage->isUniqueKey();
}
bool ddColumnFigure::isUniqueKey(int uniqueIndex)
{
return kindImage->isUniqueKey(uniqueIndex);
}
bool ddColumnFigure::isPlain()
{
return kindImage->isNone();
}
void ddColumnFigure::setColumnKindToNone()
{
kindImage->disablePrimaryKey();
kindImage->disableUniqueKey();
//Foreign key cannot be changed by set / toggle functions
}
void ddColumnFigure::setRightIconForColumn()
{
kindImage->setRightIconForColumn();
}
void ddColumnFigure::toggleColumnKind(ddColumnType type, hdDrawingView *view)
{
kindImage->toggleColumnKind(type, view);
}
void ddColumnFigure::setColumnOption(ddColumnOptionType type)
{
optionImage->changeIcon(type);
}
wxString &ddColumnFigure::getColumnName(bool datatype)
{
return columnText->getText(datatype);
}
void ddColumnFigure::setPrecision(int n)
{
columnText->setPrecision(n);
}
int ddColumnFigure::getPrecision()
{
return columnText->getPrecision();
}
void ddColumnFigure::setScale(int n)
{
columnText->setScale(n);
}
int ddColumnFigure::getScale()
{
return columnText->getScale();
}
int ddColumnFigure::getUniqueConstraintIndex()
{
return kindImage->getUniqueConstraintIndex();
}
void ddColumnFigure::setUniqueConstraintIndex(int i)
{
kindImage->setUniqueConstraintIndex(i);
}
ddColumnOptionType ddColumnFigure::getColumnOption()
{
return optionImage->getOption();
}
ddDataType ddColumnFigure::getDataType()
{
return columnText->getDataType();
}
void ddColumnFigure::setDataType(ddDataType type)
{
columnText->setDataType(type);
}
void ddColumnFigure::setColumnName(wxString name)
{
columnText->setText(name);
}
bool ddColumnFigure::isForeignKeyFromPk()
{
if(fkSource)
{
return fkSource->isForeignKeyFromPk();
}
else if(usedAsFkDestFor)
{
return usedAsFkDestFor->isForeignKeyFromPk();
}
return false;
}
bool ddColumnFigure::isForeignKey()
{
bool a = isUserCreatedForeignKey();
bool b = isGeneratedForeignKey();
return (a || b);
}
bool ddColumnFigure::isGeneratedForeignKey()
{
return fkSource != NULL;
}
bool ddColumnFigure::isUserCreatedForeignKey()
{
return usedAsFkDestFor != NULL;
}
ddRelationshipItem *ddColumnFigure::getRelatedFkItem()
{
return usedAsFkDestFor;
}
void ddColumnFigure::setAsUserCreatedFk(ddRelationshipItem *relatedFkItem)
{
usedAsFkDestFor = relatedFkItem;
//Now fix icons of kind of column
if(relatedFkItem == NULL)
{
kindImage->setRightIconForColumn();
}
}
bool ddColumnFigure::isFkNameGenerated()
{
return generateFkName;
}
void ddColumnFigure::activateGenFkName()
{
generateFkName = true;
}
void ddColumnFigure::deactivateGenFkName()
{
generateFkName = false;
}
wxString ddColumnFigure::generateSQL(bool forAlterColumn)
{
wxString tmp;
tmp = wxT("\"") + getColumnName() + wxT("\" ");
if(forAlterColumn)
tmp += wxT("TYPE ");
tmp += columnText->getType();
return tmp;
}
ddRelationshipItem *ddColumnFigure::getFkSource()
{
return fkSource;
}
void ddColumnFigure::setTextColour(wxColour colour)
{
columnText->fontColorAttribute->fontColor = colour;
}
//Validate status of column for SQL DDL generation
bool ddColumnFigure::validateColumn(wxString &errors)
{
bool out = true;
wxString tmp;
if(usedAsFkDestFor)
{
//Validate if relationship is marked as identifying but column isn't marked as primary key
wxString sourceTableName = usedAsFkDestFor->sourceTableName();
wxString destTableName = usedAsFkDestFor->destTableName();
wxString fkColName = usedAsFkDestFor->fkColumn->getColumnName(false);
wxString sourceColName = usedAsFkDestFor->original->getColumnName(false);
if(usedAsFkDestFor->relationIsIdentifying() && !this->isPrimaryKey())
{
out = false;
tmp = _("relation between table: ") + sourceTableName + _(" and table: ") + destTableName;
tmp += _(" is marked as identifying but user created foreign key column: ") + fkColName;
tmp += _(" isn't set as primary key \n");
errors.Append(tmp);
}
//Validate if relationship is marked as optional but column is mandatory
if( !usedAsFkDestFor->relationIsMandatory() && this->isNotNull())
{
out = false;
tmp = _("relation between table:") + sourceTableName + _(" and table: ") + destTableName;
tmp += _("is marked as optional but user created foreign key column: ") + fkColName;
tmp += _("is set as mandatory \n");
errors.Append(tmp);
}
//Validate if relationship is marked as mandatory buy column is optional
if( usedAsFkDestFor->relationIsMandatory() && this->isNull())
{
out = false;
tmp = _("relation between table:") + sourceTableName + _(" and table: ") + destTableName +
tmp += _(" is marked as mandatory but user created foreign key column: ") + fkColName;
tmp += _("is set as optional \n");
errors.Append(tmp);
}
//Validate datatype compatibility (right now only exactly same datatype)
if(this->getDataType() != usedAsFkDestFor->original->getDataType())
{
out = false;
tmp = _("User created foreign key column: ") + fkColName + _("have different datatype from source column of relationship: ");
tmp += sourceColName + _(" \n");
errors.Append(tmp);
}
}
return out;
}
//Used by persistence classes
void ddColumnFigure::setFkSource(ddRelationshipItem *newColumn)
{
fkSource = newColumn;
}
//Used by reverse engineering functions
wxString ddColumnFigure::getRawDataType()
{
return columnText->getType(true);
}

View file

@ -0,0 +1,435 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// ddColumnKindIcon.cpp - Figure container for kind of Column Images (fk,pk,uk) only used
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
#include <wx/dcbuffer.h>
#include <wx/pen.h>
// App headers
#include "dd/dditems/figures/ddColumnKindIcon.h"
#include "hotdraw/main/hdDrawingView.h"
#include "dd/dditems/figures/ddColumnFigure.h"
#include "dd/dditems/figures/ddTableFigure.h"
//Images
#include "images/ddforeignkey.pngc"
#include "images/ddprimarykey.pngc"
#include "images/ddunique.pngc"
#include "images/ddprimaryforeignkey.pngc"
#include "images/ddprimarykeyuniquekey.pngc"
#include "images/ddforeignkeyfromuk.pngc"
#include "images/ddprimaryforeignkeyfromuk.pngc"
#include "images/ddforeignkeyuniquekey.pngc"
#include "images/ddforeignkeyuniquekeyfromuk.pngc"
ddColumnKindIcon::ddColumnKindIcon(ddColumnFigure *owner)
{
setKindId(DDCOLUMNKINDICON);
ownerColumn = owner;
// Initialize with an image to allow initial size calculations
icon = wxBitmap(*ddprimarykey_png_img);
iconToDraw = NULL;
getBasicDisplayBox().SetSize(wxSize(getWidth(), getHeight()));
isPk = false;
ukIndex = -1;
//Set Value default Attributes
fontAttribute->font().SetPointSize(owner->fontAttribute->font().GetPointSize());
}
ddColumnKindIcon::~ddColumnKindIcon()
{
}
void ddColumnKindIcon::createMenu(wxMenu &mnu)
{
wxMenuItem *item;
item = mnu.AppendCheckItem(MNU_DDCTPKEY, _("Primary key"));
item->Check(isPrimaryKey());
item->Enable(!getOwnerColumn()->isGeneratedForeignKey());
item = mnu.AppendCheckItem(MNU_DDCTUKEY, _("Unique key"));
item->Check(isUniqueKey());
}
void ddColumnKindIcon::OnGenericPopupClick(wxCommandEvent &event, hdDrawingView *view)
{
toggleColumnKind((ddColumnType)event.GetId(), view);
}
ddColumnFigure *ddColumnKindIcon::getOwnerColumn()
{
return ownerColumn;
}
//if columntype attribute (type) is active then disable, is disable then active.
void ddColumnKindIcon::toggleColumnKind(ddColumnType type, hdDrawingView *view, bool interaction)
{
switch(type)
{
case pk:
//Set Pk attribute
if(isPrimaryKey())
{
disablePrimaryKey();
}
else
{
enablePrimaryKey();
if(getOwnerColumn()->isNull())
{
getOwnerColumn()->setColumnOption(notnull);
}
}
break;
case uk:
if(isUniqueKey())
{
disableUniqueKey();
}
else
{
uniqueConstraintManager(false, view, interaction);
}
break;
default:
break;
}
getBasicDisplayBox().SetSize(wxSize(getWidth(), getHeight()));
}
void ddColumnKindIcon::basicDraw(wxBufferedDC &context, hdDrawingView *view)
{
if(iconToDraw)
{
hdRect copy = displayBox().gethdRect(view->getIdx());
view->CalcScrolledPosition(copy.x, copy.y, &copy.x, &copy.y);
//Adding a yellow circle to increase visibility of uk index
if(isUniqueKey())
{
context.SetBrush(wxBrush(wxColour(wxT("YELLOW")), wxSOLID));
context.SetPen(wxPen(wxColour(wxT("YELLOW"))));
context.DrawCircle(copy.x + 6, copy.y + 7, 4);
}
//Draw icon
context.DrawBitmap(*iconToDraw, copy.GetPosition(), true);
//Draw Uk index if needed
if(isUniqueKey() && ukIndex > 0)
{
wxFont font = fontAttribute->font();
font.SetPointSize(6);
context.SetFont(font);
wxString inumber = wxString::Format(wxT("%d"), (int)ukIndex + 1);
context.DrawText(inumber, copy.x + 4, copy.y + 2);
}
}
}
void ddColumnKindIcon::basicDrawSelected(wxBufferedDC &context, hdDrawingView *view)
{
basicDraw(context, view);
}
int ddColumnKindIcon::getWidth()
{
if(iconToDraw)
return iconToDraw->GetWidth();
else
return 8;
}
int ddColumnKindIcon::getHeight()
{
if(iconToDraw)
return iconToDraw->GetHeight();
else
return 10;
}
/*
ddColumnType ddColumnKindIcon::getKind()
{
return colType;
}
*/
int ddColumnKindIcon::getUniqueConstraintIndex()
{
return ukIndex;
}
void ddColumnKindIcon::setUniqueConstraintIndex(int i)
{
ukIndex = i;
}
bool ddColumnKindIcon::uniqueConstraintManager(bool ukCol, hdDrawingView *view, bool interaction)
{
wxString tmpString;
bool isColUk = true;
if(ukCol) //if already this column kind is Unique Key then convert in a normal column
{
syncUkIndexes();
setUniqueConstraintIndex(-1);
}
else //colType!=uk
{
if(interaction)
{
if(ownerColumn->getOwnerTable()->getUkConstraintsNames().Count() == 0)
{
tmpString = getOwnerColumn()->getOwnerTable()->getTableName();
tmpString.append(wxT("_uk"));
tmpString = wxGetTextFromUser(wxT("Name of new Unique Key constraint:"), tmpString, tmpString, view);
if(tmpString.length() > 0)
{
getOwnerColumn()->getOwnerTable()->getUkConstraintsNames().Add(tmpString);
ukIndex = 0;
}
else
{
setUniqueConstraintIndex(-1);
}
}
else //>0
{
getOwnerColumn()->getOwnerTable()->getUkConstraintsNames().Add(wxString(wxT("Add new Unique Constraint...")));
int i = wxGetSingleChoiceIndex(wxT("Select Unique Key to add Column"), wxT("Select Unique Key to add Column:"), getOwnerColumn()->getOwnerTable()->getUkConstraintsNames(), view);
getOwnerColumn()->getOwnerTable()->getUkConstraintsNames().RemoveAt(getOwnerColumn()->getOwnerTable()->getUkConstraintsNames().Count() - 1);
if(i >= 0)
{
if(i == getOwnerColumn()->getOwnerTable()->getUkConstraintsNames().Count())
{
tmpString = getOwnerColumn()->getOwnerTable()->getTableName();
tmpString.append(wxT("_uk"));
int newIndex = i + 1;
wxString inumber = wxString::Format(wxT("%s%d"), tmpString.c_str(), (int)newIndex);
//Validate new name of uk doesn't exists
while(getOwnerColumn()->getOwnerTable()->getUkConstraintsNames().Index(inumber, false) != -1)
{
newIndex++;
inumber = wxString::Format(wxT("%s%d"), tmpString.c_str(), (int)newIndex);
}
inumber = wxString::Format(wxT("%d"), (int)newIndex);
tmpString.append(inumber);
tmpString = wxGetTextFromUser(wxT("Name of new Unique Key constraint:"), tmpString, tmpString, view);
if(tmpString.length() > 0)
{
getOwnerColumn()->getOwnerTable()->getUkConstraintsNames().Add(tmpString);
ukIndex = i;
}
else
{
setUniqueConstraintIndex(-1);
}
}
else
{
ukIndex = i;
}
}
else
{
setUniqueConstraintIndex(-1);
}
}
}
else //without user interaction
{
}
}
//synchronize observers if this uk column is used as source of fk
setRightIconForColumn();
getOwnerColumn()->getOwnerTable()->updateFkObservers();
if(!isUniqueKey())
return false;
else
return true;
}
//synchronize uk indexes when an uk is change kind from uk to other and other index should be update with that info
void ddColumnKindIcon::syncUkIndexes()
{
ddColumnFigure *col;
bool lastUk = true;
hdIteratorBase *iterator = getOwnerColumn()->getOwnerTable()->figuresEnumerator();
iterator->Next(); //First Figure is Main Rect
iterator->Next(); //Second Figure is Table Title
while(iterator->HasNext())
{
col = (ddColumnFigure *) iterator->Next();
if(col != getOwnerColumn() && (col->getUniqueConstraintIndex() == getOwnerColumn()->getUniqueConstraintIndex()))
{
lastUk = false;
break;
}
}
if(lastUk)
{
//here uks indexes are fixed
iterator->ResetIterator();
iterator->Next(); //First Figure is Main Rect
iterator->Next(); //Second Figure is Table Title
while(iterator->HasNext())
{
col = (ddColumnFigure *) iterator->Next();
if( col->getUniqueConstraintIndex() > getOwnerColumn()->getUniqueConstraintIndex() )
col->setUniqueConstraintIndex(col->getUniqueConstraintIndex() - 1);
}
getOwnerColumn()->getOwnerTable()->getUkConstraintsNames().RemoveAt(getOwnerColumn()->getUniqueConstraintIndex());
getOwnerColumn()->setUniqueConstraintIndex(-1);
}
delete iterator;
}
void ddColumnKindIcon::setRightIconForColumn()
{
//for a pk Column
if(isPrimaryKey())
{
if(isForeignKey())
{
if(getOwnerColumn()->isForeignKeyFromPk())
{
icon = wxBitmap(*ddprimaryforeignkey_png_img);
}
else
{
icon = wxBitmap(*ddprimaryforeignkeyfromuk_png_img);
}
}
else if(isUniqueKey())
{
icon = wxBitmap(*ddprimarykeyuniquekey_png_img);
}
else
{
icon = wxBitmap(*ddprimarykey_png_img);
}
}
else if(isUniqueKey())
{
if(isForeignKey())
{
if(getOwnerColumn()->isForeignKeyFromPk())
{
icon = wxBitmap(*ddforeignkeyuniquekey_png_img);
}
else
{
icon = wxBitmap(*ddforeignkeyuniquekeyfromuk_png_img);
}
}
else if(isPrimaryKey())
{
icon = wxBitmap(*ddprimarykeyuniquekey_png_img);
}
else
{
icon = wxBitmap(*ddunique_png_img);
}
}
else if(isForeignKey() && !isPrimaryKey() && !isUniqueKey() )
{
if(getOwnerColumn()->isForeignKeyFromPk())
{
icon = wxBitmap(*ddforeignkey_png_img);
}
else
{
icon = wxBitmap(*ddforeignkeyfromuk_png_img);
}
}
if(isNone())
{
iconToDraw = NULL;
}
else
{
iconToDraw = &icon;
}
}
bool ddColumnKindIcon::isPrimaryKey()
{
return isPk;
}
bool ddColumnKindIcon::isUniqueKey()
{
return ukIndex >= 0;
}
bool ddColumnKindIcon::isUniqueKey(int uniqueIndex)
{
return (ukIndex == uniqueIndex);
}
bool ddColumnKindIcon::isNone()
{
return !isUniqueKey() && !isPrimaryKey() && !isForeignKey();
}
bool ddColumnKindIcon::isForeignKey()
{
return getOwnerColumn()->isForeignKey();
}
void ddColumnKindIcon::disableUniqueKey()
{
syncUkIndexes(); //prepare to remove uk attribute to this column
ukIndex = -1;
getOwnerColumn()->getOwnerTable()->updateFkObservers();
setRightIconForColumn();
}
void ddColumnKindIcon::disablePrimaryKey()
{
getOwnerColumn()->getOwnerTable()->prepareForDeleteFkColumn(getOwnerColumn());
isPk = false;
getOwnerColumn()->getOwnerTable()->updateFkObservers();
setRightIconForColumn();
}
void ddColumnKindIcon::enablePrimaryKey()
{
isPk = true;
getOwnerColumn()->getOwnerTable()->updateFkObservers();
setRightIconForColumn();
}
//Only used for figures created from storage
void ddColumnKindIcon::setPrimaryKey(bool value)
{
isPk = value;
setRightIconForColumn();
}
//Only used for figures created from storage
void ddColumnKindIcon::setUkIndex(int ukIdx)
{
ukIndex = ukIdx;
}

View file

@ -0,0 +1,128 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// ddColumnOptionIcon.cpp - Part of composite column figure, allow to choose between: not null and null
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
#include <wx/dcbuffer.h>
#include <wx/pen.h>
// App headers
#include "dd/dditems/figures/ddColumnOptionIcon.h"
#include "hotdraw/main/hdDrawingView.h"
#include "dd/dditems/figures/ddColumnFigure.h"
//Images
#include "images/ddnull.pngc"
#include "images/ddnotnull.pngc"
ddColumnOptionIcon::ddColumnOptionIcon(ddColumnFigure *owner)
{
setKindId(DDCOLUMNOPTIONICON);
ownerColumn = owner;
colOption = null;
icon = wxBitmap(*ddnull_png_img);
iconToDraw = &icon;
getBasicDisplayBox().SetSize(wxSize(getWidth(), getHeight()));
//Set Value default Attributes
fontAttribute->font().SetPointSize(owner->fontAttribute->font().GetPointSize());
}
ddColumnOptionIcon::~ddColumnOptionIcon()
{
}
void ddColumnOptionIcon::createMenu(wxMenu &mnu)
{
wxMenuItem *item;
item = mnu.AppendCheckItem(MNU_COLNULL, _("NULL"));
item->Check(colOption == null);
item->Enable(!getOwnerColumn()->isGeneratedForeignKey());
item = mnu.AppendCheckItem(MNU_COLNOTNULL, _("Not NULL"));
item->Check(colOption == notnull);
item->Enable(!getOwnerColumn()->isGeneratedForeignKey());
}
void ddColumnOptionIcon::OnGenericPopupClick(wxCommandEvent &event, hdDrawingView *view)
{
changeIcon((ddColumnOptionType)event.GetId());
}
void ddColumnOptionIcon::changeIcon(ddColumnOptionType type)
{
colOption = type;
switch(type)
{
case MNU_COLNULL:
icon = wxBitmap(*ddnull_png_img);
if(getOwnerColumn()->isPrimaryKey())
{
if(getOwnerColumn()->isForeignKey() || getOwnerColumn()->isUniqueKey())
{
getOwnerColumn()->toggleColumnKind(pk); //remove pk attribute because column now is null
getOwnerColumn()->setRightIconForColumn();
}
else
{
getOwnerColumn()->disablePrimaryKey();
}
}
break;
case MNU_COLNOTNULL:
icon = wxBitmap(*ddnotnull_png_img);
break;
}
getBasicDisplayBox().SetSize(wxSize(getWidth(), getHeight()));
}
void ddColumnOptionIcon::basicDraw(wxBufferedDC &context, hdDrawingView *view)
{
if(iconToDraw)
{
hdRect copy = displayBox().gethdRect(view->getIdx());
view->CalcScrolledPosition(copy.x, copy.y, &copy.x, &copy.y);
context.DrawBitmap(*iconToDraw, copy.GetPosition(), true);
}
}
void ddColumnOptionIcon::basicDrawSelected(wxBufferedDC &context, hdDrawingView *view)
{
basicDraw(context, view);
}
int ddColumnOptionIcon::getWidth()
{
if(iconToDraw)
return iconToDraw->GetWidth();
else
return 8;
}
int ddColumnOptionIcon::getHeight()
{
if(iconToDraw)
return iconToDraw->GetHeight();
else
return 10;
}
ddColumnOptionType ddColumnOptionIcon::getOption()
{
return colOption;
}
ddColumnFigure *ddColumnOptionIcon::getOwnerColumn()
{
return ownerColumn;
}

View file

@ -0,0 +1,930 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// ddRelationshipFigure.cpp - Figure to draw foreign keys between tables.
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
#include <wx/dcbuffer.h>
// App headers
#include "dd/dditems/figures/ddRelationshipFigure.h"
#include "dd/dditems/figures/ddRelationshipItem.h"
#include "dd/ddmodel/ddDrawingEditor.h"
#include "dd/ddmodel/ddDatabaseDesign.h"
#include "dd/dditems/utilities/ddDataType.h"
#include "dd/dditems/utilities/ddSelectKindFksDialog.h"
#include "hotdraw/utilities/hdRemoveDeleteDialog.h"
ddRelationshipFigure::ddRelationshipFigure():
hdLineConnection()
{
constraintName = wxEmptyString;
setKindId(DDRELATIONSHIPFIGURE);
fkFromPk = true;
fkMandatory = true;
fkOneToMany = true;
fkIdentifying = false;
matchSimple = false;
ukIndex = -1;
disconnectedEndTable = NULL;
paintingFkColumns = false;
//DEFERRABLE, NOT DEFERRABLE, INITIALLY IMMEDIATE, INITIALLY DEFERRED can be added in a future
onUpdateAction = FK_ACTION_NO;
onDeleteAction = FK_ACTION_NO;
enablePopUp();
}
ddRelationshipFigure::ddRelationshipFigure(int posIdx, hdIFigure *figure1, hdIFigure *figure2):
hdLineConnection(posIdx, figure1, figure2)
{
enablePopUp();
}
ddRelationshipFigure::~ddRelationshipFigure()
{
columnsHashMap::iterator it;
ddRelationshipItem *item;
for (it = chm.begin(); it != chm.end(); ++it)
{
wxString key = it->first;
item = it->second;
delete item;
}
chm.clear();
}
wxString ddRelationshipFigure::getConstraintName()
{
return constraintName;
}
//This function avoid a bug with recursive indentifying relationships when
//a column is removed from pk or delete.
void ddRelationshipFigure::prepareFkForDelete(ddColumnFigure *column)
{
ddTableFigure *endTable = (ddTableFigure *) getEndFigure();
ddRelationshipItem *fkColumnRelItem;
columnsHashMap::iterator it;
for (it = chm.begin(); it != chm.end(); ++it)
{
fkColumnRelItem = it->second;
if(fkColumnRelItem->original == column)
fkColumnRelItem->original = NULL;
}
//go recursive to next table
if(endTable)
endTable->prepareForDeleteFkColumn(column);
}
void ddRelationshipFigure::updateForeignKey()
{
if(getEndFigure() && getStartFigure() && getStartFigure()->ms_classInfo.IsKindOf(&ddTableFigure::ms_classInfo) && getEndFigure()->ms_classInfo.IsKindOf(&ddTableFigure::ms_classInfo))
{
ddTableFigure *startTable = (ddTableFigure *) getStartFigure();
ddTableFigure *endTable = (ddTableFigure *) getEndFigure();
ddColumnFigure *col;
ddRelationshipItem *fkColumnRelItem;
//STEP 0: Look for changes on source columns names and short table names.
//Before iterate over all columns of source table look if any column used at relationship
//and changed their name and updated it when needed
columnsHashMap::iterator it;
for (it = chm.begin(); it != chm.end(); ++it)
{
wxString key = it->first;
fkColumnRelItem = it->second;
if(fkColumnRelItem->original != NULL) //original column wasn't mark for delete.
{
//if autonaming is set on this item and column name at original source column of fk have been changed
if(!fkColumnRelItem->original->getColumnName(false).IsSameAs(fkColumnRelItem->originalStartColName, false))
{
chm[fkColumnRelItem->original->getColumnName(false)] = fkColumnRelItem;
chm[fkColumnRelItem->originalStartColName] = NULL;
chm.erase(it);
fkColumnRelItem->syncAutoFkName();
break;
}
}
}
//STEP 1: Look at all source table columns add and delete fk
hdIteratorBase *iterator = startTable->figuresEnumerator();
iterator->Next(); //first figure is main rect
iterator->Next(); //second figure is table title
while(iterator->HasNext())
{
col = (ddColumnFigure *) iterator->Next();
if(fkFromPk) //fk is generated from a pk column: add new fk/pk(s) column(s) from source fk table to destination
{
//STEP 1.1a: Add fk columns from table source pk if not exists using same name
it = chm.find(col->getColumnName());
bool NotFound = it == chm.end();
if( col->isPrimaryKey() && NotFound )
{
fkColumnRelItem = new ddRelationshipItem(this, col, endTable, (fkMandatory ? notnull : null), (fkIdentifying ? pk : none) );
chm[col->getColumnName()] = fkColumnRelItem; //hashmap key will be original table name always
endTable->addColumn(0, fkColumnRelItem->fkColumn);
updateConnection(0);
}
//STEP 1.2a: Delete old Fk columns not pk now or deleted from source fk table.
//This part of code (repeat) is a hack cause every time a column is delete hashmap is modified inside and becomes invalid iterator at that loop
bool repeat;
do
{
repeat = false;
for (it = chm.begin(); it != chm.end(); ++it)
{
wxString key = it->first;
fkColumnRelItem = it->second;
if( fkColumnRelItem->original == NULL || !fkColumnRelItem->original->isPrimaryKey() || !startTable->includes(fkColumnRelItem->original) ) //order matters fkColumnRelItem->original==NULL short circuit evaluation should be first
{
if(fkColumnRelItem->isAutomaticallyGenerated()) //don't remove from fk_dest table fk column created from existing column, just mark now as not foreign key
{
fkColumnRelItem->getDestinationTable()->removeColumn(0, fkColumnRelItem->fkColumn);
}
else
{
fkColumnRelItem->fkColumn->setAsUserCreatedFk(NULL);
}
chm.erase(it);
delete fkColumnRelItem;
repeat = true;
updateConnection(0);
}
if (repeat)
break;
}
}
while(repeat);
}
else //fk is generated from a uk column: add new fk/pk(s) column(s) from source fk table to destination
{
//STEP 1.1b: Add fk columns from table source uk if not exists
columnsHashMap::iterator it = chm.find(col->getColumnName());
bool NotFound = it == chm.end();
if( col->isUniqueKey(ukIndex) && NotFound )
{
fkColumnRelItem = new ddRelationshipItem(this, col, endTable, (fkMandatory ? notnull : null), (fkIdentifying ? pk : none) );
chm[col->getColumnName()] = fkColumnRelItem; //hashmap key will be original table name always
endTable->addColumn(0, fkColumnRelItem->fkColumn);
updateConnection(0);
}
//STEP 1.2b: Delete old Fk columns not pk now or deleted from source fk table.
//This part of code (repeat) is a hack cause every time a column is delete hashmap is modified inside and becomes invalid iterator at that loop
bool repeat;
do
{
repeat = false;
for (it = chm.begin(); it != chm.end(); ++it)
{
wxString key = it->first;
fkColumnRelItem = it->second;
if( fkColumnRelItem->original == NULL || !fkColumnRelItem->original->isUniqueKey(ukIndex) || !startTable->includes(fkColumnRelItem->original) ) //order matters fkColumnRelItem->original==NULL short circuit evaluation should be first
{
if(fkColumnRelItem->isAutomaticallyGenerated()) //don't remove from fk_dest table fk column created from existing column, just mark now as not foreign key
{
fkColumnRelItem->getDestinationTable()->removeColumn(0, fkColumnRelItem->fkColumn);
}
else
{
fkColumnRelItem->fkColumn->setAsUserCreatedFk(NULL);
}
chm.erase(it);
delete fkColumnRelItem;
repeat = true;
updateConnection(0);
}
if (repeat)
break;
}
}
while(repeat);
}
}
delete iterator;
//Now if relationship is an identifying one, I should alert all observers of my observer to update it.
if(fkIdentifying)
{
if(endTable)
endTable->updateFkObservers();
}
}
else
{
wxMessageBox(wxT("Error invalid kind of start figure at relationship"), wxT("Error invalid kind of start figure at relationship"), wxICON_ERROR | wxOK);
}
}
void ddRelationshipFigure::createMenu(wxMenu &mnu)
{
wxMenu *submenu;
wxMenuItem *item;
wxString tmp;
submenu = new wxMenu(_("Select one"));
tmp = _("Foreign Key From");
mnu.AppendSubMenu(submenu, tmp);
item = submenu->AppendCheckItem(MNU_FKEYFROMPKEY, _("Primary Key"));
item->Check(fkFromPk);
if(getStartFigure())
{
ddTableFigure *startTable = (ddTableFigure *) getStartFigure();
int i, last = startTable->getUkConstraintsNames().Count();
int eventID; //Hack to allow multiple submenu items for select uk as fk origin
for(i = 0; i < last; i++)
{
eventID = MNU_FKEYFROMUKEYBASE + i;
tmp = _("Unique Key: ");
tmp += startTable->getUkConstraintsNames()[i];
item = submenu->AppendCheckItem(eventID, tmp );
item->Check(!fkFromPk && ukIndex == i);
}
}
mnu.AppendSeparator();
//disable right now item = mnu.AppendCheckItem(MNU_FKEYCUSTOMMAPPING, _("Foreign Key Columns Custom Mapping"));
item = mnu.AppendCheckItem(MNU_MANDATORYRELATIONSHIP, _("Mandatory relationship kind"));
item->Check(fkMandatory);
item = mnu.AppendCheckItem(MNU_IDENTIFYINGRELATIONSHIP, _("Identifying relationship"));
item->Check(fkIdentifying);
mnu.AppendSeparator();
item = mnu.AppendCheckItem(MNU_1MRELATIONSHIP, _("1:M"));
item->Check(fkOneToMany);
mnu.AppendSeparator();
mnu.Append(MNU_FKCONSTRAINTNAME, _("Foreign Key Constraint Name"));
submenu = new wxMenu(_("Select one"));
item = submenu->AppendCheckItem(MNU_FKMATCHTYPESIMPLE, _("Type Simple"));
item->Check(matchSimple);
item = submenu->AppendCheckItem(MNU_FKMATCHTYPEFULL, _("Type Full"));
item->Check(!matchSimple);
tmp = _("Match Type ");
if(matchSimple)
tmp += _("[ Simple ]");
else
tmp += _("[ Full ]");
mnu.AppendSubMenu(submenu, tmp);
tmp = _("On Delete ");
submenu = new wxMenu(_("Select one"));
item = submenu->AppendCheckItem(MNU_FKONDELETENOACTION, _("No Action"));
item->Check(onDeleteAction == FK_ACTION_NO);
if(onDeleteAction == FK_ACTION_NO)
tmp += _("[ No Action ]");
item = submenu->AppendCheckItem(MNU_FKONDELETERESTRICT, _("Restrict"));
item->Check(onDeleteAction == FK_RESTRICT);
if(onDeleteAction == FK_RESTRICT)
tmp += _("[ Restrict ]");
item = submenu->AppendCheckItem(MNU_FKONDELETECASCADE, _("Cascade"));
item->Check(onDeleteAction == FK_CASCADE);
if(onDeleteAction == FK_CASCADE)
tmp += _("[ Cascade ]");
item = submenu->AppendCheckItem(MNU_FKONDELETESETNULL, _("Set Null"));
item->Check(onDeleteAction == FK_SETNULL);
if(onDeleteAction == FK_SETNULL)
tmp += _("[ Set Null ]");
item = submenu->AppendCheckItem(MNU_FKONDELETESETDEFAULT, _("Set Default"));
item->Check(onDeleteAction == FK_SETDEFAULT);
if(onDeleteAction == FK_SETDEFAULT)
tmp += _("[ Set Default ]");
mnu.AppendSubMenu(submenu, tmp);
tmp = _("On Update ");
submenu = new wxMenu(_("Select one"));
item = submenu->AppendCheckItem(MNU_FKONUPDATENOACTION, _("No Action"));
item->Check(onUpdateAction == FK_ACTION_NO);
if(onUpdateAction == FK_ACTION_NO)
tmp += _("[ No Action ]");
item = submenu->AppendCheckItem(MNU_FKONUPDATERESTRICT, _("Restrict"));
item->Check(onUpdateAction == FK_RESTRICT);
if(onUpdateAction == FK_RESTRICT)
tmp += _("[ Restrict ]");
item = submenu->AppendCheckItem(MNU_FKONUPDATECASCADE, _("Cascade"));
item->Check(onUpdateAction == FK_CASCADE);
if(onUpdateAction == FK_CASCADE)
tmp += _("[ Cascade ]");
item = submenu->AppendCheckItem(MNU_FKONUPDATESETNULL, _("Set Null"));
item->Check(onUpdateAction == FK_SETNULL);
if(onUpdateAction == FK_SETNULL)
tmp += _("[ Set Null ]");
item = submenu->AppendCheckItem(MNU_FKONUPDATESETDEFAULT, _("Set Default"));
item->Check(onUpdateAction == FK_SETDEFAULT);
if(onUpdateAction == FK_SETDEFAULT)
tmp += _("[ Set Default ]");
mnu.AppendSubMenu(submenu, tmp);
mnu.AppendSeparator();
mnu.Append(MNU_DELETERELATIONSHIP, _("Delete Relationship..."));
};
void ddRelationshipFigure::OnGenericPopupClick(wxCommandEvent &event, hdDrawingView *view)
{
int answer;
ddTableFigure *startTable = NULL;
ddTableFigure *endTable = NULL;
wxTextEntryDialog *nameDialog = NULL;
hdRemoveDeleteDialog *delremDialog = NULL;
ddSelectKindFksDialog *mappingDialog = NULL;
wxString tmpString;
switch(event.GetId())
{
case MNU_MANDATORYRELATIONSHIP:
fkMandatory = !fkMandatory;
if(fkMandatory)
{
setLinePen(wxPen(*wxBLACK_PEN));
setOptionAtForeignKeys(notnull);
}
else
{
fkIdentifying = false;
setLinePen(wxPen(*wxBLACK, 1, wxSHORT_DASH));
setOptionAtForeignKeys(null);
}
view->notifyChanged();
break;
case MNU_IDENTIFYINGRELATIONSHIP:
fkMandatory = true;
setLinePen(wxPen(*wxBLACK_PEN));
fkIdentifying = !fkIdentifying;
fkOneToMany = true;
updatePkAtFkCols();
view->notifyChanged();
break;
case MNU_FKCONSTRAINTNAME:
startTable = (ddTableFigure *) getStartFigure();
endTable = (ddTableFigure *) getEndFigure();
if(constraintName.IsEmpty() && startTable && endTable )
{
constraintName = endTable->getTableName();
constraintName += _("_");
constraintName += startTable->getTableName();
}
nameDialog = new wxTextEntryDialog(view, wxT("Change Constraint Name"), wxT("Constraint Name"), constraintName );
answer = nameDialog->ShowModal();
if (answer == wxID_OK)
{
tmpString = nameDialog->GetValue();
constraintName = tmpString;
view->notifyChanged();
}
delete nameDialog;
break;
case MNU_FKMATCHTYPEFULL:
matchSimple = false;
view->notifyChanged();
break;
case MNU_FKMATCHTYPESIMPLE:
matchSimple = true;
view->notifyChanged();
break;
case MNU_FKONDELETENOACTION:
onDeleteAction = FK_ACTION_NO;
view->notifyChanged();
break;
case MNU_FKONDELETERESTRICT:
onDeleteAction = FK_RESTRICT;
view->notifyChanged();
break;
case MNU_FKONDELETECASCADE:
onDeleteAction = FK_CASCADE;
view->notifyChanged();
break;
case MNU_FKONDELETESETNULL:
onDeleteAction = FK_SETNULL;
view->notifyChanged();
break;
case MNU_FKONDELETESETDEFAULT:
onDeleteAction = FK_SETDEFAULT;
view->notifyChanged();
break;
case MNU_FKONUPDATENOACTION:
onUpdateAction = FK_ACTION_NO;
view->notifyChanged();
break;
case MNU_FKONUPDATERESTRICT:
onUpdateAction = FK_RESTRICT;
view->notifyChanged();
break;
case MNU_FKONUPDATECASCADE:
onUpdateAction = FK_CASCADE;
view->notifyChanged();
break;
case MNU_FKONUPDATESETNULL:
onUpdateAction = FK_SETNULL;
view->notifyChanged();
break;
case MNU_FKONUPDATESETDEFAULT:
onUpdateAction = FK_SETDEFAULT;
view->notifyChanged();
break;
case MNU_1MRELATIONSHIP:
fkOneToMany = !fkOneToMany;
view->notifyChanged();
break;
case MNU_DELETERELATIONSHIP:
if(getStartFigure() && getEndFigure())
{
ddTableFigure *t1 = (ddTableFigure *)getStartFigure();
ddTableFigure *t2 = (ddTableFigure *)getEndFigure();
//Relationship can be delete only NOT REMOVED
delremDialog = new hdRemoveDeleteDialog(
wxString::Format(_("Are you sure you wish to delete relationship between tables %s and %s?"),
t1->getTableName().c_str(), t2->getTableName().c_str()),
_("Delete relationship?"),
(wxScrolledWindow *)view,
false);
answer = delremDialog->ShowModal();
ddDrawingEditor *editor = (ddDrawingEditor *) view->editor();
if (answer == DD_DELETE)
{
editor->removeFromAllSelections(this);
removeForeignKeys();
disconnectStart();
disconnectEnd();
//Hack to autodelete relationship
ddRelationshipFigure *r = this;
if(r)
editor->deleteModelFigure(r);
editor->getDesign()->refreshBrowser();
view->notifyChanged();
}
else if(answer == DD_REMOVE)
{
editor->getExistingDiagram(view->getIdx())->removeFromSelection(this);
editor->getExistingDiagram(view->getIdx())->remove(this);
view->notifyChanged();
}
delete delremDialog;
}
break;
case MNU_FKEYFROMPKEY:
fkFromPk = true;
updateForeignKey();
view->notifyChanged();
break;
case MNU_FKEYCUSTOMMAPPING:
//disable right now
/*
mappingDialog = new ddSelectKindFksDialog(view,this);
mappingDialog->ShowModal();
delete mappingDialog;
*/
break;
default:
//Hack to allow multiple selection of Uk in submenu
answer = event.GetId();
if( answer >= MNU_FKEYFROMUKEYBASE)
{
fkFromPk = false;
ukIndex = answer - MNU_FKEYFROMUKEYBASE;
updateForeignKey();
view->notifyChanged();
}
break;
}
}
bool ddRelationshipFigure::getIdentifying()
{
return fkIdentifying;
}
bool ddRelationshipFigure::getOneToMany()
{
return fkOneToMany;
}
bool ddRelationshipFigure::getMandatory()
{
return fkMandatory;
}
// relationship is observed by several tables at same time, one is the
// owner (start connector table) others are just observers of that
// relationship (end connectors table)
void ddRelationshipFigure::connectEnd(hdIConnector *end, hdDrawingView *view)
{
ddSelectKindFksDialog *mappingDialog = NULL;
hdLineConnection::connectEnd(end);
if(view)
view->Refresh();
//Hack to allow abort of mapping of Fk
int answer = -1; //By default
//If connecte destination and start figure
if(getEndFigure() && getStartFigure())
{
mappingDialog = new ddSelectKindFksDialog(view, this);
answer = mappingDialog->ShowModal();
delete mappingDialog;
if(answer == wxID_OK)
{
if(view)
{
view->AcceptsFocus();
view->SetFocus();
}
updateForeignKey();
}
}
//Is Start is connected Tell to check consistency
if(getStartFigure())
{
getStartTable()->setSelectFkDestMode(false);
ddDrawingEditor *editor = (ddDrawingEditor *) view->editor();
editor->checkAllDigramsRelConsistency();
}
//Hack to Abort Connection because user press cancel button at fk mapping dialog
if (answer == wxID_CANCEL)
{
//By disconnecting both sides I force to delete relationship at ConnectionCreationTool
getStartTable()->setSelectFkDestMode(false);
this->disconnectStart();
this->disconnectEnd();
}
}
bool ddRelationshipFigure::isForeignKeyFromPk()
{
return fkFromPk;
}
void ddRelationshipFigure::setFkFrom(bool primaryKey, int useUkIndex, bool issueUpdateFk)
{
if(useUkIndex >= 0)
{
fkFromPk = false;
ukIndex = useUkIndex;
}
else
{
fkFromPk = true;
ukIndex = -1;
}
if(issueUpdateFk)
updateForeignKey();
}
void ddRelationshipFigure::connectStart(hdIConnector *start, hdDrawingView *view)
{
hdLineConnection::connectStart(start);
if(getEndFigure() && getStartFigure())
updateForeignKey();
}
void ddRelationshipFigure::disconnectStart(hdDrawingView *view)
{
paintingFkColumns = false;
changeFkOSTextColor( *wxBLACK, *wxBLACK, true );
disconnectedEndTable = (ddTableFigure *) getEndFigure();
removeForeignKeys();
hdLineConnection::disconnectStart();
}
void ddRelationshipFigure::disconnectEnd(hdDrawingView *view)
{
paintingFkColumns = false;
changeFkOSTextColor( *wxBLACK, *wxBLACK, true );
disconnectedEndTable = (ddTableFigure *) getEndFigure();
hdLineConnection::disconnectEnd();
removeForeignKeys();
}
void ddRelationshipFigure::addExistingColumnFk(ddColumnFigure *startTablesourceCol, wxString destColumn)
{
ddTableFigure *endTable = (ddTableFigure *) getEndFigure();
ddRelationshipItem *fkColumnRelItem;
ddColumnFigure *endTablesourceCol = endTable->getColumnByName(destColumn);
//Create a new relationship item but with an existing column for fk at destination table
if(endTablesourceCol)
{
fkColumnRelItem = new ddRelationshipItem(this, startTablesourceCol, endTable, (fkMandatory ? notnull : null), (fkIdentifying ? pk : noaction), endTablesourceCol);
//Mark it as Custom Fk (fk from existing column not an automatic generated)
endTablesourceCol->setAsUserCreatedFk(fkColumnRelItem);
chm[startTablesourceCol->getColumnName()] = fkColumnRelItem; //hashmap key will be original table name always
updateConnection(0);
}
}
void ddRelationshipFigure::removeForeignKeys()
{
if(disconnectedEndTable)
{
columnsHashMap::iterator it;
ddRelationshipItem *fkColumnRelItem;
//This part of code (repeat) is a hack cause every time a column is delete hashmap is modified inside and becomes invalid iterator at that loop
bool repeat;
do
{
repeat = false;
for( it = chm.begin(); it != chm.end(); ++it )
{
wxString key = it->first;
fkColumnRelItem = it->second;
if(fkColumnRelItem->getDestinationTable()->includes(fkColumnRelItem->fkColumn))
{
//Remove fk column only if that column is automatically generated
if(fkColumnRelItem->isAutomaticallyGenerated())
{
fkColumnRelItem->getDestinationTable()->removeColumn(0, fkColumnRelItem->fkColumn);
} //is an existing column use as fk
else
{
//Mark as existing column not used as foreign key destination
fkColumnRelItem->fkColumn->setAsUserCreatedFk(NULL);
}
chm.erase(it);
delete fkColumnRelItem;
repeat = true;
break;
}
}
}
while(repeat);
chm.clear();
disconnectedEndTable = NULL;
}
}
void ddRelationshipFigure::setOptionAtForeignKeys(ddColumnOptionType type)
{
columnsHashMap::iterator it;
ddRelationshipItem *item;
for (it = chm.begin(); it != chm.end(); ++it)
{
wxString key = it->first;
item = it->second;
if(item->isAutomaticallyGenerated())
item->fkColumn->setColumnOption(type);
}
}
void ddRelationshipFigure::updatePkAtFkCols()
{
bool changed = false;
columnsHashMap::iterator it;
ddRelationshipItem *item;
for (it = chm.begin(); it != chm.end(); ++it)
{
wxString key = it->first;
item = it->second;
if(item->isAutomaticallyGenerated()) //only update fk status at fk NOT created from an existing column
{
if(fkIdentifying)
{
item->fkColumn->enablePrimaryKey();
}
else
{
item->fkColumn->disablePrimaryKey();
}
changed = true;
}
}
if(changed) //set as identifying relationship (hierarchy)
{
ddTableFigure *table = (ddTableFigure *) getEndFigure();
table->updateFkObservers();
}
}
wxString ddRelationshipFigure::generateSQL(wxString schemaName)
{
wxString tmp;
if(chm.size() > 0)
{
tmp = wxT("\nALTER TABLE ");
if(!schemaName.IsEmpty())
tmp += wxT("\"") + schemaName + wxT("\".");
tmp += wxT("\"") + ((ddTableFigure *)getEndFigure())->getTableName() + wxT("\"") ;
tmp += wxT(" ADD ");
if(!constraintName.IsEmpty())
{
tmp += wxT("CONSTRAINT \"") + constraintName + wxT("\" ");
}
tmp += wxT("FOREIGN KEY ( ");
columnsHashMap::iterator it, end;
ddRelationshipItem *item;
for( it = chm.begin(); it != chm.end(); ++it )
{
wxString key = it->first;
item = it->second;
tmp += wxT("\"") + item->fkColumn->getColumnName() + wxT("\"");
end = it;
end++;
if(end != chm.end())
{
tmp += wxT(" , ");
}
else
{
tmp += wxT(" )");
}
}
tmp += wxT(" REFERENCES ");
if(!schemaName.IsEmpty())
tmp += wxT("\"") + schemaName + _("\".");
tmp += wxT("\"") + ((ddTableFigure *)getStartFigure())->getTableName() + wxT("\"") ;
tmp += wxT(" ( ");
for( it = chm.begin(); it != chm.end(); ++it )
{
wxString key = it->first;
item = it->second;
tmp += wxT("\"") + item->original->getColumnName() + wxT("\"");
end = it;
end++;
if(end != chm.end())
{
tmp += wxT(" , ");
}
else
{
tmp += wxT(" )");
}
}
if(matchSimple)
tmp += wxT(" MATCH SIMPLE ");
else
tmp += wxT(" MATCH FULL ");
tmp += wxT(" ON DELETE ");
switch(onDeleteAction)
{
case FK_ACTION_NO:
tmp += wxT(" NO ACTION ");
break;
case FK_RESTRICT:
tmp += wxT(" RESTRICT ");
break;
case FK_CASCADE:
tmp += wxT(" CASCADE ");
break;
case FK_SETNULL:
tmp += wxT(" SET NULL ");
break;
case FK_SETDEFAULT:
tmp += wxT(" SET DEFAULT ");
break;
}
tmp += wxT(" ON UPDATE ");
switch(onUpdateAction)
{
case FK_ACTION_NO:
tmp += wxT(" NO ACTION ");
break;
case FK_RESTRICT:
tmp += wxT(" RESTRICT ");
break;
case FK_CASCADE:
tmp += wxT(" CASCADE ");
break;
case FK_SETNULL:
tmp += wxT(" SET NULL ");
break;
case FK_SETDEFAULT:
tmp += wxT(" SET DEFAULT ");
break;
}
tmp += _(";\n");
}
return tmp;
}
ddTableFigure *ddRelationshipFigure::getStartTable()
{
return (ddTableFigure *) getStartFigure();
}
ddTableFigure *ddRelationshipFigure::getEndTable()
{
return (ddTableFigure *) getEndFigure();
}
void ddRelationshipFigure::basicDrawSelected(wxBufferedDC &context, hdDrawingView *view)
{
hdLineConnection::basicDrawSelected(context, view);
if(getEndFigure() && getStartFigure())
{
paintingFkColumns = true;
changeFkOSTextColor(wxColour(255, 0, 0), wxColour(255, 0, 0));
}
}
void ddRelationshipFigure::basicDraw(wxBufferedDC &context, hdDrawingView *view)
{
hdLineConnection::basicDraw(context, view);
if(getEndFigure() && getStartFigure() && paintingFkColumns)
{
paintingFkColumns = false;
changeFkOSTextColor( *wxBLACK, *wxBLACK, true );
}
}
void ddRelationshipFigure::changeFkOSTextColor(wxColour originalColour, wxColour fkColour, bool reset)
{
columnsHashMap::iterator it;
ddRelationshipItem *item;
for (it = chm.begin(); it != chm.end(); ++it)
{
wxString key = it->first;
item = it->second;
if(item->original)
{
if(!reset)
item->original->setTextColour(originalColour);
else
item->original->setTextColour(item->original->getOwnerTable()->fontColorAttribute->fontColor);
}
if(item->fkColumn)
{
if(!reset)
item->fkColumn->setTextColour(fkColour);
else
item->fkColumn->setTextColour(item->fkColumn->getOwnerTable()->fontColorAttribute->fontColor);
}
}
}
//Used by persistence classes
int ddRelationshipFigure::getUkIndex()
{
return ukIndex;
}
//Used by persistence classes
actionKind ddRelationshipFigure::getOnUpdateAction()
{
return onUpdateAction;
}
//Used by persistence classes
actionKind ddRelationshipFigure::getOnDeleteAction()
{
return onDeleteAction;
}
//Used by persistence classes
bool ddRelationshipFigure::getMatchSimple()
{
return matchSimple;
}
//Used by persistence classes
void ddRelationshipFigure::initRelationValues( ddTableFigure *source, ddTableFigure *destination, int ukIdx, wxString constraint, actionKind onUpdate, actionKind onDelete, bool simpleMatch, bool identifying, bool oneToMany, bool mandatory, bool fromPk )
{
ukIndex = ukIdx;
fkFromPk = fromPk;
fkMandatory = mandatory;
fkOneToMany = oneToMany;
fkIdentifying = identifying;
matchSimple = simpleMatch;
onUpdateAction = onUpdate;
onDeleteAction = onDelete;
constraintName = constraint;
//I need to add two points to relationship if there aren't any points for it a first position
int i, start = pointCount(0);
for(i = start; i < 2 ; i++)
{
addPoint(0, -100, -100);
}
//reestablish connections between figures
hdLineConnection::connectStart(source->connectorAt(0, getStartPoint(0).x, getStartPoint(0).y));
hdLineConnection::connectEnd(destination->connectorAt(0, getEndPoint(0).x, getEndPoint(0).y));
}

View file

@ -0,0 +1,132 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// ddRelationshipItem.cpp - Items (fk columns) inside a relationship figure hashmap
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
#include <wx/dcbuffer.h>
// App headers
#include "dd/dditems/figures/ddRelationshipItem.h"
#include "dd/dditems/figures/ddRelationshipFigure.h"
#include "hotdraw/main/hdDrawingView.h"
#include "dd/dditems/utilities/ddDataType.h"
ddRelationshipItem::ddRelationshipItem()
{
ownerRel = NULL;
original = NULL;
destinationTable = NULL;
}
ddRelationshipItem::ddRelationshipItem(ddRelationshipFigure *owner, ddColumnFigure *originalColumn, ddTableFigure *destination, ddColumnOptionType type, ddColumnType colType, ddColumnFigure *existingColumn)
{
ownerRel = owner;
original = originalColumn;
originalStartColName = original->getColumnName(false);
destinationTable = destination;
if(existingColumn == NULL) //Fk destination column will be automatically generated
{
generatedName = autoGenerateNameForFk();
fkColumn = new ddColumnFigure( generatedName, destinationTable, this);
fkColumn->setColumnOption(type);
fkColumn->toggleColumnKind(colType);
fkColumn->activateGenFkName(); //By default fk name is generate by using ( alias | tableName) . ColumnName combination
fkIsAutoGenerated = true;
}
else //using existing column as fk destination (Validation Required at user choices compatibility of types, precision and scale)
{
generatedName = wxEmptyString;
fkColumn = existingColumn;
fkColumn->setAsUserCreatedFk(this);
fkColumn->deactivateGenFkName();
fkIsAutoGenerated = false;
}
fkColumn->setRightIconForColumn();
}
ddRelationshipItem::~ddRelationshipItem()
{
}
void ddRelationshipItem::initRelationshipItemValues(ddRelationshipFigure *owner, ddTableFigure *destination, bool fromExistingColumn, ddColumnFigure *fkCol, ddColumnFigure *sourceCol, wxString initialColName)
{
ownerRel = owner;
destinationTable = destination;
fkIsAutoGenerated = fromExistingColumn;
original = sourceCol;
fkColumn = fkCol;
originalStartColName = initialColName;
if(fkIsAutoGenerated)
fkColumn->setFkSource(this);
else
fkColumn->setAsUserCreatedFk(this);
fkColumn->setRightIconForColumn();
}
bool ddRelationshipItem::isAutomaticallyGenerated()
{
return fkIsAutoGenerated;
}
wxString ddRelationshipItem::autoGenerateNameForFk()
{
wxString newName;
newName = original->getOwnerTable()->getTableName();
newName.append(wxT("_"));
newName.append(originalStartColName);
return newName;
}
void ddRelationshipItem::syncAutoFkName()
{
originalStartColName = original->getColumnName(false); //Because original name was probably changed, now I should update it.
if(fkColumn->isGeneratedForeignKey() && fkColumn->isFkNameGenerated() )
{
fkColumn->setColumnName(autoGenerateNameForFk());
//Update all connections, but need to be notified to all views only doing it right now for first view
ownerRel->updateConnection(0);
}
}
bool ddRelationshipItem::relationIsIdentifying()
{
return ownerRel->getIdentifying();
}
bool ddRelationshipItem::relationIsMandatory()
{
return ownerRel->getMandatory();
}
wxString ddRelationshipItem::sourceTableName()
{
return original->getOwnerTable()->getTableName();
}
wxString ddRelationshipItem::destTableName()
{
return destinationTable->getTableName();
}
bool ddRelationshipItem::isForeignKeyFromPk()
{
if(ownerRel)
{
return ownerRel->isForeignKeyFromPk();
}
return false;
}

View file

@ -0,0 +1,154 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// ddRelationshipTerminal.cpp - Draw inverse arrows at fk terminal based on kind of relationship.
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
#include <wx/dcbuffer.h>
// App headers
#include "dd/dditems/figures/ddRelationshipTerminal.h"
#include "hotdraw/utilities/hdPoint.h"
#include "hotdraw/utilities/hdRect.h"
#include "hotdraw/main/hdDrawingView.h"
#include "hotdraw/utilities/hdGeometry.h"
ddRelationshipTerminal::ddRelationshipTerminal(ddRelationshipFigure *owner, bool endFigureTerminal)
{
ownerFigure = owner;
endTerminal = endFigureTerminal;
}
ddRelationshipTerminal::~ddRelationshipTerminal()
{
}
hdPoint &ddRelationshipTerminal::draw (wxBufferedDC &context, hdPoint &a, hdPoint &b, hdDrawingView *view)
{
hdGeometry g;
hdPoint points[3];
context.SetPen(terminalLinePen);
hdPoint aCopy = a, bCopy = b;
view->CalcScrolledPosition(aCopy.x, aCopy.y, &aCopy.x, &aCopy.y);
view->CalcScrolledPosition(bCopy.x, bCopy.y, &bCopy.x, &bCopy.y);
if(endTerminal)
{
//Calc a point very far away of center of table to intersect one of the sides lines of the table rectangle figure
double X = aCopy.x + (bCopy.x - aCopy.x) * 0.9;
double Y = aCopy.y + (bCopy.y - aCopy.y) * 0.9;
if(ownerFigure->getEndFigure() && ownerFigure->getOneToMany())
{
hdRect r = ownerFigure->getEndFigure()->displayBox().gethdRect(view->getIdx());
view->CalcScrolledPosition(r.x, r.y, &r.x, &r.y);
int centerX = r.x + r.width / 2;
int centerY = r.y + r.height / 2;
context.SetPen(*wxBLACK_PEN);
context.SetBrush(*wxBLACK_BRUSH);
double XX, YY, distance;
//Calculate a new point to a given distance from the end of the relationship to draw many ( ----<| ) connector
//first calculate vector from point1 & point2
double vectorx = aCopy.x - bCopy.x;
double vectory = aCopy.y - bCopy.y;
//calculate the length
double length = sqrt(vectorx * vectorx + vectory * vectory);
//normalize the vector to unit length
double normalizevx = vectorx / length;
double normalizevy = vectory / length;
distance = -15;
//calculate point a given distance
XX = bCopy.x + normalizevx * (length + distance);
YY = bCopy.y + normalizevy * (length + distance);
wxPoint intersectionLine1(centerX, centerY);
wxPoint intersectionLine2(X, Y);
//TOP
if(g.intersection(intersectionLine1, intersectionLine2, r.GetTopLeft(), r.GetTopRight()))
{
points[0] = wxPoint(XX, YY);
points[1] = wxPoint(aCopy.x - 7, aCopy.y);
points[2] = wxPoint(aCopy.x + 7, aCopy.y);
context.DrawPolygon(3, points);
if(ownerFigure->getIdentifying())
{
context.SetPen(wxPen(*wxBLACK, 2));
context.DrawLine(wxPoint(XX - 7, YY), wxPoint(XX + 7, YY));
context.SetPen(*wxBLACK_PEN);
}
} //RIGHT
else if(g.intersection(intersectionLine1, intersectionLine2, r.GetTopRight(), r.GetBottomRight()))
{
points[0] = wxPoint(XX, YY);
points[1] = wxPoint(aCopy.x, aCopy.y - 7);
points[2] = wxPoint(aCopy.x, aCopy.y + 7);
context.DrawPolygon(3, points);
if(ownerFigure->getIdentifying())
{
context.SetPen(wxPen(*wxBLACK, 2));
context.DrawLine(wxPoint(XX, YY - 7), wxPoint(XX, YY + 7));
context.SetPen(*wxBLACK_PEN);
}
} //BOTTOM
else if(g.intersection(intersectionLine1, intersectionLine2, r.GetBottomLeft(), r.GetBottomRight()))
{
points[0] = wxPoint(XX, YY);
points[1] = wxPoint(aCopy.x - 7, aCopy.y);
points[2] = wxPoint(aCopy.x + 7, aCopy.y);
context.DrawPolygon(3, points);
if(ownerFigure->getIdentifying())
{
context.SetPen(wxPen(*wxBLACK, 2));
context.DrawLine(wxPoint(XX - 7, YY), wxPoint(XX + 7, YY));
context.SetPen(*wxBLACK_PEN);
}
} //LEFT
else if(g.intersection(intersectionLine1, intersectionLine2, r.GetTopLeft(), r.GetBottomLeft()))
{
points[0] = wxPoint(XX, YY);
points[1] = wxPoint(aCopy.x, aCopy.y - 7);
points[2] = wxPoint(aCopy.x, aCopy.y + 7);
context.DrawPolygon(3, points);
if(ownerFigure->getIdentifying())
{
context.SetPen(wxPen(*wxBLACK, 2));
context.DrawLine(wxPoint(XX, YY - 7), wxPoint(XX, YY + 7));
context.SetPen(*wxBLACK_PEN);
}
}
else
{
//CENTER of star figure or invalid place, do nothing
}
value = hdPoint(XX, YY);
return value;
}
value = hdPoint(0, 0);
return value;
}
value = hdPoint(0, 0);
return value;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,582 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// ddTextTableItemFigure.cpp - Draw a column inside a table
//
////////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
#include <wx/dcbuffer.h>
#include <wx/numdlg.h>
// App headers
#include "dd/dditems/figures/ddTextTableItemFigure.h"
#include "dd/dditems/figures/ddRelationshipItem.h"
#include "dd/dditems/tools/ddColumnTextTool.h"
#include "dd/dditems/utilities/ddDataType.h"
#include "hotdraw/figures/hdSimpleTextFigure.h"
#include "hotdraw/main/hdDrawingView.h"
#include "dd/ddmodel/ddDrawingEditor.h"
#include "dd/ddmodel/ddDatabaseDesign.h"
#include "dd/dditems/figures/ddTableFigure.h"
#include "dd/dditems/utilities/ddPrecisionScaleDialog.h"
#include "hotdraw/utilities/hdRemoveDeleteDialog.h"
ddTextTableItemFigure::ddTextTableItemFigure(wxString &columnName, ddDataType dataType, ddColumnFigure *owner):
hdSimpleTextFigure(columnName)
{
setKindId(DDTEXTTABLEITEMFIGURE);
ownerTable = NULL; //table name item is the only one case of use of this variable
oneTimeNoAlias = false;
columnType = dataType;
this->setEditable(true);
enablePopUp();
ownerColumn = owner;
showDataType = true;
showAlias = false;
recalculateDisplayBox();
precision = -1;
scale = -1;
if(owner) //is Column Object
{
fontColorAttribute->fontColor = owner->getOwnerTable()->fontColorAttribute->fontColor;
fontSelColorAttribute->fontColor = owner->getOwnerTable()->fontSelColorAttribute->fontColor;
}
}
ddTextTableItemFigure::~ddTextTableItemFigure()
{
}
void ddTextTableItemFigure::recalculateDisplayBox()
{
if (ownerColumn && ownerColumn->getOwnerTable())
{
hdSimpleTextFigure::recalculateDisplayBox();
displayBox().width = ownerColumn->getOwnerTable()->getFiguresMaxWidth();
}
}
void ddTextTableItemFigure::displayBoxUpdate()
{
recalculateDisplayBox();
}
void ddTextTableItemFigure::setOwnerTable(ddTableFigure *table)
{
ownerTable = table;
if(table)//is Table Name object
{
fontColorAttribute->fontColor = ownerTable->fontColorAttribute->fontColor;
fontSelColorAttribute->fontColor = ownerTable->fontSelColorAttribute->fontColor;
}
}
wxString &ddTextTableItemFigure::getText(bool extended)
{
if(showDataType && extended && getOwnerColumn())
{
wxString ddType = dataTypes()[getDataType()]; //Should use getDataType() & getPrecision(), because when column is fk, type is not taken from this column, instead from original column (source of fk)
bool havePrecision = columnType == dt_numeric || columnType == dt_bit || columnType == dt_char || columnType == dt_interval || columnType == dt_varbit || columnType == dt_varchar;
if( havePrecision && getPrecision() >= 0)
{
ddType.Truncate(ddType.Find(wxT("(")));
if(getScale() == -1)
ddType += wxString::Format(wxT("(%d)"), getPrecision());
else
ddType += wxString::Format(wxT("(%d,%d)"), getPrecision(), getScale());
}
//Fix to serial is integer at automatically generated foreign key
if(getDataType() == dt_serial && getOwnerColumn()->isGeneratedForeignKey())
ddType = dataTypes()[dt_integer];
out = wxString( hdSimpleTextFigure::getText() + wxString(wxT(" : ")) + ddType );
return out;
}
else if( showAlias && getOwnerColumn() == NULL )
{
if(!oneTimeNoAlias)
out = wxString( hdSimpleTextFigure::getText() + wxString(wxT(" ( ")) + colAlias + wxString(wxT(" ) ")) );
else
{
out = wxString( hdSimpleTextFigure::getText() );
oneTimeNoAlias = false;
}
return out;
}
else
{
return hdSimpleTextFigure::getText();
}
}
wxString ddTextTableItemFigure::getType(bool raw)
{
wxString ddType = dataTypes()[columnType];
if(raw)
return ddType;
bool havePrecision = columnType == dt_numeric || columnType == dt_bit || columnType == dt_char || columnType == dt_interval || columnType == dt_varbit || columnType == dt_varchar;
if( havePrecision && getPrecision() >= 0)
{
ddType.Truncate(ddType.Find(wxT("(")));
if(getScale() == -1)
ddType += wxString::Format(wxT("(%d)"), getPrecision());
else
ddType += wxString::Format(wxT("(%d,%d)"), getPrecision(), getScale());
}
//Fix to serial is integer at automatically generated foreign key
if(columnType == dt_serial && getOwnerColumn()->isGeneratedForeignKey())
ddType = dataTypes()[dt_integer];
return ddType;
}
//WARNING: event ID must match enum ddDataType!!! this event was created on view
void ddTextTableItemFigure::OnGenericPopupClick(wxCommandEvent &event, hdDrawingView *view)
{
wxTextEntryDialog *nameDialog = NULL;
ddPrecisionScaleDialog *numericDialog = NULL;
wxString tmpString;
int answer;
int tmpprecision;
long tmpvalue;
hdRemoveDeleteDialog *delremDialog = NULL;
switch(event.GetId())
{
case MNU_DDADDCOLUMN:
nameDialog = new wxTextEntryDialog(view, wxT("New column name"), wxT("Add a column"));
answer = nameDialog->ShowModal();
if (answer == wxID_OK)
{
tmpString = nameDialog->GetValue();
getOwnerColumn()->getOwnerTable()->addColumn(view->getIdx(), new ddColumnFigure(tmpString, getOwnerColumn()->getOwnerTable()));
view->notifyChanged();
}
delete nameDialog;
break;
case MNU_DELCOLUMN:
answer = wxMessageBox(wxT("Are you sure you wish to delete column ") + getText(true) + wxT("?"), wxT("Delete column?"), wxYES_NO | wxNO_DEFAULT, view);
if (answer == wxYES)
{
getOwnerColumn()->getOwnerTable()->removeColumn(view->getIdx(), getOwnerColumn());
view->notifyChanged();
}
break;
case MNU_AUTONAMCOLUMN:
getOwnerColumn()->activateGenFkName();
getOwnerColumn()->getFkSource()->syncAutoFkName();
view->notifyChanged();
break;
case MNU_RENAMECOLUMN:
nameDialog = new wxTextEntryDialog(view, wxT("New column name"), wxT("Rename Column"), getText());
nameDialog->ShowModal();
if(getOwnerColumn()->isGeneratedForeignKey()) //after a manual user column rename, deactivated automatic generation of fk name.
getOwnerColumn()->deactivateGenFkName();
setText(nameDialog->GetValue());
delete nameDialog;
view->notifyChanged();
break;
case MNU_NOTNULL:
if(getOwnerColumn()->isNotNull())
getOwnerColumn()->setColumnOption(null);
else
getOwnerColumn()->setColumnOption(notnull);
view->notifyChanged();
break;
case MNU_PKEY:
if(getOwnerColumn()->isPrimaryKey())
{
getOwnerColumn()->disablePrimaryKey();
}
else
{
getOwnerColumn()->enablePrimaryKey();
getOwnerColumn()->setColumnOption(notnull);
}
view->notifyChanged();
break;
case MNU_UKEY:
getOwnerColumn()->toggleColumnKind(uk, view);
view->notifyChanged();
break;
case MNU_TYPESERIAL:
setDataType(dt_serial); //Should use setDataType always to set this value to allow fk to work flawlessly
recalculateDisplayBox();
getOwnerColumn()->displayBoxUpdate();
getOwnerColumn()->getOwnerTable()->updateTableSize();
view->notifyChanged();
break;
case MNU_TYPEBOOLEAN:
setDataType(dt_boolean);
recalculateDisplayBox();
getOwnerColumn()->displayBoxUpdate();
getOwnerColumn()->getOwnerTable()->updateTableSize();
view->notifyChanged();
break;
case MNU_TYPEINTEGER:
setDataType(dt_integer);
recalculateDisplayBox();
getOwnerColumn()->displayBoxUpdate();
getOwnerColumn()->getOwnerTable()->updateTableSize();
view->notifyChanged();
break;
case MNU_TYPEMONEY:
setDataType(dt_money);
recalculateDisplayBox();
getOwnerColumn()->displayBoxUpdate();
getOwnerColumn()->getOwnerTable()->updateTableSize();
view->notifyChanged();
break;
case MNU_TYPEVARCHAR:
setDataType(dt_varchar);
tmpprecision = wxGetNumberFromUser(_("Varchar size"),
_("Size for varchar datatype"),
_("Varchar size"),
getPrecision(), 0, 255, view);
if (tmpprecision >= 0)
{
setPrecision(tmpprecision);
setScale(-1);
}
recalculateDisplayBox();
getOwnerColumn()->displayBoxUpdate();
getOwnerColumn()->getOwnerTable()->updateTableSize();
view->notifyChanged();
break;
case MNU_TYPEOTHER:
answer = wxGetSingleChoiceIndex(wxT("New column datatype"), wxT("Column Datatypes"), dataTypes(), view);
if(answer >= 0)
{
view->notifyChanged();
if(answer == dt_varchar || answer == dt_bit || answer == dt_char || answer == dt_interval || answer == dt_varbit)
{
tmpprecision = wxGetNumberFromUser(_("datatype size"),
_("Size for datatype"),
_("size"),
getPrecision(), 0, 255, view);
if (tmpprecision >= 0)
{
setPrecision(tmpprecision);
setScale(-1);
}
recalculateDisplayBox();
getOwnerColumn()->displayBoxUpdate();
getOwnerColumn()->getOwnerTable()->updateTableSize();
}
if(answer == dt_numeric)
{
numericDialog = new ddPrecisionScaleDialog( view,
NumToStr((long)getPrecision()),
NumToStr((long)getScale()));
numericDialog->ShowModal();
numericDialog->GetValue1().ToLong(&tmpvalue);
setPrecision(tmpvalue);
numericDialog->GetValue2().ToLong(&tmpvalue);
setScale(tmpvalue);
delete numericDialog;
recalculateDisplayBox();
getOwnerColumn()->displayBoxUpdate();
getOwnerColumn()->getOwnerTable()->updateTableSize();
}
setDataType( (ddDataType) answer );
recalculateDisplayBox();
getOwnerColumn()->displayBoxUpdate();
getOwnerColumn()->getOwnerTable()->updateTableSize();
}
break;
case MNU_TYPEPKEY_CONSTRAINTNAME:
tmpString = wxGetTextFromUser(wxT("New name of primary key:"), getOwnerColumn()->getOwnerTable()->getPkConstraintName(), getOwnerColumn()->getOwnerTable()->getPkConstraintName(), view);
if(tmpString.length() > 0)
{
getOwnerColumn()->getOwnerTable()->setPkConstraintName(tmpString);
view->notifyChanged();
}
break;
case MNU_TYPEUKEY_CONSTRAINTNAME:
answer = wxGetSingleChoiceIndex(wxT("Select Unique Key constraint to edit name"), wxT("Select Unique Constraint to edit name:"), getOwnerColumn()->getOwnerTable()->getUkConstraintsNames(), view);
if(answer >= 0)
{
tmpString = wxGetTextFromUser(wxT("Change name of Unique Key constraint:"), getOwnerColumn()->getOwnerTable()->getUkConstraintsNames().Item(answer), getOwnerColumn()->getOwnerTable()->getUkConstraintsNames().Item(answer), view);
if(tmpString.length() > 0)
{
getOwnerColumn()->getOwnerTable()->getUkConstraintsNames().Item(answer) = tmpString;
view->notifyChanged();
}
}
break;
case MNU_DELTABLE:
delremDialog = new hdRemoveDeleteDialog(wxT("Are you sure you wish to delete table ") + getOwnerColumn()->getOwnerTable()->getTableName() + wxT("?"), wxT("Delete table?"), view);
answer = delremDialog->ShowModal();
ddTableFigure *table = getOwnerColumn()->getOwnerTable();
if (answer == DD_DELETE)
{
ddDrawingEditor *editor = (ddDrawingEditor *) view->editor();
//Unselect table at all diagrams
editor->removeFromAllSelections(table);
//Drop foreign keys with this table as origin or destination
table->processDeleteAlert(view->getDrawing());
//Drop table
editor->deleteModelFigure(table);
editor->getDesign()->refreshBrowser();
view->notifyChanged();
}
else if(answer == DD_REMOVE)
{
ddDrawingEditor *editor = (ddDrawingEditor *) view->editor();
editor->getExistingDiagram(view->getIdx())->removeFromSelection(table);
editor->getExistingDiagram(view->getIdx())->remove(table);
view->notifyChanged();
}
delete delremDialog;
break;
}
}
void ddTextTableItemFigure::createMenu(wxMenu &mnu)
{
wxMenu *submenu;
wxMenuItem *item;
mnu.Append(MNU_DDADDCOLUMN, _("Add a column..."));
item = mnu.Append(MNU_DELCOLUMN, _("Delete the selected column..."));
if(getOwnerColumn()->isGeneratedForeignKey())
item->Enable(false);
mnu.Append(MNU_RENAMECOLUMN, _("Rename the selected column..."));
if(getOwnerColumn()->isGeneratedForeignKey() && !getOwnerColumn()->isFkNameGenerated())
mnu.Append(MNU_AUTONAMCOLUMN, _("Activate fk auto-naming..."));
mnu.AppendSeparator();
item = mnu.AppendCheckItem(MNU_NOTNULL, _("Not NULL constraint"));
if(getOwnerColumn()->isNotNull())
item->Check(true);
if(getOwnerColumn()->isGeneratedForeignKey())
item->Enable(false);
mnu.AppendSeparator();
item = mnu.AppendCheckItem(MNU_PKEY, _("Primary Key"));
if(getOwnerColumn()->isPrimaryKey())
item->Check(true);
if(getOwnerColumn()->isGeneratedForeignKey())
item->Enable(false);
item = mnu.AppendCheckItem(MNU_UKEY, _("Unique Key"));
if(getOwnerColumn()->isUniqueKey())
item->Check(true);
mnu.AppendSeparator();
submenu = new wxMenu();
item = mnu.AppendSubMenu(submenu, _("Column datatype"));
if(getOwnerColumn()->isGeneratedForeignKey())
item->Enable(false);
item = submenu->AppendCheckItem(MNU_TYPESERIAL, _("serial"));
item->Check(columnType == dt_bigint);
item = submenu->AppendCheckItem(MNU_TYPEBOOLEAN, _("boolean"));
item->Check(columnType == dt_boolean);
item = submenu->AppendCheckItem(MNU_TYPEINTEGER, _("integer"));
item->Check(columnType == dt_integer);
item = submenu->AppendCheckItem(MNU_TYPEMONEY, _("money"));
item->Check(columnType == dt_money);
item = submenu->AppendCheckItem(MNU_TYPEVARCHAR, _("varchar(n)"));
item->Check(columnType == dt_varchar);
item = submenu->Append(MNU_TYPEOTHER, _("Choose another datatype..."));
mnu.AppendSeparator();
mnu.Append(MNU_TYPEPKEY_CONSTRAINTNAME, _("Primary Key Constraint name..."));
mnu.Append(MNU_TYPEUKEY_CONSTRAINTNAME, _("Unique Key Constraint name..."));
mnu.AppendSeparator();
mnu.Append(MNU_DELTABLE, _("Delete table..."));
};
const wxArrayString ddTextTableItemFigure::dataTypes()
{
if(ddDatatypes.IsEmpty())
{
//Fast access ddDatatypes
ddDatatypes.Add(wxT("ANY"));
ddDatatypes.Add(wxT("serial"));
ddDatatypes.Add(wxT("boolean"));
ddDatatypes.Add(wxT("integer"));
ddDatatypes.Add(wxT("money"));
ddDatatypes.Add(wxT("varchar(n)"));
//Normal access ddDatatypes
ddDatatypes.Add(wxT("bigint"));
ddDatatypes.Add(wxT("bit(n)"));
ddDatatypes.Add(wxT("bytea"));
ddDatatypes.Add(wxT("char(n)"));
ddDatatypes.Add(wxT("cidr"));
ddDatatypes.Add(wxT("circle"));
ddDatatypes.Add(wxT("date"));
ddDatatypes.Add(wxT("double precision"));
ddDatatypes.Add(wxT("inet"));
ddDatatypes.Add(wxT("interval(n)"));
ddDatatypes.Add(wxT("line"));
ddDatatypes.Add(wxT("lseg"));
ddDatatypes.Add(wxT("macaddr"));
ddDatatypes.Add(wxT("numeric(p,s)"));
ddDatatypes.Add(wxT("path"));
ddDatatypes.Add(wxT("point"));
ddDatatypes.Add(wxT("polygon"));
ddDatatypes.Add(wxT("real"));
ddDatatypes.Add(wxT("smallint"));
ddDatatypes.Add(wxT("text"));
ddDatatypes.Add(wxT("time"));
ddDatatypes.Add(wxT("timestamp"));
ddDatatypes.Add(wxT("varbit(n)"));
}
return ddDatatypes;
}
void ddTextTableItemFigure::setText(wxString textString)
{
hdSimpleTextFigure::setText(textString);
//Hack to allow column text to submit new size of text signal to tablefigure
//and then recalculate displaybox. Helps with fk autorenaming too.
if(ownerColumn)
{
ownerColumn->displayBoxUpdate();
ownerColumn->getOwnerTable()->updateTableSize();
ownerColumn->getOwnerTable()->updateFkObservers();
}
}
wxString ddTextTableItemFigure::getAlias()
{
return colAlias;
}
//Activate use of alias or short names at ddtextTableItems like TableNames [Columns don' use it]
void ddTextTableItemFigure::setAlias(wxString alias)
{
if(alias.length() <= 0 || alias.length() > 3 )
{
showAlias = false;
colAlias = wxEmptyString;
}
else
{
showAlias = true;
colAlias = alias;
}
recalculateDisplayBox();
ownerTable->updateFkObservers(); //Only triggered by a tableName item [Not a column]
ownerTable->updateTableSize();
}
void ddTextTableItemFigure::setOneTimeNoAlias()
{
oneTimeNoAlias = true;
}
ddColumnFigure *ddTextTableItemFigure::getOwnerColumn()
{
return ownerColumn;
}
void ddTextTableItemFigure::setOwnerColumn(ddColumnFigure *column)
{
ownerColumn = column;
}
void ddTextTableItemFigure::setShowDataType(bool value)
{
showDataType = value;
}
hdITool *ddTextTableItemFigure::CreateFigureTool(hdDrawingView *view, hdITool *defaultTool)
{
if(getOwnerColumn())
{
return textEditable ? new ddColumnTextTool(view, this, defaultTool, false, wxT("New Column Name"), wxT("Rename Column")) : defaultTool;
}
else
{
setOneTimeNoAlias();
return textEditable ? new ddColumnTextTool(view, this, defaultTool, false, wxT("New Table Name"), wxT("Rename Table")) : defaultTool;
}
}
int ddTextTableItemFigure::getTextWidth()
{
int w, h;
getFontMetrics(w, h);
return w;
}
int ddTextTableItemFigure::getTextHeight()
{
int w, h;
getFontMetrics(w, h);
return h;
}
ddDataType ddTextTableItemFigure::getDataType()
{
if(!getOwnerColumn()->isGeneratedForeignKey())
return columnType;
else
{
columnType = getOwnerColumn()->getFkSource()->original->getDataType();
return columnType;
}
}
void ddTextTableItemFigure::setDataType(ddDataType type)
{
columnType = type;
ownerColumn->getOwnerTable()->updateSizeOfObservers();
}
int ddTextTableItemFigure::getPrecision()
{
if(getOwnerColumn()->isGeneratedForeignKey())
{
precision = getOwnerColumn()->getFkSource()->original->getPrecision();
return precision;
}
else
{
return precision;
}
}
void ddTextTableItemFigure::setPrecision(int value)
{
if(!getOwnerColumn()->isGeneratedForeignKey())
{
precision = value;
ownerColumn->getOwnerTable()->updateSizeOfObservers();
}
}
void ddTextTableItemFigure::setScale(int value)
{
if(!getOwnerColumn()->isGeneratedForeignKey())
{
scale = value;
ownerColumn->getOwnerTable()->updateSizeOfObservers();
}
}
int ddTextTableItemFigure::getScale()
{
if(getOwnerColumn()->isGeneratedForeignKey())
{
scale = getOwnerColumn()->getFkSource()->original->getScale();
return scale;
}
else
{
return scale;
}
}

View file

@ -0,0 +1,25 @@
#######################################################################
#
# pgAdmin III - PostgreSQL Tools
#
# Copyright (C) 2002 - 2016, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
# module.mk - pgadmin/dd/dditems/figures/ Makefile fragment
#
#######################################################################
pgadmin3_SOURCES += \
dd/dditems/figures/ddColumnFigure.cpp \
dd/dditems/figures/ddColumnKindIcon.cpp \
dd/dditems/figures/ddColumnOptionIcon.cpp \
dd/dditems/figures/ddRelationshipFigure.cpp \
dd/dditems/figures/ddRelationshipItem.cpp \
dd/dditems/figures/ddRelationshipTerminal.cpp \
dd/dditems/figures/ddTableFigure.cpp \
dd/dditems/figures/ddTextTableItemFigure.cpp
EXTRA_DIST += \
dd/dditems/figures/module.mk
include dd/dditems/figures/xml/module.mk

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,16 @@
#######################################################################
#
# pgAdmin III - PostgreSQL Tools
#
# Copyright (C) 2002 - 2016, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
# module.mk - pgadmin/dd/dditems/figures/xml/ Makefile fragment
#
#######################################################################
pgadmin3_SOURCES += \
dd/dditems/figures/xml/ddXmlStorage.cpp
EXTRA_DIST += \
dd/dditems/figures/xml/module.mk

View file

@ -0,0 +1,76 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// ddAddColButtonHandle.cpp - A handle for a table figure that allow to graphically add columns
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
// App headers
#include "dd/dditems/handles/ddAddColButtonHandle.h"
#include "dd/dditems/figures/ddTableFigure.h"
#include "dd/dditems/utilities/ddDataType.h"
#include "hotdraw/main/hdDrawingView.h"
//Images
#include "images/ddAddColumnCursor.pngc"
ddAddColButtonHandle::ddAddColButtonHandle(hdIFigure *owner, hdILocator *buttonLocator , wxBitmap &buttonImage, wxSize &size):
hdButtonHandle(owner, buttonLocator, buttonImage, size)
{
}
ddAddColButtonHandle::~ddAddColButtonHandle()
{
}
void ddAddColButtonHandle::invokeStart(hdMouseEvent &event, hdDrawingView *view)
{
ddTableFigure *table = (ddTableFigure *) getOwner();
wxTextEntryDialog nameDialog(view, wxT("New column name"), wxT("Add a column"));
bool again;
do
{
again = false;
int answer = nameDialog.ShowModal();
if (answer == wxID_OK)
{
wxString name = nameDialog.GetValue();
if(table->getColByName(name) == NULL)
{
table->addColumn(view->getIdx(), new ddColumnFigure(name, table));
view->notifyChanged();
}
else
{
wxString msg(wxT("Error trying to add new column '"));
msg.Append(name);
msg.Append(wxT("' column name already in use"));
wxMessageDialog info( view, msg ,
wxT("Column name already in use"),
wxNO_DEFAULT | wxOK | wxICON_EXCLAMATION);
again = true;
info.ShowModal();
}
}
}
while(again);
view->Refresh();
}
void ddAddColButtonHandle::invokeStep(hdMouseEvent &event, hdDrawingView *view)
{
}
void ddAddColButtonHandle::invokeEnd(hdMouseEvent &event, hdDrawingView *view)
{
}

View file

@ -0,0 +1,73 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// ddAddFkButtonHandle.cpp - A handle for a table figure that allow to graphically add relationships (fk)
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
// App headers
#include "dd/dditems/handles/ddAddFkButtonHandle.h"
#include "dd/dditems/figures/ddTableFigure.h"
#include "dd/dditems/utilities/ddDataType.h"
#include "hotdraw/tools/hdConnectionCreationTool.h"
#include "hotdraw/utilities/hdMouseEvent.h"
#include "dd/dditems/figures/ddRelationshipFigure.h"
#include "dd/dditems/figures/ddRelationshipTerminal.h"
//Images
#include "images/ddRelationshipCursor.pngc"
ddAddFkButtonHandle::ddAddFkButtonHandle(hdIFigure *owner, hdILocator *buttonLocator , wxBitmap &buttonImage, wxSize &size):
hdButtonHandle(owner, buttonLocator, buttonImage, size)
{
}
ddAddFkButtonHandle::~ddAddFkButtonHandle()
{
}
void ddAddFkButtonHandle::invokeStart(hdMouseEvent &event, hdDrawingView *view)
{
if(getOwner()->ms_classInfo.IsKindOf(&ddTableFigure::ms_classInfo))
{
ddRelationshipFigure *fkConnection = new ddRelationshipFigure();
//Check figure available positions for diagrams, add at least needed to allow initialization of the class
int i, start;
start = fkConnection->displayBox().CountPositions();
for(i = start; i < (view->getIdx() + 1); i++)
{
fkConnection->AddPosForNewDiagram();
}
fkConnection->setStartTerminal(new ddRelationshipTerminal(fkConnection, false));
fkConnection->setEndTerminal(new ddRelationshipTerminal(fkConnection, true));
hdConnectionCreationTool *conn = new hdConnectionCreationTool(view, fkConnection);
view->setTool(conn);
// Simulate button down to start connection of foreign key
wxMouseEvent e(wxEVT_LEFT_DOWN);
e.m_x = event.GetPosition().x;
e.m_y = event.GetPosition().y;
e.SetEventObject(view);
hdMouseEvent evento(e, view);
conn->mouseDown(evento);
ddTableFigure *table = (ddTableFigure *) getOwner();
table->setSelectFkDestMode(true);
view->notifyChanged();
}
}
void ddAddFkButtonHandle::invokeStep(hdMouseEvent &event, hdDrawingView *view)
{
}
void ddAddFkButtonHandle::invokeEnd(hdMouseEvent &event, hdDrawingView *view)
{
}

View file

@ -0,0 +1,62 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// ddMinMaxTableButtonHandle.cpp - A handle for a table figure that allow to graphically minimize or maximize table window size
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
// App headers
#include "dd/dditems/handles/ddMinMaxTableButtonHandle.h"
#include "dd/dditems/figures/ddTableFigure.h"
#include "dd/dditems/utilities/ddDataType.h"
#include "hotdraw/main/hdDrawingView.h"
//Images
#include "images/ddMinMaxCursor.pngc"
ddMinMaxTableButtonHandle::ddMinMaxTableButtonHandle(hdIFigure *owner, hdILocator *buttonLocator , wxBitmap &buttonImage, wxBitmap &buttonSecondImage, wxSize &size):
hdButtonHandle(owner, buttonLocator, buttonImage, size)
{
buttonMaximizeImage = buttonSecondImage;
tmpImage = buttonImage;
showFirst = true;
}
ddMinMaxTableButtonHandle::~ddMinMaxTableButtonHandle()
{
}
void ddMinMaxTableButtonHandle::invokeStart(hdMouseEvent &event, hdDrawingView *view)
{
}
void ddMinMaxTableButtonHandle::invokeStep(hdMouseEvent &event, hdDrawingView *view)
{
}
void ddMinMaxTableButtonHandle::invokeEnd(hdMouseEvent &event, hdDrawingView *view)
{
ddTableFigure *table = (ddTableFigure *) getOwner();
if(showFirst)
{
buttonIcon = buttonMaximizeImage;
table->setColumnsWindow(view->getIdx(), 1);
}
else
{
buttonIcon = tmpImage;
table->setColumnsWindow(table->getTotalColumns(), true);
}
showFirst = !showFirst;
view->notifyChanged();
}

View file

@ -0,0 +1,77 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// ddRemoveTableButtonHandle.cpp - A handle for a table figure that allow to delete it
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
// App headers
#include "dd/dditems/handles/ddRemoveTableButtonHandle.h"
#include "dd/dditems/figures/ddTableFigure.h"
#include "dd/dditems/figures/ddRelationshipFigure.h"
#include "dd/dditems/utilities/ddDataType.h"
#include "hotdraw/main/hdDrawingView.h"
#include "hotdraw/utilities/hdRemoveDeleteDialog.h"
#include "dd/ddmodel/ddDrawingEditor.h"
#include "dd/ddmodel/ddDatabaseDesign.h"
//Images
#include "images/ddDeleteTableCursor.pngc"
ddRemoveTableButtonHandle::ddRemoveTableButtonHandle(hdIFigure *owner, hdILocator *buttonLocator , wxBitmap &buttonImage, wxSize &size):
hdButtonHandle(owner, buttonLocator, buttonImage, size)
{
}
ddRemoveTableButtonHandle::~ddRemoveTableButtonHandle()
{
}
void ddRemoveTableButtonHandle::invokeStart(hdMouseEvent &event, hdDrawingView *view)
{
}
void ddRemoveTableButtonHandle::invokeStep(hdMouseEvent &event, hdDrawingView *view)
{
}
void ddRemoveTableButtonHandle::invokeEnd(hdMouseEvent &event, hdDrawingView *view)
{
if(view && getOwner())
{
ddTableFigure *table = (ddTableFigure *) getOwner();
hdRemoveDeleteDialog dialog(_("Are you sure you wish to delete table ") + table->getTableName() + wxT("?"), _("Delete table?"), view);
int answer = dialog.ShowModal();
if (answer == DD_DELETE)
{
ddDrawingEditor *editor = (ddDrawingEditor *) view->editor();
//Unselect table at all diagrams
editor->removeFromAllSelections(table);
//Drop foreign keys with this table as origin or destination
table->processDeleteAlert(view->getDrawing());
//Drop table
editor->deleteModelFigure(table);
editor->getDesign()->refreshBrowser();
editor->checkRelationshipsConsistency(view->getIdx());
view->notifyChanged();
}
else if(answer == DD_REMOVE)
{
ddDrawingEditor *editor = (ddDrawingEditor *) view->editor();
editor->getExistingDiagram(view->getIdx())->removeFromSelection(table);
editor->getExistingDiagram(view->getIdx())->remove(table);
editor->checkRelationshipsConsistency(view->getIdx());
view->notifyChanged();
}
}
}

View file

@ -0,0 +1,131 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// ddScrollBarHandle.cpp - A handle for a table figure that allow to scroll it when table is not in full size
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
// App headers
#include "dd/dditems/handles/ddScrollBarHandle.h"
#include "hotdraw/utilities/hdPoint.h"
#include "hotdraw/main/hdDrawingView.h"
#include "hotdraw/utilities/hdGeometry.h"
//Images
#include "images/ddUp.pngc"
#include "images/ddDown.pngc"
ddScrollBarHandle::ddScrollBarHandle(ddTableFigure *owner, hdILocator *scrollBarLocator , wxSize &size):
hdLocatorHandle(owner, scrollBarLocator)
{
table = owner;
scrollLocator = scrollBarLocator;
displayBox.SetSize(size);
upBitmap = wxBitmap(*ddUp_png_img);
downBitmap = wxBitmap(*ddDown_png_img);
}
ddScrollBarHandle::~ddScrollBarHandle()
{
}
wxCursor ddScrollBarHandle::createCursor()
{
return wxCursor(wxCURSOR_HAND);
}
//avoid to use inflate on this handle
hdRect &ddScrollBarHandle::getDisplayBox(int posIdx)
{
hdPoint p = locate(posIdx);
displayBox.width = 11; //as defined at locator
displayBox.height = table->getColsSpace().height;
displayBox.SetPosition(p);
return displayBox;
}
void ddScrollBarHandle::draw(wxBufferedDC &context, hdDrawingView *view)
{
int idx = view->getIdx();
context.SetBrush(*wxWHITE_BRUSH);
wxPoint copy = getDisplayBox(idx).GetPosition();
view->CalcScrolledPosition(copy.x, copy.y, &copy.x, &copy.y);
context.DrawRectangle(copy.x, copy.y, getDisplayBox(idx).width, getDisplayBox(idx).height);
context.DrawBitmap(upBitmap, copy.x + 1, copy.y + 2, true);
context.DrawBitmap(downBitmap, copy.x + 1, copy.y + getDisplayBox(idx).height - 2 - downBitmap.GetHeight(), true);
barSize.SetHeight((getDisplayBox(idx).height - 12) * 0.45);
barSize.SetWidth(getDisplayBox(idx).width - 4);
int divBy = (table->getTotalColumns() - table->getColumnsWindow());
if(divBy <= 0)
divBy = table->getColumnsWindow();
int colOffset = barSize.GetHeight() / divBy;
int verticalPosBar = 3 + copy.y + downBitmap.GetHeight() + colOffset * table->getTopColWindowIndex();
if(table->getColumnsWindow() > 1)
context.DrawRectangle(wxPoint(copy.x + 2, verticalPosBar), barSize);
}
void ddScrollBarHandle::invokeStart(hdMouseEvent &event, hdDrawingView *view)
{
int idx = view->getIdx();
int y = event.GetPosition().y;
anchorY = y;
if( (y > (getDisplayBox(idx).GetPosition().y + 2)) && (y < (getDisplayBox(idx).GetPosition().y + 2 + 6)) ) //6 image height
table->columnsWindowUp(idx);
if( (y > (getDisplayBox(idx).GetPosition().y + getDisplayBox(idx).height - 2 - downBitmap.GetHeight()) ) && (y < (getDisplayBox(idx).GetPosition().y + getDisplayBox(idx).height - 2) ) )
table->columnsWindowDown(idx);
view->notifyChanged();
}
void ddScrollBarHandle::invokeStep(hdMouseEvent &event, hdDrawingView *view)
{
int y = event.GetPosition().y;
int divBy = (table->getTotalColumns() - table->getColumnsWindow());
if(divBy <= 0)
divBy = table->getColumnsWindow();
int colOffset = barSize.GetHeight() / divBy;
hdGeometry g;
if ( g.ddabs(anchorY - y) > colOffset)
{
if((anchorY - y) > 0)
{
table->columnsWindowUp(view->getIdx());
}
else
{
table->columnsWindowDown(view->getIdx());
}
anchorY = y;
}
view->notifyChanged();
}
void ddScrollBarHandle::invokeEnd(hdMouseEvent &event, hdDrawingView *view)
{
}
hdPoint &ddScrollBarHandle::locate(int posIdx)
{
if(scrollLocator)
{
pointLocate = scrollLocator->locate(posIdx, getOwner());
return pointLocate;
}
else
pointLocate = hdPoint(0, 0);
return pointLocate;
}

View file

@ -0,0 +1,94 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// ddSouthTableSizeHandle.cpp - Allow to change table size by using drag and drop from south side of table rectangle
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
// App headers
#include "dd/dditems/handles/ddSouthTableSizeHandle.h"
#include "dd/dditems/figures/ddTableFigure.h"
#include "hotdraw/utilities/hdGeometry.h"
#include "hotdraw/figures/defaultAttributes/hdFontAttribute.h"
#include "hotdraw/main/hdDrawingView.h"
ddSouthTableSizeHandle::ddSouthTableSizeHandle(ddTableFigure *owner, hdILocator *locator):
hdLocatorHandle(owner, locator)
{
}
hdRect &ddSouthTableSizeHandle::getDisplayBox(int posIdx)
{
hdPoint p = locate(posIdx);
ddTableFigure *table = (ddTableFigure *) getOwner();
displayBox.width = table->getFullSpace().width * 0.5; //as defined at locator
displayBox.height = 3;
displayBox.SetPosition(p);
return displayBox;
}
wxCursor ddSouthTableSizeHandle::createCursor()
{
return wxCursor(wxCURSOR_SIZENS);
}
void ddSouthTableSizeHandle::draw(wxBufferedDC &context, hdDrawingView *view)
{
}
ddSouthTableSizeHandle::~ddSouthTableSizeHandle()
{
}
void ddSouthTableSizeHandle::invokeStart(hdMouseEvent &event, hdDrawingView *view)
{
anchorY = event.GetPosition().y;
}
void ddSouthTableSizeHandle::invokeStep(hdMouseEvent &event, hdDrawingView *view)
{
int y = event.GetPosition().y;
ddTableFigure *table = (ddTableFigure *) getOwner();
wxFont font = *hdFontAttribute::defaultFont;
int colOffset = table->getColDefaultHeight(font);
int divBy = (table->getTotalColumns() - table->getColumnsWindow());
if(divBy <= 0)
divBy = table->getColumnsWindow();
hdGeometry g;
if ( g.ddabs(anchorY - y) > colOffset)
{
if((anchorY - y) > 0)
{
table->setColumnsWindow(view->getIdx(), table->getColumnsWindow() - 1);
}
else
{
table->setColumnsWindow(view->getIdx(), table->getColumnsWindow() + 1);
}
anchorY = y;
}
//hack to update relationship position when table size change
table->manuallyNotifyChange(view->getIdx());
view->notifyChanged();
}
void ddSouthTableSizeHandle::invokeEnd(hdMouseEvent &event, hdDrawingView *view)
{
//hack to update relationship position when table size change
ddTableFigure *table = (ddTableFigure *) getOwner();
table->manuallyNotifyChange(view->getIdx());
view->notifyChanged();
}

View file

@ -0,0 +1,21 @@
#######################################################################
#
# pgAdmin III - PostgreSQL Tools
#
# Copyright (C) 2002 - 2016, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
# module.mk - pgadmin/dd/dditems/handles/ Makefile fragment
#
#######################################################################
pgadmin3_SOURCES += \
dd/dditems/handles/ddAddColButtonHandle.cpp \
dd/dditems/handles/ddAddFkButtonHandle.cpp \
dd/dditems/handles/ddMinMaxTableButtonHandle.cpp \
dd/dditems/handles/ddRemoveTableButtonHandle.cpp \
dd/dditems/handles/ddScrollBarHandle.cpp \
dd/dditems/handles/ddSouthTableSizeHandle.cpp
EXTRA_DIST += \
dd/dditems/handles/module.mk

View file

@ -0,0 +1,41 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// ddAddColLocator.cpp - Locate table add column button inside a table.
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
// App headers
#include "dd/dditems/locators/ddAddColLocator.h"
#include "dd/dditems/figures/ddTableFigure.h"
ddAddColLocator::ddAddColLocator()
{
}
ddAddColLocator::~ddAddColLocator()
{
}
hdPoint &ddAddColLocator::locate(int posIdx, hdIFigure *owner)
{
if(owner)
{
ddTableFigure *table = (ddTableFigure *) owner;
locatePoint.x = table->getTitleRect().GetBottomRight(posIdx).x - 20;
locatePoint.y = table->getTitleRect().GetBottomRight(posIdx).y - 9;
return locatePoint;
}
locatePoint.x = 0;
locatePoint.y = 0;
return locatePoint;
}

View file

@ -0,0 +1,41 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// ddAddFkLocator.cpp - Locate table add fk relationship button inside a table.
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
// App headers
#include "dd/dditems/locators/ddAddFkLocator.h"
#include "dd/dditems/figures/ddTableFigure.h"
ddAddFkLocator::ddAddFkLocator()
{
}
ddAddFkLocator::~ddAddFkLocator()
{
}
hdPoint &ddAddFkLocator::locate(int posIdx, hdIFigure *owner)
{
if(owner)
{
ddTableFigure *table = (ddTableFigure *) owner;
locatePoint.x = table->getTitleRect().GetBottomRight(posIdx).x - 10;
locatePoint.y = table->getTitleRect().GetBottomRight(posIdx).y - 9;
return locatePoint;
}
locatePoint.x = 0;
locatePoint.y = 0;
return locatePoint;
}

View file

@ -0,0 +1,44 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// ddMinMaxTableLocator.cpp - Locate table minimize/maximize button inside a table.
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
// App headers
#include "dd/dditems/locators/ddMinMaxTableLocator.h"
#include "dd/dditems/figures/ddTableFigure.h"
ddMinMaxTableLocator::ddMinMaxTableLocator()
{
}
ddMinMaxTableLocator::~ddMinMaxTableLocator()
{
}
hdPoint &ddMinMaxTableLocator::locate(int posIdx, hdIFigure *owner)
{
if(owner)
{
ddTableFigure *table = (ddTableFigure *) owner;
int x = table->displayBox().x[posIdx] + table->displayBox().width - 20; //(8+2)
int y = table->displayBox().y[posIdx] + 6;
locatePoint.x = x;
locatePoint.y = y;
return locatePoint;
}
locatePoint.x = 0;
locatePoint.y = 0;
return locatePoint;
}

View file

@ -0,0 +1,44 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// ddRemoveColLocator.cpp - Locate table delete button inside a table.
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
// App headers
#include "dd/dditems/locators/ddRemoveTableLocator.h"
#include "dd/dditems/figures/ddTableFigure.h"
ddRemoveTableLocator::ddRemoveTableLocator()
{
}
ddRemoveTableLocator::~ddRemoveTableLocator()
{
}
hdPoint &ddRemoveTableLocator::locate(int posIdx, hdIFigure *owner)
{
if(owner)
{
ddTableFigure *table = (ddTableFigure *) owner;
int x = table->displayBox().x[posIdx] + table->displayBox().width - 10; //(8+2)
int y = table->displayBox().y[posIdx] + 6;
locatePoint.x = x;
locatePoint.y = y;
return locatePoint;
}
locatePoint.x = 0;
locatePoint.y = 0;
return locatePoint;
}

View file

@ -0,0 +1,45 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// ddScrollBarTableLocator.cpp - Locate table scrollbar inside a table.
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
// App headers
#include "dd/dditems/locators/ddScrollBarTableLocator.h"
#include "dd/dditems/figures/ddTableFigure.h"
ddScrollBarTableLocator::ddScrollBarTableLocator()
{
}
ddScrollBarTableLocator::~ddScrollBarTableLocator()
{
}
hdPoint &ddScrollBarTableLocator::locate(int posIdx, hdIFigure *owner)
{
if(owner)
{
ddTableFigure *table = (ddTableFigure *) owner;
;
int x = table->getColsSpace().GetTopRight(posIdx).x - 11; //scrollwidth
int y = table->getColsSpace().y[posIdx];
locatePoint.x = x;
locatePoint.y = y;
return locatePoint;
}
locatePoint.x = 0;
locatePoint.y = 0;
return locatePoint;
}

View file

@ -0,0 +1,45 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// ddTableBottomLocator.cpp - Locate bottom (south) of table for use of south table size handle.
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
// App headers
#include "dd/dditems/locators/ddTableBottomLocator.h"
#include "dd/dditems/figures/ddTableFigure.h"
ddTableBottomLocator::ddTableBottomLocator()
{
}
ddTableBottomLocator::~ddTableBottomLocator()
{
}
hdPoint &ddTableBottomLocator::locate(int posIdx, hdIFigure *owner)
{
if(owner)
{
ddTableFigure *table = (ddTableFigure *) owner;
int x = table->getFullSpace().GetLeftBottom(posIdx).x + table->getFullSpace().width * 0.25;
int y = table->getFullSpace().GetLeftBottom(posIdx).y - 2;
locatePoint.x = x;
locatePoint.y = y;
return locatePoint;
}
locatePoint.x = 0;
locatePoint.y = 0;
return locatePoint;
}

View file

@ -0,0 +1,21 @@
#######################################################################
#
# pgAdmin III - PostgreSQL Tools
#
# Copyright (C) 2002 - 2016, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
# module.mk - pgadmin/dd/dditems/locators/ Makefile fragment
#
#######################################################################
pgadmin3_SOURCES += \
dd/dditems/locators/ddAddColLocator.cpp \
dd/dditems/locators/ddAddFkLocator.cpp \
dd/dditems/locators/ddMinMaxTableLocator.cpp \
dd/dditems/locators/ddRemoveTableLocator.cpp \
dd/dditems/locators/ddScrollBarTableLocator.cpp \
dd/dditems/locators/ddTableBottomLocator.cpp
EXTRA_DIST += \
dd/dditems/locators/module.mk

19
dd/dditems/module.mk Normal file
View file

@ -0,0 +1,19 @@
#######################################################################
#
# pgAdmin III - PostgreSQL Tools
#
# Copyright (C) 2002 - 2016, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
# module.mk - pgadmin/dd/dditems/ Makefile fragment
#
#######################################################################
include dd/dditems/figures/module.mk
include dd/dditems/handles/module.mk
include dd/dditems/locators/module.mk
include dd/dditems/tools/module.mk
include dd/dditems/utilities/module.mk
EXTRA_DIST += \
dd/dditems/module.mk

View file

@ -0,0 +1,130 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// ddColumnFigureTool.cpp - Improvement to hdFigureTool to work with composite table figures
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
// App headers
#include "dd/dditems/tools/ddColumnFigureTool.h"
#include "dd/dditems/figures/ddColumnFigure.h"
#include "hotdraw/tools/hdDragTrackerTool.h"
ddColumnFigureTool::ddColumnFigureTool(hdDrawingView *view, hdIFigure *fig, hdITool *dt):
hdFigureTool(view, fig, dt)
{
delegateTool = NULL;
}
ddColumnFigureTool::~ddColumnFigureTool()
{
//This tool destructor is at compositeTool, because this is only a selection tool and neither tool belongs to it.
hdITool *tmpDefault = hdFigureTool::getDefaultTool();
hdFigureTool *tmpDelegateDefault;
if(delegateTool->ms_classInfo.IsKindOf(&hdFigureTool::ms_classInfo))
tmpDelegateDefault = (hdFigureTool *)delegateTool;
else
tmpDelegateDefault = NULL;
if(delegateTool && delegateTool != tmpDefault)
{
//Hack to avoid delete defaultTool (Delegate->defaultTool) of delegate tool
// if this is the same as defaultTool (this->defaultTool) of this Object.
if(tmpDelegateDefault && tmpDelegateDefault->getDefaultTool() == tmpDefault)
tmpDelegateDefault->setDefaultTool(NULL);
//Hack to avoid delete hdDragTrackerTool Default twice because this figure is only used inside
//a table, and then create a compositeTool and default in both tools is hdDragTrackerTool
//but I can't hard code this is Composite because that class should remain generic
if(tmpDelegateDefault->getDefaultTool()->ms_classInfo.IsKindOf(&hdDragTrackerTool::ms_classInfo))
tmpDelegateDefault->setDefaultTool(NULL);
delete delegateTool;
}
}
void ddColumnFigureTool::setDefaultTool(hdITool *dt)
{
hdFigureTool::setDefaultTool(dt);
}
hdITool *ddColumnFigureTool::getDefaultTool()
{
if(delegateTool)
{
return delegateTool;
}
else
{
return hdFigureTool::getDefaultTool();
}
}
void ddColumnFigureTool::mouseDown(hdMouseEvent &event)
{
int x = event.GetPosition().x, y = event.GetPosition().y;
ddColumnFigure *cfigure = (ddColumnFigure *) getFigure();
hdIFigure *figure = cfigure->findFigure(event.getView()->getIdx(), x, y);
if(figure)
{
setDelegateTool(event.getView(), figure->CreateFigureTool(event.getView(), getDefaultTool()));
}
else
{
setDelegateTool(event.getView(), getDefaultTool());
}
if(delegateTool)
{
delegateTool->mouseDown(event);
}
}
void ddColumnFigureTool::activate(hdDrawingView *view)
{
if(delegateTool)
{
delegateTool->activate(view);
}
}
void ddColumnFigureTool::deactivate(hdDrawingView *view)
{
if(delegateTool)
{
delegateTool->deactivate(view);
}
}
void ddColumnFigureTool::setDelegateTool(hdDrawingView *view, hdITool *tool)
{
if(delegateTool)
{
delegateTool->deactivate(view);
delete delegateTool;
delegateTool = NULL;
}
delegateTool = tool;
if(delegateTool)
{
delegateTool->activate(view);
}
}
hdITool *ddColumnFigureTool::getDelegateTool()
{
return delegateTool;
}

View file

@ -0,0 +1,83 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// ddColumnTextTool.cpp - Modification of simple text tool for editing composite figure columns
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
#include <wx/textctrl.h>
#include <wx/choicdlg.h>
// App headers
#include "dd/dditems/tools/ddColumnTextTool.h"
#include "dd/dditems/figures/ddTextTableItemFigure.h"
#include "dd/dditems/figures/ddTableFigure.h"
#include "dd/dditems/utilities/ddTableNameDialog.h"
class hdDrawingEditor;
ddColumnTextTool::ddColumnTextTool(hdDrawingView *view, hdIFigure *fig, hdITool *dt, bool fastEdit , wxString dialogCaption, wxString dialogMessage):
hdSimpleTextTool(view, fig, dt, fastEdit, dialogCaption, dialogMessage)
{
if(colTextFigure->ms_classInfo.IsKindOf(&ddTextTableItemFigure::ms_classInfo))
colTextFigure = (ddTextTableItemFigure *) fig;
else
colTextFigure = NULL;
}
ddColumnTextTool::~ddColumnTextTool()
{
}
void ddColumnTextTool::mouseDown(hdMouseEvent &event)
{
hdSimpleTextTool::mouseDown(event);
}
bool ddColumnTextTool::callDialog(hdDrawingView *view)
{
if(colTextFigure->getOwnerColumn() == NULL)
{
wxString colName = colTextFigure->getText();
wxString colShortName = colTextFigure->getAlias();
ddTableNameDialog *nameAliasDialog = new ddTableNameDialog(
view,
colName,
colShortName,
colTextFigure
);
int answer = nameAliasDialog->ShowModal();
bool change = false;
if(answer == wxOK)
{
//check if names changed
change = ! (colShortName.IsSameAs(nameAliasDialog->GetValue1()) && colShortName.IsSameAs(nameAliasDialog->GetValue2()));
if(change)
{
colTextFigure->setText(nameAliasDialog->GetValue1());
colTextFigure->setAlias(nameAliasDialog->GetValue2());
view->notifyChanged();
}
}
delete nameAliasDialog;
return change;
}
else
{
bool change = hdSimpleTextTool::callDialog(view);
if( change && colTextFigure->getOwnerColumn()->isGeneratedForeignKey()) //after a manual user column rename, deactivated automatic generation of fk name.
colTextFigure->getOwnerColumn()->deactivateGenFkName();
return change;
}
}

View file

@ -0,0 +1,17 @@
#######################################################################
#
# pgAdmin III - PostgreSQL Tools
#
# Copyright (C) 2002 - 2016, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
# module.mk - pgadmin/dd/dditems/tools/ Makefile fragment
#
#######################################################################
pgadmin3_SOURCES += \
dd/dditems/tools/ddColumnFigureTool.cpp \
dd/dditems/tools/ddColumnTextTool.cpp
EXTRA_DIST += \
dd/dditems/tools/module.mk

View file

@ -0,0 +1,76 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// ddPrecisionScaleDialog.cpp - Utility dialog class to allow user input of table name and short name
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
#include <wx/statline.h>
#include <wx/spinctrl.h>
// App headers
#include "dd/dditems/utilities/ddPrecisionScaleDialog.h"
#include "dd/dditems/figures/ddTableFigure.h"
#define txtPrecision CTRL_TEXT("txtPrecision")
#define txtScale CTRL_TEXT("txtScale")
BEGIN_EVENT_TABLE(ddPrecisionScaleDialog, pgDialog)
END_EVENT_TABLE()
ddPrecisionScaleDialog::ddPrecisionScaleDialog( wxWindow *parent,
const wxString &defaultValue1,
const wxString &defaultValue2
)
{
SetFont(settings->GetSystemFont());
LoadResource(parent, wxT("ddPrecisionScaleDialog"));
RestorePosition();
Init();
SetValue1(defaultValue1);
SetValue2(defaultValue2);
txtPrecision->SetFocus();
}
ddPrecisionScaleDialog::~ddPrecisionScaleDialog()
{
}
ddPrecisionScaleDialog::ddPrecisionScaleDialog()
{
Init();
}
void ddPrecisionScaleDialog::Init( )
{
m_value1 = wxT("0");
m_value2 = wxT("0");
}
//Transfer data to the window
bool ddPrecisionScaleDialog::TransferDataToWindow()
{
txtPrecision->SetValue(m_value1);
txtScale->SetValue(m_value2);
return true;
}
//Transfer data from the window
bool ddPrecisionScaleDialog::TransferDataFromWindow()
{
m_value1 = txtPrecision->GetValue();
m_value2 = txtScale->GetValue();
return true;
}

View file

@ -0,0 +1,316 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// ddSelectKindFksDialog.cpp - Utility dialog class to allow user select destination of fk: automatically generated or existing column
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
#include <wx/statline.h>
#include "wx/stattext.h"
// App headers
#include "dd/dditems/utilities/ddSelectKindFksDialog.h"
#include "dd/dditems/figures/ddTableFigure.h"
#include "hotdraw/main/hdDrawingView.h"
#include "hotdraw/main/hdDrawingEditor.h"
IMPLEMENT_CLASS( ddSelectKindFksDialog, wxDialog )
BEGIN_EVENT_TABLE( ddSelectKindFksDialog, wxDialog )
EVT_BUTTON(wxID_OK, ddSelectKindFksDialog::OnOkButtonClicked)
EVT_BUTTON(wxID_CANCEL, ddSelectKindFksDialog::OnCancelButtonClicked)
EVT_CHOICE(DDSELECTKINDFK, ddSelectKindFksDialog::OnChoiceFkKind)
END_EVENT_TABLE()
ddSelectKindFksDialog::ddSelectKindFksDialog( wxWindow *parent,
ddRelationshipFigure *relation,
wxWindowID id,
const wxPoint &pos,
const wxSize &size,
long style
)
{
tablesRelation = relation;
Init();
Create(parent, id, pos, size, style);
}
ddSelectKindFksDialog::~ddSelectKindFksDialog()
{
//before delete all items inside to free memory
deleteColsControls();
delete ok;
delete line;
}
ddSelectKindFksDialog::ddSelectKindFksDialog()
{
Init();
}
void ddSelectKindFksDialog::Init( )
{
// choices.clear(); and delete all items inside hash map
}
bool ddSelectKindFksDialog::Create( wxWindow *parent,
wxWindowID id,
const wxPoint &pos = wxDefaultPosition,
const wxSize &size = wxDefaultSize,
long style = wxCAPTION )
{
if (!wxDialog::Create( parent, id, wxT("Select Foreign Key Mapping for each Column"), pos, size, style ))
return false;
CreateControls();
// This fits the dialog to the minimum size dictated by
// the sizers
GetSizer()->Fit(this);
// This ensures that the dialog cannot be sized smaller
// than the minimum size
GetSizer()->SetSizeHints(this);
// Centre the dialog on the parent or (if none) screen
Centre();
return true;
}
void ddSelectKindFksDialog::CreateControls()
{
// A top-level sizer
topSizer = new wxBoxSizer(wxVERTICAL );
this->SetSizer(topSizer);
// A wxChoice to choice kind of foreign key: from Uk or from pk
wxArrayString kindFks;
kindFks.Add(_("From Primary"));
int i, last = tablesRelation->getStartTable()->getUkConstraintsNames().Count();
wxString tmp;
for(i = 0; i < last; i++)
{
tmp = _("Unique Key: ");
tmp += tablesRelation->getStartTable()->getUkConstraintsNames()[i];
kindFks.Add(tmp);
}
kindFkCtrl = new wxChoice(this, DDSELECTKINDFK, wxDefaultPosition, wxDefaultSize, kindFks);
kindFkCtrl->SetStringSelection(_("From Primary"));
topSizer->Add(kindFkCtrl, 0, wxALIGN_CENTER, 5);
// A dividing line before the mapping items and kind of mapping
line = new wxStaticLine ( this, wxID_STATIC,
wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
topSizer->Add(line, 0, wxGROW | wxALL, 5);
//Hack to allow multiple pairs of wxStaticText wxchoice at dialog
colsTopSizer = new wxBoxSizer(wxVERTICAL );
topSizer->Add(colsTopSizer);
populateColumnsControls(true, -1);
// A dividing line before the OK and Cancel buttons
line = new wxStaticLine ( this, wxID_STATIC,
wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
topSizer->Add(line, 0, wxGROW | wxALL, 5);
// A horizontal box sizer to contain Reset, OK, Cancel and Help
okCancelBox = new wxBoxSizer(wxHORIZONTAL);
topSizer->Add(okCancelBox, 0, wxALIGN_CENTER_HORIZONTAL | wxALL, 5);
// The OK button
ok = new wxButton ( this, wxID_OK, wxT("&OK"),
wxDefaultPosition, wxDefaultSize, 0 );
okCancelBox->Add(ok, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
// The Cancel button
cancel = new wxButton ( this, wxID_CANCEL, wxT("&Cancel"),
wxDefaultPosition, wxDefaultSize, 0 );
okCancelBox->Add(cancel, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
}
void ddSelectKindFksDialog::OnChoiceFkKind(wxCommandEvent &event)
{
int selectedUk = kindFkCtrl->GetSelection() == 0 ? -1 : kindFkCtrl->GetSelection() - 1;
bool frompk = kindFkCtrl->GetSelection() == 0;
populateColumnsControls(frompk, selectedUk);
}
void ddSelectKindFksDialog::deleteColsControls()
{
choicesControlsHashMap::iterator it;
bool repeat;
ddSelectFkKindLine *columnLine;
do
{
repeat = false;
for (it = choices.begin(); it != choices.end(); ++it)
{
wxString key = it->first;
columnLine = it->second;
choices.erase(it);
delete columnLine;
repeat = true;
if (repeat)
break;
}
}
while(repeat);
colsTopSizer->Clear();
choices.clear();
}
void ddSelectKindFksDialog::populateColumnsControls(bool primaryKey, int useUkIndex)
{
// Adding all columns mapping items
ddTableFigure *source = (ddTableFigure *) tablesRelation->getStartFigure();
ddTableFigure *destination = (ddTableFigure *) tablesRelation->getEndFigure();
//Delete existing controllers
deleteColsControls();
//Populate controllers
if(primaryKey)
{
wxArrayString sourceCols = source->getAllFkSourceColsNames(true);
wxArrayString destCols = destination->getAllColumnsNames();
destCols.Insert(_("Automatically Generated"), 0);
int i, last = sourceCols.Count();
int eventID;
for(i = 0; i < last; i++)
{
// A sizer to allow lines of controls
linesSizer = new wxBoxSizer(wxHORIZONTAL );
colsTopSizer->Add(linesSizer, 0, wxALIGN_CENTER_HORIZONTAL | wxALL, 5);
//Create controls and put inside linesizer
eventID = DDCHOICESELECTBASE + i;
ddSelectFkKindLine *newLineControls = new ddSelectFkKindLine(this, sourceCols[i], destCols, eventID);
choices[ sourceCols[i] ] = newLineControls;
linesSizer->Add(newLineControls->sourceCtrl, 0, wxALIGN_LEFT | wxALL, 5);
linesSizer->Add(newLineControls->destinationCtrl, 0, wxGROW | wxALL, 5);
}
}
else
{
wxArrayString sourceCols = source->getAllFkSourceColsNames(false, useUkIndex);
wxArrayString destCols = destination->getAllColumnsNames();
destCols.Insert(_("Automatically Generated"), 0);
int i, last = sourceCols.Count();
int eventID;
for(i = 0; i < last; i++)
{
// A sizer to allow lines of controls
linesSizer = new wxBoxSizer(wxHORIZONTAL );
colsTopSizer->Add(linesSizer, 0, wxALIGN_CENTER_HORIZONTAL | wxALL, 5);
//Create controls and put inside linesizer
eventID = DDCHOICESELECTBASE + i;
ddSelectFkKindLine *newLineControls = new ddSelectFkKindLine(this, sourceCols[i], destCols, eventID);
choices[ sourceCols[i] ] = newLineControls;
linesSizer->Add(newLineControls->sourceCtrl, 0, wxALIGN_LEFT | wxALL, 5);
linesSizer->Add(newLineControls->destinationCtrl, 0, wxGROW | wxALL, 5);
}
}
this->Layout();
this->InvalidateBestSize();
this->Fit();
topSizer->RecalcSizes();
}
//Transfer data to the window
bool ddSelectKindFksDialog::TransferDataToWindow()
{
return true;
}
//Transfer data from the window
bool ddSelectKindFksDialog::TransferDataFromWindow()
{
return true;
}
void ddSelectKindFksDialog::OnEnterPressed( wxCommandEvent &event )
{
if (event.GetEventType() == wxEVT_COMMAND_TEXT_ENTER)
{
if ( Validate() && TransferDataFromWindow() )
{
if ( IsModal() )
EndModal(wxID_OK); // If modal
else
{
SetReturnCode(wxID_OK);
this->Show(false); // If modeless
}
}
}
}
void ddSelectKindFksDialog::OnCancelButtonClicked( wxCommandEvent &event )
{
//Do nothing, just return wxID_CANCEL to don't allow connection
event.Skip();
}
void ddSelectKindFksDialog::OnOkButtonClicked( wxCommandEvent &event )
{
choicesControlsHashMap::iterator it;
ddSelectFkKindLine *lineOfCtrls;
int selectedUk = kindFkCtrl->GetSelection() == 0 ? -1 : kindFkCtrl->GetSelection() - 1;
bool fromPk = kindFkCtrl->GetSelection() == 0;
tablesRelation->setFkFrom(fromPk, selectedUk); //true or bigger from zero both are mutually exclusive
for( it = choices.begin(); it != choices.end(); ++it )
{
wxString key = it->first;
lineOfCtrls = it->second;
if(lineOfCtrls->destinationCtrl->GetSelection() != 0) //No automatic Generated
{
ddColumnFigure *col = tablesRelation->getStartTable()->getColumnByName(lineOfCtrls->sourceCtrl->GetLabel());
tablesRelation->addExistingColumnFk(col, lineOfCtrls->destinationCtrl->GetString(lineOfCtrls->destinationCtrl->GetSelection()));
}
}
event.Skip();
}
ddSelectFkKindLine::ddSelectFkKindLine(wxWindow *parent, wxString sourceColumn, wxArrayString possibleTargets, wxWindowID eventId)
{
sourceCtrl = new wxStaticText(parent, wxID_STATIC, sourceColumn, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
destinationCtrl = new wxChoice(parent, eventId, wxDefaultPosition, wxDefaultSize, possibleTargets);
destinationCtrl->SetStringSelection(_("Automatically Generated"));
}
ddSelectFkKindLine::ddSelectFkKindLine()
{
sourceCtrl = NULL;
destinationCtrl = NULL;
}
ddSelectFkKindLine::~ddSelectFkKindLine()
{
delete sourceCtrl;
delete destinationCtrl;
}

View file

@ -0,0 +1,78 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// ddTableNameDialog.cpp - Utility dialog class to allow user input of table name and short name
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
#include <wx/statline.h>
// App headers
#include "dd/dditems/utilities/ddTableNameDialog.h"
#include "dd/dditems/figures/ddTableFigure.h"
#define txtUsualTableName CTRL_TEXT("txtUsualTableName")
BEGIN_EVENT_TABLE(ddTableNameDialog, pgDialog)
END_EVENT_TABLE()
ddTableNameDialog::ddTableNameDialog( wxWindow *parent,
const wxString &defaultValue1,
const wxString &defaultValue2,
ddTextTableItemFigure *tableItem
) :
pgDialog()
{
SetFont(settings->GetSystemFont());
LoadResource(parent, wxT("ddTableNameDialog"));
RestorePosition();
Init();
tabItem = tableItem;
checkGenerate = false;
SetValue1(defaultValue1);
SetValue2(defaultValue2);
txtUsualTableName->SetFocus();
}
ddTableNameDialog::~ddTableNameDialog()
{
}
ddTableNameDialog::ddTableNameDialog() :
pgDialog()
{
Init();
}
void ddTableNameDialog::Init( )
{
m_value1 = wxEmptyString;
m_value2 = wxEmptyString;
}
//Transfer data to the window
bool ddTableNameDialog::TransferDataToWindow()
{
txtUsualTableName->SetValue(m_value1);
return true;
}
//Transfer data from the window
bool ddTableNameDialog::TransferDataFromWindow()
{
m_value1 = txtUsualTableName->GetValue();
return true;
}

View file

@ -0,0 +1,17 @@
#######################################################################
#
# pgAdmin III - PostgreSQL Tools
#
# Copyright (C) 2002 - 2016, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
# module.mk - pgadmin/dd/dditems/utilities/ Makefile fragment
#
#######################################################################
pgadmin3_SOURCES += \
dd/dditems/utilities/ddPrecisionScaleDialog.cpp \
dd/dditems/utilities/ddSelectKindFksDialog.cpp \
dd/dditems/utilities/ddTableNameDialog.cpp
EXTRA_DIST += \
dd/dditems/utilities/module.mk

View file

@ -0,0 +1,42 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// ddBrowserDataContainer.cpp - Item to contain data for each treview child.
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
// App headers
#include "dd/ddmodel/ddBrowserDataContainer.h"
#include "hotdraw/figures/hdIFigure.h"
ddBrowserDataContainer::ddBrowserDataContainer(hdIFigure *data)
{
figure = data;
}
// Destructor
ddBrowserDataContainer::~ddBrowserDataContainer()
{
}
int ddBrowserDataContainer::getFigureKindId()
{
return figure->getKindId();
}
hdIFigure *ddBrowserDataContainer::getFigure()
{
return figure;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,710 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// ddDatabaseDesign.cpp - Manages all design related info and contains all model(s) and tables.
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
// libxml2 headers
#include <libxml/xmlwriter.h>
#include <libxml/xmlreader.h>
// App headers
#include "dd/ddmodel/ddDatabaseDesign.h"
#include "hotdraw/tools/hdSelectionTool.h"
#include "dd/dditems/figures/ddTableFigure.h"
#include "dd/dditems/figures/ddRelationshipFigure.h"
#include "dd/dditems/utilities/ddDataType.h"
#include "dd/ddmodel/ddDrawingEditor.h"
#include "dd/dditems/figures/xml/ddXmlStorage.h"
#include "dd/ddmodel/ddModelBrowser.h"
ddDatabaseDesign::ddDatabaseDesign(wxWindow *parent, wxWindow *frmOwner)
{
editor = new ddDrawingEditor(parent, frmOwner, this);
attachedBrowser = NULL;
}
ddDatabaseDesign::~ddDatabaseDesign()
{
if(editor)
delete editor;
}
ddDrawingEditor *ddDatabaseDesign::getEditor()
{
return editor;
}
hdDrawingView *ddDatabaseDesign::getView(int diagramIndex)
{
return editor->getExistingView(diagramIndex);
}
void ddDatabaseDesign::registerBrowser(ddModelBrowser *browser)
{
attachedBrowser = browser;
}
void ddDatabaseDesign::addTableToModel(hdIFigure *figure)
{
editor->addModelFigure(figure);
if(attachedBrowser)
{
attachedBrowser->refreshFromModel();
}
}
void ddDatabaseDesign::addTableToView(int diagramIndex, hdIFigure *figure)
{
editor->addDiagramFigure(diagramIndex, figure);
if(attachedBrowser)
{
attachedBrowser->refreshFromModel();
}
}
void ddDatabaseDesign::refreshBrowser()
{
if(attachedBrowser)
{
attachedBrowser->refreshFromModel();
}
}
void ddDatabaseDesign::removeTable(int diagramIndex, hdIFigure *figure)
{
editor->removeDiagramFigure(diagramIndex, figure);
}
void ddDatabaseDesign::refreshDraw(int diagramIndex)
{
editor->getExistingView(diagramIndex)->Refresh();
}
void ddDatabaseDesign::eraseDiagram(int diagramIndex)
{
editor->getExistingDiagram(diagramIndex)->removeAllFigures();
}
void ddDatabaseDesign::emptyModel()
{
editor->deleteAllModelFigures();
if(attachedBrowser)
{
attachedBrowser->refreshFromModel();
}
}
bool ddDatabaseDesign::validateModel(wxString &errors)
{
bool out = true;
hdIteratorBase *iterator = editor->modelFiguresEnumerator();
hdIFigure *tmpFigure;
ddTableFigure *table;
while(iterator->HasNext())
{
tmpFigure = (hdIFigure *)iterator->Next();
if(tmpFigure->getKindId() == DDTABLEFIGURE)
{
table = (ddTableFigure *)tmpFigure;
if(!table->validateTable(errors))
{
out = false;
}
}
}
delete iterator;
return out;
}
wxString ddDatabaseDesign::generateList(wxArrayString tables, wxArrayInt options, pgConn *connection, wxString schemaName)
{
int i;
// Validate
if(tables.Count() != options.Count())
{
// shouldn't it be a WXASSERT?
wxMessageBox(_("Invalid number of arguments in call of function generate tables of list"), _("Error at generation process"), wxICON_ERROR | wxOK);
return wxEmptyString;
}
int tablesCount = tables.Count();
for(i = 0; i < tablesCount; i++)
{
ddTableFigure *table = getTable(tables[i]);
if(table == NULL)
{
// shouldn't it be a WXASSERT?
wxMessageBox(_("Metadata of table to be generated not found at database designer model"), _("Error at generation process"), wxICON_ERROR | wxOK);
return wxEmptyString;
}
}
// Start building of CREATE + ALTER PK(s) + ALTER UK(s) + ALTER FK(s)
wxString out;
out += wxT(" \n");
out += wxT("--\n-- ");
out += _("Generating Create sentence(s) for table(s) ");
out += wxT(" \n--\n");
out += wxT(" \n");
for(i = 0; i < tablesCount; i++)
{
if(options[i] == DDGENCREATE || options[i] == DDGENDROPCRE)
{
ddTableFigure *table = getTable(tables[i]);
if(options[i] == DDGENDROPCRE)
{
out += wxT(" \n");
out += wxT("DROP TABLE \"") + table->getTableName() + wxT("\";");
out += wxT(" \n");
}
out += wxT(" \n");
out += table->generateSQLCreate(schemaName);
out += wxT(" \n");
}
}
out += wxT(" \n");
out += wxT(" \n");
out += wxT(" \n");
out += wxT("--\n-- ");
out += _("Generating Pk sentence for table(s) ");
out += wxT(" \n--\n");
out += wxT(" \n");
out += wxT(" \n");
for(i = 0; i < tablesCount; i++)
{
if(options[i] == DDGENCREATE || options[i] == DDGENDROPCRE)
{
ddTableFigure *table = getTable(tables[i]);
out += table->generateSQLAlterPks(schemaName);
}
}
out += wxT(" \n");
out += wxT(" \n");
out += wxT(" \n");
out += wxT("--\n-- ");
out += _("Generating Uk sentence(s) for table(s) ");
out += wxT(" \n--\n");
out += wxT(" \n");
out += wxT(" \n");
for(i = 0; i < tablesCount; i++)
{
if(options[i] == DDGENCREATE || options[i] == DDGENDROPCRE)
{
ddTableFigure *table = getTable(tables[i]);
out += table->generateSQLAlterUks(schemaName);
}
}
out += wxT(" \n");
out += wxT(" \n");
out += wxT(" \n");
out += wxT("--\n-- ");
out += _("Generating Fk sentence(s) for table(s) ");
out += wxT(" \n--\n");
out += wxT(" \n");
out += wxT(" \n");
for(i = 0; i < tablesCount; i++)
{
if(options[i] == DDGENCREATE || options[i] == DDGENDROPCRE)
{
ddTableFigure *table = getTable(tables[i]);
out += table->generateSQLAlterFks(schemaName);
}
}
//Start generation of alter table instead of create
//Check there is some
int countAlter = 0;
for(i = 0; i < tablesCount; i++)
{
if(options[i] == DDGENALTER)
{
countAlter++;
}
}
if(countAlter > 0 && connection == NULL)
{
wxMessageBox(_("No connection found when building ALTER objects DDL."), _("Error at generation process"), wxICON_ERROR | wxOK);
return out;
}
else if(countAlter > 0 && connection != NULL)
{
if(schemaName.IsEmpty())
{
wxMessageBox(_("Schema defined when building ALTER TABLE DDL"), _("Error at generation process"), wxICON_ERROR | wxOK);
return out;
}
out += wxT(" \n");
out += wxT(" \n");
out += wxT(" \n");
out += wxT("--\n-- ");
out += _("Generating Alter table sentence(s) for table(s) ");
out += wxT(" \n--\n");
out += wxT(" \n");
out += wxT(" \n");
for(i = 0; i < tablesCount; i++)
{
if(options[i] == DDGENALTER)
{
ddTableFigure *table = getTable(tables[i]);
out += table->generateAltersTable(connection, schemaName, this);
out += wxT(" \n");
}
}
}
return out;
}
wxArrayString ddDatabaseDesign::getModelTables()
{
wxArrayString out;
hdIteratorBase *iterator = editor->modelFiguresEnumerator();
hdIFigure *tmp;
ddTableFigure *table;
while(iterator->HasNext())
{
tmp = (hdIFigure *)iterator->Next();
if(tmp->getKindId() == DDTABLEFIGURE)
{
table = (ddTableFigure *)tmp;
out.Add(table->getTableName());
}
}
return out;
}
wxString ddDatabaseDesign::generateModel(wxString schemaName)
{
wxString out;
hdIteratorBase *iterator = editor->modelFiguresEnumerator();
hdIFigure *tmp;
ddTableFigure *table;
out += wxT(" \n");
out += wxT("--\n-- ");
out += _("Generating Create sentence(s) for table(s) ");
out += wxT(" \n--\n");
out += wxT(" \n");
while(iterator->HasNext())
{
tmp = (hdIFigure *)iterator->Next();
if(tmp->getKindId() == DDTABLEFIGURE)
{
out += wxT(" \n");
table = (ddTableFigure *)tmp;
out += table->generateSQLCreate(schemaName);
out += wxT(" \n");
}
}
out += wxT(" \n");
out += wxT(" \n");
out += wxT(" \n");
out += wxT("--\n-- ");
out += _("Generating Pk sentence for table(s) ");
out += wxT(" \n--\n");
out += wxT(" \n");
out += wxT(" \n");
iterator->ResetIterator();
while(iterator->HasNext())
{
tmp = (hdIFigure *)iterator->Next();
if(tmp->getKindId() == DDTABLEFIGURE)
{
table = (ddTableFigure *)tmp;
out += table->generateSQLAlterPks(schemaName);
}
}
out += wxT(" \n");
out += wxT(" \n");
out += wxT(" \n");
out += wxT("--\n-- ");
out += _("Generating Uk sentence(s) for table(s) ");
out += wxT(" \n--\n");
out += wxT(" \n");
out += wxT(" \n");
iterator->ResetIterator();
while(iterator->HasNext())
{
tmp = (hdIFigure *)iterator->Next();
if(tmp->getKindId() == DDTABLEFIGURE)
{
table = (ddTableFigure *)tmp;
out += table->generateSQLAlterUks(schemaName);
}
}
out += wxT(" \n");
out += wxT(" \n");
out += wxT(" \n");
out += wxT("--\n-- ");
out += _("Generating Fk sentence(s) for table(s) ");
out += wxT(" \n--\n");
out += wxT(" \n");
out += wxT(" \n");
iterator->ResetIterator();
while(iterator->HasNext())
{
tmp = (hdIFigure *)iterator->Next();
if(tmp->getKindId() == DDTABLEFIGURE)
{
table = (ddTableFigure *)tmp;
out += table->generateSQLAlterFks(schemaName);
}
}
delete iterator;
return out;
}
wxArrayString ddDatabaseDesign::getDiagramTables(int diagramIndex)
{
wxArrayString out;
hdIteratorBase *iterator = editor->getExistingDiagram(diagramIndex)->figuresEnumerator();
hdIFigure *tmp;
ddTableFigure *table;
while(iterator->HasNext())
{
tmp = (hdIFigure *)iterator->Next();
if(tmp->getKindId() == DDTABLEFIGURE)
{
table = (ddTableFigure *)tmp;
out.Add(table->getTableName());
}
}
return out;
}
wxString ddDatabaseDesign::generateDiagram(int diagramIndex, wxString schemaName)
{
wxString out;
hdIteratorBase *iterator = editor->getExistingDiagram(diagramIndex)->figuresEnumerator();
hdIFigure *tmp;
ddTableFigure *table;
out += wxT(" \n");
out += wxT("--\n-- ");
out += _("Generating Create sentence(s) for table(s) ");
out += wxT(" \n--\n");
out += wxT(" \n");
while(iterator->HasNext())
{
tmp = (hdIFigure *)iterator->Next();
if(tmp->getKindId() == DDTABLEFIGURE)
{
out += wxT(" \n");
table = (ddTableFigure *)tmp;
out += table->generateSQLCreate(schemaName);
out += wxT(" \n");
}
}
out += wxT(" \n");
out += wxT(" \n");
out += wxT(" \n");
out += wxT("--\n-- ");
out += _("Generating Pk sentence for table(s) ");
out += wxT(" \n--\n");
out += wxT(" \n");
out += wxT(" \n");
iterator->ResetIterator();
while(iterator->HasNext())
{
tmp = (hdIFigure *)iterator->Next();
if(tmp->getKindId() == DDTABLEFIGURE)
{
table = (ddTableFigure *)tmp;
out += table->generateSQLAlterPks(schemaName);
}
}
out += wxT(" \n");
out += wxT(" \n");
out += wxT(" \n");
out += wxT("--\n-- ");
out += _("Generating Uk sentence(s) for table(s) ");
out += wxT(" \n--\n");
out += wxT(" \n");
out += wxT(" \n");
iterator->ResetIterator();
while(iterator->HasNext())
{
tmp = (hdIFigure *)iterator->Next();
if(tmp->getKindId() == DDTABLEFIGURE)
{
table = (ddTableFigure *)tmp;
out += table->generateSQLAlterUks(schemaName);
}
}
out += wxT(" \n");
out += wxT(" \n");
out += wxT(" \n");
out += wxT("--\n-- ");
out += _("Generating Fk sentence(s) for table(s) ");
out += wxT(" \n--\n");
out += wxT(" \n");
out += wxT(" \n");
iterator->ResetIterator();
while(iterator->HasNext())
{
tmp = (hdIFigure *)iterator->Next();
if(tmp->getKindId() == DDTABLEFIGURE)
{
table = (ddTableFigure *)tmp;
out += table->generateSQLAlterFks(schemaName);
}
}
delete iterator;
return out;
}
ddTableFigure *ddDatabaseDesign::getSelectedTable(int diagramIndex)
{
hdIteratorBase *iterator = editor->getExistingDiagram(diagramIndex)->figuresEnumerator();
hdIFigure *tmp;
ddTableFigure *table = NULL;
while(iterator->HasNext())
{
tmp = (hdIFigure *)iterator->Next();
if (tmp->isSelected(diagramIndex) && tmp->getKindId() == DDTABLEFIGURE)
table = (ddTableFigure *)tmp;
}
delete iterator;
return table;
}
ddTableFigure *ddDatabaseDesign::getTable(wxString tableName)
{
ddTableFigure *out = NULL;
hdIteratorBase *iterator = editor->modelFiguresEnumerator();
hdIFigure *tmp;
ddTableFigure *table;
while(iterator->HasNext())
{
tmp = (hdIFigure *)iterator->Next();
if(tmp->getKindId() == DDTABLEFIGURE)
{
table = (ddTableFigure *)tmp;
if(table->getTableName().IsSameAs(tableName, false))
{
out = table;
}
}
}
delete iterator;
return out;
}
#define XML_FROM_WXSTRING(s) ((xmlChar *)(const char *)s.mb_str(wxConvUTF8))
#define WXSTRING_FROM_XML(s) wxString((char *)s, wxConvUTF8)
bool ddDatabaseDesign::writeXmlModel(wxString file)
{
int rc;
xmlWriter = xmlNewTextWriterFilename(file.mb_str(wxConvUTF8), 0);
if (xmlWriter == NULL)
{
wxMessageBox(_("Failed to write the model file!"), _("Error"), wxICON_ERROR | wxOK);
return false;
}
rc = xmlTextWriterStartDocument(xmlWriter, NULL, "UTF-8" , NULL);
if(rc < 0)
{
wxMessageBox(_("Failed to write the model file!"), _("Error"), wxICON_ERROR | wxOK);
return false;
}
else
{
ddXmlStorage::StartModel(xmlWriter, this);
//initialize IDs of tables
mappingNameToId.clear();
hdIteratorBase *iterator = editor->modelFiguresEnumerator();
hdIFigure *tmp;
ddTableFigure *table;
int nextID = 10;
while(iterator->HasNext())
{
tmp = (hdIFigure *)iterator->Next();
if(tmp->getKindId() == DDTABLEFIGURE)
{
table = (ddTableFigure *)tmp;
mappingNameToId[table->getTableName()] = wxString::Format(wxT("TID%d"), nextID);
nextID += 10;
}
}
delete iterator;
//Create table xml info
iterator = editor->modelFiguresEnumerator();
while(iterator->HasNext())
{
tmp = (hdIFigure *)iterator->Next();
if(tmp->getKindId() == DDTABLEFIGURE)
{
table = (ddTableFigure *)tmp;
ddXmlStorage::Write(xmlWriter, table);
}
}
delete iterator;
//Create relationships xml info
ddRelationshipFigure *relationship;
iterator = editor->modelFiguresEnumerator();
while(iterator->HasNext())
{
tmp = (hdIFigure *)iterator->Next();
if(tmp->getKindId() == DDRELATIONSHIPFIGURE)
{
relationship = (ddRelationshipFigure *)tmp;
ddXmlStorage::Write(xmlWriter, relationship);
}
}
delete iterator;
//Create Diagrams xml info
ddXmlStorage::StarDiagrams(xmlWriter);
iterator = editor->diagramsEnumerator();
hdDrawing *tmpDiagram;
while(iterator->HasNext())
{
tmpDiagram = (hdDrawing *)iterator->Next();
ddXmlStorage::WriteLocal(xmlWriter, tmpDiagram);
}
delete iterator;
ddXmlStorage::EndDiagrams(xmlWriter);
//End model xml info
ddXmlStorage::EndModel(xmlWriter);
xmlTextWriterEndDocument(xmlWriter);
xmlFreeTextWriter(xmlWriter);
return true;
}
return false;
}
bool ddDatabaseDesign::readXmlModel(wxString file, ctlAuiNotebook *notebook)
{
emptyModel();
mappingIdToName.clear();
//Initial Parse Model
xmlTextReaderPtr reader = xmlReaderForFile(file.mb_str(wxConvUTF8), NULL, 0);
ddXmlStorage::setModel(this);
ddXmlStorage::setModel(this);
ddXmlStorage::initialModelParse(reader);
//Parse Model
xmlReaderNewFile(reader, file.mb_str(wxConvUTF8), NULL, 0);
ddXmlStorage::setModel(this);
ddXmlStorage::setNotebook(notebook);
if(!ddXmlStorage::Read(reader))
{
return false;
}
xmlFreeTextReader(reader);
return true;
}
wxString ddDatabaseDesign::getTableId(wxString tableName)
{
return mappingNameToId[tableName];
}
void ddDatabaseDesign::addTableToMapping(wxString IdKey, wxString tableName)
{
mappingIdToName[IdKey] = tableName;
}
wxString ddDatabaseDesign::getTableName(wxString Id)
{
wxString tableName = wxEmptyString;
tablesMappingHashMap::iterator it;
for (it = mappingIdToName.begin(); it != mappingIdToName.end(); ++it)
{
wxString key = it->first;
if(key.IsSameAs(Id, false))
{
tableName = it->second;
break;
}
}
return tableName;
}
hdDrawing *ddDatabaseDesign::createDiagram(wxWindow *owner, wxString name, bool fromXml)
{
hdDrawing *drawing = editor->createDiagram(owner, fromXml);
drawing->setName(name);
return drawing;
}
void ddDatabaseDesign::deleteDiagram(int diagramIndex, bool deleteView)
{
editor->deleteDiagram(diagramIndex, deleteView);
}
wxString ddDatabaseDesign::getVersionXML()
{
return wxString(_("1.0"));
}
void ddDatabaseDesign::markSchemaOn(wxArrayString tables)
{
int i, tablesCount = tables.Count();
for(i = 0; i < tablesCount; i++)
{
ddTableFigure *table = getTable(tables[i]);
if(table != NULL) //mark on if table exists in other case do nothing
{
table->setBelongsToSchema(true);
}
}
}
void ddDatabaseDesign::unMarkSchemaOnAll()
{
hdIteratorBase *iterator = editor->modelFiguresEnumerator();
hdIFigure *tmpFigure;
ddTableFigure *table;
while(iterator->HasNext())
{
tmpFigure = (hdIFigure *)iterator->Next();
if(tmpFigure->getKindId() == DDTABLEFIGURE)
{
table = (ddTableFigure *)tmpFigure;
table->setBelongsToSchema(false);
}
}
delete iterator;
}

View file

@ -0,0 +1,300 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// hdDrawingEditor.cpp - Main class that manages all other classes
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
#include <wx/dcbuffer.h>
// App headers
#include "dd/ddmodel/ddDrawingEditor.h"
#include "dd/dditems/utilities/ddTableNameDialog.h"
#include "dd/ddmodel/ddDatabaseDesign.h"
#include "dd/ddmodel/ddDrawingView.h"
#include "hotdraw/utilities/hdRemoveDeleteDialog.h"
#include "dd/dditems/figures/ddRelationshipFigure.h"
#include "frm/frmDatabaseDesigner.h"
ddDrawingEditor::ddDrawingEditor(wxWindow *owner, wxWindow *frmOwner, ddDatabaseDesign *design)
: hdDrawingEditor(owner, true)
{
databaseDesign = design;
frm = (frmDatabaseDesigner *) frmOwner;
}
hdDrawing *ddDrawingEditor::createDiagram(wxWindow *owner, bool fromXml)
{
hdDrawing *_tmpModel = new hdDrawing(this);
ddDrawingView *_viewTmp = new ddDrawingView(_diagrams->count(), owner, this, wxSize(1200, 1200), _tmpModel);
// Set Scroll Bar & split
_viewTmp->SetScrollbars( 10, 10, 127, 80 );
_viewTmp->EnableScrolling(true, true);
_viewTmp->AdjustScrollbars();
_tmpModel->registerView(_viewTmp);
_viewTmp->SetDropTarget(new ddDropTarget(databaseDesign, _tmpModel));
if(!fromXml)
{
//Add a new position inside each figure to allow use of this new diagram existing figures.
int i;
hdIFigure *tmp;
for(i = 0; i < _model->count(); i++)
{
tmp = (hdIFigure *) _model->getItemAt(i);
tmp->AddPosForNewDiagram();
}
}
//Add Diagram
_diagrams->addItem((hdObject *) _tmpModel);
modelChanged = true;
return _tmpModel;
}
void ddDrawingEditor::remOrDelSelFigures(int diagramIndex)
{
hdIFigure *tmp;
ddTableFigure *table;
ddRelationshipFigure *relation;
hdIteratorBase *iterator;
hdCollection *tmpSelection;
int answer;
int numbTables = 0;
int numbRelationships = 0;
if (getExistingDiagram(diagramIndex)->countSelectedFigures() == 1)
{
tmp = (hdIFigure *) getExistingDiagram(diagramIndex)->selectedFigures()->getItemAt(0);
if(tmp->getKindId() == DDTABLEFIGURE)
{
numbTables = 1;
table = (ddTableFigure *)tmp;
hdRemoveDeleteDialog dialog(_("Are you sure you wish to delete table ") + table->getTableName() + wxT("?"), _("Delete table?"), getExistingView(diagramIndex));
answer = dialog.ShowModal();
}
if(tmp->getKindId() == DDRELATIONSHIPFIGURE)
{
numbRelationships = 1;
relation = (ddRelationshipFigure *)tmp;
//Relationship can be delete only NOT REMOVED
hdRemoveDeleteDialog dialog2(_("Are you sure you wish to delete relationship ") + relation->getConstraintName() + wxT("?"), _("Delete relationship?"), getExistingView(diagramIndex), false);
answer = dialog2.ShowModal();
}
}
else if (getExistingDiagram(diagramIndex)->countSelectedFigures() > 1)
{
numbTables = 0;
iterator = getExistingDiagram(diagramIndex)->selectionFigures();
while(iterator->HasNext())
{
tmp = (hdIFigure *)iterator->Next();
if(tmp->getKindId() == DDTABLEFIGURE)
numbTables++;
}
delete iterator;
//Improve messages to display about relationships and tables and only relationship
hdRemoveDeleteDialog dialog3(
wxString::Format(_("Are you sure you wish to delete %d tables, removing from model related relationships?"), numbTables),
_("Delete tables?"), getExistingView(diagramIndex));
answer = dialog3.ShowModal();
}
if (answer == DD_DELETE || answer == DD_REMOVE)
{
modelChanged = true;
tmpSelection = new hdCollection(new hdArrayCollection());
//Preprocess relationships counting and storing at temporary collection
numbRelationships = 0;
iterator = getExistingDiagram(diagramIndex)->selectionFigures();
while(iterator->HasNext())
{
tmp = (hdIFigure *)iterator->Next();
if(tmp->getKindId() == DDRELATIONSHIPFIGURE)
{
numbRelationships++;
tmpSelection->addItem(tmp);
}
}
delete iterator;
while(numbTables > 0)
{
tmp = (hdIFigure *) getExistingDiagram(diagramIndex)->selectedFigures()->getItemAt(0);
if(tmp->getKindId() == DDTABLEFIGURE)
{
table = (ddTableFigure *)tmp;
if(table && answer == DD_REMOVE)
{
getExistingDiagram(diagramIndex)->removeFromSelection(table);
getExistingDiagram(diagramIndex)->remove(table);
}
//if table is going to be delete all others diagrams should be alerted about it
if(table && answer == DD_DELETE)
{
removeFromAllSelections(table);
table->processDeleteAlert(getExistingDiagram(diagramIndex));
deleteModelFigure(table);
databaseDesign->refreshBrowser();
}
numbTables--;
}
else if(tmp->getKindId() == DDRELATIONSHIPFIGURE)
{
getExistingDiagram(diagramIndex)->removeFromSelection(tmp); //isn't a tables is probably a relationship
}
else
{
wxMessageBox(_("Invalid figure kind found"), _("Error while deleting tables"), wxCENTRE | wxICON_ERROR, getExistingView(diagramIndex));
}
}
if( numbRelationships > 0 )
{
//check again: Are there relationships selected that wasn't delete (only selected relationship not source/destination table)
numbRelationships = 0;
iterator = tmpSelection->createIterator(); //getExistingDiagram(diagramIndex)->selectionFigures();
while(iterator->HasNext())
{
tmp = (hdIFigure *)iterator->Next();
//only way a relationship don't exists at diagram is
//it had been deleted before by deleting source/destination table that owns it
if(getExistingDiagram(diagramIndex)->includes(tmp))
{
if(tmp->getKindId() == DDRELATIONSHIPFIGURE)
{
numbRelationships++;
}
else
{
wxMessageBox(_("Invalid figure kind found"), _("Error while deleting table"), wxCENTRE | wxICON_ERROR, getExistingView(diagramIndex));
}
}
else
{
tmpSelection->removeItem(tmp);
}
}
delete iterator;
while(numbRelationships > 0 && tmpSelection->count() == numbRelationships)
{
tmp = (hdIFigure *) tmpSelection->getItemAt(0);
if(tmp->getKindId() == DDRELATIONSHIPFIGURE)
{
relation = (ddRelationshipFigure *)tmp;
if(relation && answer == DD_REMOVE)
{
getExistingDiagram(diagramIndex)->removeFromSelection(relation);
getExistingDiagram(diagramIndex)->remove(relation);
}
//if relation is going to be delete all others diagrams should be alerted about it
if(relation && answer == DD_DELETE)
{
removeFromAllSelections(relation);
relation->removeForeignKeys();
relation->disconnectEnd();
relation->disconnectStart();
deleteModelFigure(relation);
databaseDesign->refreshBrowser();
}
numbRelationships--;
}
else
{
wxMessageBox(_("Invalid figure kind found"), _("Error while deleting realtionships"), wxCENTRE | wxICON_ERROR, getExistingView(diagramIndex));
// getExistingDiagram(diagramIndex)->removeFromSelection(tmp); //isn't neither a table or relationship
}
}
}
getExistingDiagram(diagramIndex)->clearSelection(); //after delete all items all relationships remains at selection and should be removed
tmpSelection->removeAll();
delete tmpSelection;
}
}
void ddDrawingEditor::checkRelationshipsConsistency(int diagramIndex)
{
hdIFigure *tmp;
ddRelationshipFigure *relation;
hdDrawing *diagram = getExistingDiagram(diagramIndex);
// First Step Removel all orphan [relations without source or destination] relationships
// from DIAGRAM but NOT from MODEL
hdIteratorBase *iterator = diagram->figuresEnumerator();
while(iterator->HasNext())
{
tmp = (hdIFigure *)iterator->Next();
if(tmp->getKindId() == DDRELATIONSHIPFIGURE)
{
relation = (ddRelationshipFigure *)tmp;
//test if all tables of a relationship are included if this is not the case then remove relationship from this diagram
bool sourceExists = diagram->getFiguresCollection()->existsObject(relation->getStartTable());
bool destinationExists = diagram->getFiguresCollection()->existsObject(relation->getEndTable());
if(!sourceExists || !destinationExists)
{
diagram->remove(relation);
}
}
}
delete iterator;
// Now add all existing relationships in MODEL to DIAGRAM if both source and destination
// tables exists at DIAGRAM
iterator = _model->createIterator();
while(iterator->HasNext())
{
tmp = (hdIFigure *)iterator->Next();
if(tmp->getKindId() == DDRELATIONSHIPFIGURE)
{
relation = (ddRelationshipFigure *)tmp;
//test if all tables of a relationship are included if this is the case then include relationship at this diagram
bool sourceExists = diagram->getFiguresCollection()->existsObject(relation->getStartTable());
bool destinationExists = diagram->getFiguresCollection()->existsObject(relation->getEndTable());
bool relationExists = diagram->getFiguresCollection()->existsObject(relation);
if(sourceExists && destinationExists && !relationExists)
{
diagram->add(relation);
relation->updateConnection(diagramIndex);
}
}
}
delete iterator;
}
void ddDrawingEditor::checkAllDigramsRelConsistency()
{
int i, size = modelCount();
for(i = 0; i < size ; i++)
{
checkRelationshipsConsistency(i);
}
}
void ddDrawingEditor::notifyChanged()
{
if(frm)
frm->setModelChanged(true);
modelChanged = true;
}

View file

@ -0,0 +1,90 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// ddDrawingView.cpp - Main canvas where all tables and relationships are drawn
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
#include <wx/dcbuffer.h>
// App headers
#include "dd/ddmodel/ddDrawingView.h"
#include "hotdraw/utilities/hdArrayCollection.h"
#include "dd/dditems/figures/ddTableFigure.h"
#include "dd/dditems/figures/ddRelationshipFigure.h"
#include "dd/dditems/utilities/ddDataType.h"
#include "dd/dditems/utilities/ddTableNameDialog.h"
ddDrawingView::ddDrawingView(int diagram, wxWindow *ddParent, ddDrawingEditor *editor , wxSize size, hdDrawing *drawing)
: hdDrawingView(diagram, ddParent, editor, size, drawing)
{
}
void ddDrawingView::createViewMenu(wxMenu &mnu)
{
mnu.Append(MNU_NEWTABLE, _("Add new Table"));
}
void ddDrawingView::OnGenericViewPopupClick(wxCommandEvent &event)
{
ddDrawingEditor *ed = (ddDrawingEditor *) editor();
switch(event.GetId())
{
case MNU_NEWTABLE:
ddTableNameDialog *newTableDialog = new ddTableNameDialog(
this,
wxEmptyString,
wxEmptyString,
NULL
);
int answer = newTableDialog->ShowModal();
if (answer == wxID_OK && !newTableDialog->GetValue1().IsEmpty())
{
ddTableFigure *newTable = new ddTableFigure(newTableDialog->GetValue1(),
rand() % 90 + 200,
rand() % 90 + 140);
ed->getDesign()->addTableToView(this->getIdx(), newTable);
ed->getDesign()->refreshDraw(this->getIdx());
}
delete newTableDialog;
break;
}
}
ddDropTarget::ddDropTarget(ddDatabaseDesign *sourceDesign, hdDrawing *targetDrawing)
{
target = targetDrawing;
source = sourceDesign;
}
bool ddDropTarget::OnDropText(wxCoord x, wxCoord y, const wxString &text)
{
ddTableFigure *t = source->getTable(text);
if(t != NULL && !target->includes(t))
{
target->add(t);
t->syncInternalsPosAt(target->getView()->getIdx(), x, y);
source->getEditor()->checkRelationshipsConsistency(target->getView()->getIdx());
target->getView()->Refresh();
return true;
}
else
{
if(target->includes(t))
{
wxMessageBox(_("Table exists already at this diagram"), _("Drag and drop warning"), wxICON_EXCLAMATION | wxOK);
return true;
}
else
return false;
}
}

View file

@ -0,0 +1,739 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// ddGenerationWizard.cpp - Wizard to allow generation of tables.
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
#include <wx/regex.h>
// App headers
#include "schema/pgSchema.h"
#include "dd/ddmodel/ddGenerationWizard.h"
#include "dd/ddmodel/ddDBReverseEngineering.h"
#include "gqb/gqbGridRestTable.h"
#include "images/gqbOrderAddAll.pngc"
#include "images/gqbOrderRemoveAll.pngc"
#include "images/gqbOrderRemove.pngc"
#include "images/gqbOrderAdd.pngc"
#include "images/continue.pngc"
const wxColour LIGHT_YELLOW(0xff, 0xff, 0x80);
BEGIN_EVENT_TABLE(ddGenerationWizard, wxWizard)
EVT_WIZARD_FINISHED(wxID_ANY, ddGenerationWizard::OnFinishPressed)
EVT_WIZARD_PAGE_CHANGING(wxID_ANY, ddGenerationWizard::OnWizardPageChanging)
END_EVENT_TABLE()
BEGIN_EVENT_TABLE(SelGenTablesPage, wxWizardPage)
EVT_BUTTON(DDREMOVE, SelGenTablesPage::OnButtonRemove)
EVT_BUTTON(DDREMOVEALL, SelGenTablesPage::OnButtonRemoveAll)
EVT_BUTTON(DDADD, SelGenTablesPage::OnButtonAdd)
EVT_BUTTON(DDADDALL, SelGenTablesPage::OnButtonAddAll)
EVT_WIZARD_PAGE_CHANGING(wxID_ANY, SelGenTablesPage::OnWizardPageChanging)
END_EVENT_TABLE()
BEGIN_EVENT_TABLE(SelGenSchemaPage, wxWizardPage)
EVT_WIZARD_PAGE_CHANGING(wxID_ANY, SelGenSchemaPage::OnWizardPageChanging)
END_EVENT_TABLE()
BEGIN_EVENT_TABLE(ReportGridPage, wxWizardPage)
EVT_WIZARD_PAGE_CHANGING(wxID_ANY, ReportGridPage::OnWizardPageChanging)
END_EVENT_TABLE()
ddGenerationWizard::ddGenerationWizard(wxFrame *frame, ddDatabaseDesign *design, pgConn *connection, bool useSizer)
: wxWizard(frame, wxID_ANY, wxT("Generate DDL from model"),
wxBitmap(*continue_png_bmp), wxDefaultPosition,
wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
{
conn = connection;
figuresDesign = design;
// a wizard page may be either an object of predefined class
initialPage = new wxWizardPageSimple(this);
frontText = new wxStaticText(initialPage, wxID_ANY,
wxT("Build DDL from model tables.\n")
wxT("\n")
wxT("The next pages will allow the built of DDL of the current database designer model.")
wxT("\n\n")
wxT("Restrictions apply when using experimental function ALTER TABLE instead of DROP/CREATE:\n\n")
wxT("1. Database connection is required to check differences with existing tables.\n\n")
wxT("2. Only changes done in the design will be applied to the db, not otherwise.\n\n")
wxT("3. Rename of columns or tables is not (yet) supported.\n\n")
wxT("4. Rename of constraints generate a drop and create, except for primary key constraints.\n\n")
wxT("5. All constraints should have a defined name.\n\n")
, wxPoint(5, 5)
);
page2 = new SelGenTablesPage(this, initialPage);
initialPage->SetNext(page2);
page3 = new SelGenSchemaPage(this, page2);
page2->SetNext(page3);
page4 = new ReportGridPage(this, page3);
page3->SetNext(page4);
if (useSizer)
{
// allow the wizard to size itself around the pages
GetPageAreaSizer()->Add(initialPage);
}
}
ddGenerationWizard::~ddGenerationWizard()
{
}
void ddGenerationWizard::OnFinishPressed(wxWizardEvent &event)
{
wxString strChoicesSimple[1] = {_("Create table")};
wxString strChoicesAlter[4] = {_("Alter table"), _("Drop, then create"), _("Create table [conflict]"), wxT("No action")};
wxArrayString tables;
wxArrayInt options;
if(page4->getGrid()->GetRows() > 0)
{
int i;
for(i = page4->getGrid()->GetRows() - 1; i >= 0; i--)
{
tables.Add(page4->getGrid()->GetCellValue(i, 0));
wxString value = page4->getGrid()->GetCellValue(i, 1);
if(value.IsSameAs(strChoicesAlter[0]))
{
options.Add(DDGENALTER);
}
else if(value.IsSameAs(strChoicesAlter[1]))
{
options.Add(DDGENDROPCRE);
}
else if(value.IsSameAs(strChoicesAlter[2]) || value.IsSameAs(strChoicesSimple[0]))
{
options.Add(DDGENCREATE);
}
else if(value.IsSameAs(strChoicesAlter[3]))
{
options.Add(DDGENNOTHING);
}
}
}
DDL = getDesign()->generateList(tables, options, conn, schemaName);
}
void ddGenerationWizard::OnWizardPageChanging(wxWizardEvent &event)
{
if(event.GetDirection())
{
page2->RefreshTablesList();
}
}
// ----- SelGenTablesPage Implementation
SelGenTablesPage::SelGenTablesPage(wxWizard *parent, wxWizardPage *prev)
: wxWizardPage(parent)
{
wparent = (ddGenerationWizard *) parent;
m_prev = prev;
m_next = NULL;
wxFlexGridSizer *mainSizer = new wxFlexGridSizer(2, 3, 0, 0);
this->SetSizer(mainSizer);
leftText = new wxStaticText(this, wxID_STATIC, _("Table(s) from selected schema"), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER);
mainSizer->Add(leftText);
centerText = new wxStaticText(this, wxID_STATIC, _(" "), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER);
mainSizer->Add(centerText);
rightText = new wxStaticText(this, wxID_STATIC, _("Tables(s) to be generated"), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER);
mainSizer->Add(rightText, wxALIGN_LEFT);
// Left listbox with all tables
m_allTables = new wxListBox( this, DDALLTABS, wxDefaultPosition, wxDefaultSize, 0, NULL, wxLB_EXTENDED | wxLB_ALWAYS_SB | wxLB_SORT);
mainSizer->AddGrowableRow(1);
mainSizer->AddGrowableCol(0);
mainSizer->Add(m_allTables , 1, wxEXPAND);
addBitmap = *gqbOrderAdd_png_bmp;
addAllBitmap = *gqbOrderAddAll_png_bmp;
removeBitmap = *gqbOrderRemove_png_bmp;
removeAllBitmap = *gqbOrderRemoveAll_png_bmp;
buttonAdd = new wxBitmapButton( this, DDADD, addBitmap, wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW, wxDefaultValidator, wxT("Add Column") );
buttonAdd->SetToolTip(_("Add the selected table(s)"));
buttonAddAll = new wxBitmapButton( this, DDADDALL, addAllBitmap, wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW, wxDefaultValidator, wxT("Add All Columns") );
buttonAddAll->SetToolTip(_("Add all tables"));
buttonRemove = new wxBitmapButton( this, DDREMOVE, removeBitmap, wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW, wxDefaultValidator, wxT("Remove Column") );
buttonRemove->SetToolTip(_("Remove the selected table(s)"));
buttonRemoveAll = new wxBitmapButton( this, DDREMOVEALL, removeAllBitmap, wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW, wxDefaultValidator, wxT("Remove All Columns") );
buttonRemoveAll->SetToolTip(_("Remove all tables"));
wxBoxSizer *buttonsSizer = new wxBoxSizer( wxVERTICAL );
buttonsSizer->Add(
this->buttonAdd,
0, // make horizontally unstretchable
wxALL, // make border all around (implicit top alignment)
3 ); // set border width to 3
buttonsSizer->Add(
this->buttonAddAll,
0, // make horizontally unstretchable
wxALL, // make border all around (implicit top alignment)
3 ); // set border width to 3
buttonsSizer->Add(
this->buttonRemove,
0, // make horizontally unstretchable
wxALL, // make border all around (implicit top alignment)
3 ); // set border width to 3
buttonsSizer->Add(
this->buttonRemoveAll,
0, // make horizontally unstretchable
wxALL, // make border all around (implicit top alignment)
3 ); // set border width to 3
mainSizer->Add(
buttonsSizer,
0, // make vertically unstretchable
wxALIGN_CENTER ); // no border and centre horizontally
//right listbox with selected tables from schema to be imported.
m_selTables = new wxListBox( this, DDSELTABS, wxDefaultPosition, wxDefaultSize, 0, NULL, wxLB_EXTENDED | wxLB_ALWAYS_SB | wxLB_SORT);
mainSizer->AddGrowableCol(2);
mainSizer->Add(m_selTables , 1, wxEXPAND);
mainSizer->Fit(this);
}
SelGenTablesPage::~SelGenTablesPage()
{
if(rightText)
delete rightText;
if(centerText)
delete centerText;
if(leftText)
delete leftText;
if(m_allTables)
delete m_allTables;
if(m_selTables)
delete m_selTables;
if(buttonAdd)
delete buttonAdd;
if(buttonAddAll)
delete buttonAddAll;
if(buttonRemove)
delete buttonRemove;
if(buttonRemoveAll)
delete buttonRemoveAll;
}
void SelGenTablesPage::OnButtonAdd(wxCommandEvent &)
{
wxArrayInt positions;
if(m_allTables->GetSelections(positions) > 0)
{
int i;
int size = positions.Count();
for(i = 0; i < size; i++)
{
m_selTables->Append(m_allTables->GetString(positions[i]));
m_allTables->Deselect(positions[i]);
}
for(i = (size - 1); i >= 0 ; i--)
{
m_allTables->Delete(positions[i]);
}
if(m_allTables->GetCount() > 0)
m_allTables->Select(0);
}
}
void SelGenTablesPage::OnButtonAddAll(wxCommandEvent &)
{
int itemsCount = m_allTables->GetCount();
if( itemsCount > 0)
{
do
{
m_allTables->Deselect(0);
m_selTables->Append(m_allTables->GetString(0));
m_allTables->Delete(0);
itemsCount--;
}
while(itemsCount > 0);
}
}
void SelGenTablesPage::OnButtonRemove(wxCommandEvent &)
{
wxArrayInt positions;
if(m_selTables->GetSelections(positions) > 0)
{
int i;
int size = positions.Count(); // Warning about conversion should be ignored
for(i = 0; i < size; i++)
{
m_allTables->Append(m_selTables->GetString(positions[i]));
m_selTables->Deselect(positions[i]);
}
for(i = (size - 1); i >= 0 ; i--)
{
m_selTables->Delete(positions[i]);
}
if(m_selTables->GetCount() > 0)
m_selTables->Select(0);
}
}
void SelGenTablesPage::OnButtonRemoveAll(wxCommandEvent &)
{
int itemsCount = m_selTables->GetCount();
if( itemsCount > 0)
{
do
{
m_selTables->Deselect(0);
m_allTables->Append(m_selTables->GetString(0));
m_selTables->Delete(0);
itemsCount--;
}
while(itemsCount > 0);
}
}
void SelGenTablesPage::moveToSelectList(wxString tableName)
{
int position = m_allTables->FindString(tableName);
if(position != wxNOT_FOUND)
{
m_selTables->Append(m_allTables->GetString(position));
m_allTables->Delete(position);
}
}
void SelGenTablesPage::RefreshTablesList()
{
wxArrayString tablesList;
m_allTables->Clear();
m_selTables->Clear();
hdIteratorBase *iterator = wparent->getDesign()->getEditor()->modelFiguresEnumerator();
hdIFigure *tmpFigure;
ddTableFigure *table;
while(iterator->HasNext())
{
tmpFigure = (hdIFigure *)iterator->Next();
if(tmpFigure->getKindId() == DDTABLEFIGURE)
{
table = (ddTableFigure *)tmpFigure;
tablesList.Add(table->getTableName());
}
}
delete iterator;
m_allTables->Set(tablesList, (void **)NULL);
int max = wparent->preSelTables.Count();
if(max > 0)
{
int i;
for(i = 0; i < max; i++)
{
moveToSelectList(wparent->preSelTables[i]);
}
}
}
void SelGenTablesPage::OnWizardPageChanging(wxWizardEvent &event)
{
if(event.GetDirection() && m_selTables->GetCount() <= 0)
{
wxMessageBox(_("Please select at least one table to move to next step."), _("No tables selected"), wxICON_WARNING | wxOK, this);
event.Veto();
}
}
// ----- SelGenSchemaPage Implementation
SelGenSchemaPage::SelGenSchemaPage(wxWizard *parent, wxWizardPage *prev)
: wxWizardPage(parent)
{
wparent = (ddGenerationWizard *) parent;
m_prev = prev;
m_next = NULL;
// A top-level sizer
wxBoxSizer *topSizer = new wxBoxSizer(wxVERTICAL );
this->SetSizer(topSizer);
//Add a message
message = new wxStaticText(this, wxID_STATIC, _("Please select a schema to use as check target.\n\nThis affects the choice between CREATE/ALTER table(s) matching for the DDL generated depending on whether or not the table exists in that schema."), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
topSizer->Add(message);
topSizer->AddSpacer(10);
//Get Schemas info
if(wparent && wparent->getConnection())
refreshSchemas(wparent->getConnection());
else
schemasNames.Add(_("No schema"));
//Add a listbox with schemas
m_allSchemas = new wxListBox(this, DDALLSCHEMAS, wxDefaultPosition, wxDefaultSize, schemasNames, wxLB_SORT | wxLB_ALWAYS_SB | wxLB_SINGLE);
topSizer->Add(m_allSchemas, 1, wxEXPAND);
}
SelGenSchemaPage::~SelGenSchemaPage()
{
if(m_allSchemas)
delete m_allSchemas;
if(message)
delete message;
}
void SelGenSchemaPage::OnWizardPageChanging(wxWizardEvent &event)
{
if(event.GetDirection() && m_allSchemas->GetSelection() == wxNOT_FOUND)
{
wxMessageBox(_("Please select an item to move to next step."), _("No choice made"), wxICON_WARNING | wxOK, this);
event.Veto();
}
else if(event.GetDirection())
{
if(m_allSchemas->GetSelection() > 0)
{
wparent->OIDSelectedSchema = schemasHM[schemasNames[m_allSchemas->GetSelection()]];
wparent->schemaName = schemasNames[m_allSchemas->GetSelection()];
wxArrayString tables = ddImportDBUtils::getTablesNames(wparent->getConnection(), wparent->schemaName);
wparent->getDesign()->unMarkSchemaOnAll();
wparent->getDesign()->markSchemaOn(tables);
}
else
{
wparent->OIDSelectedSchema = -1;
wparent->schemaName = _("");
}
wparent->page4->populateGrid();
}
}
void SelGenSchemaPage::refreshSchemas(pgConn *connection)
{
schemasHM.clear();
schemasNames.Clear();
schemasNames.Add(wxT("Not schema selected"));
// Search schemas and insert them
wxString restr = wxT(" WHERE ")
wxT("NOT ")
wxT("((nspname = 'pg_catalog' AND EXISTS (SELECT 1 FROM pg_class WHERE relname = 'pg_class' AND relnamespace = nsp.oid LIMIT 1)) OR\n")
wxT("(nspname = 'pgagent' AND EXISTS (SELECT 1 FROM pg_class WHERE relname = 'pga_job' AND relnamespace = nsp.oid LIMIT 1)) OR\n")
wxT("(nspname = 'information_schema' AND EXISTS (SELECT 1 FROM pg_class WHERE relname = 'tables' AND relnamespace = nsp.oid LIMIT 1)) OR\n")
wxT("(nspname LIKE '_%' AND EXISTS (SELECT 1 FROM pg_proc WHERE proname='slonyversion' AND pronamespace = nsp.oid LIMIT 1)) OR\n")
wxT("(nspname = 'dbo' AND EXISTS (SELECT 1 FROM pg_class WHERE relname = 'systables' AND relnamespace = nsp.oid LIMIT 1)) OR\n")
wxT("(nspname = 'sys' AND EXISTS (SELECT 1 FROM pg_class WHERE relname = 'all_tables' AND relnamespace = nsp.oid LIMIT 1)))\n");
if (connection->EdbMinimumVersion(8, 2))
{
restr += wxT(" AND nsp.nspparent = 0\n");
// Do not show dbms_job_procedure in schemas
if (!settings->GetShowSystemObjects())
restr += wxT("AND NOT (nspname = 'dbms_job_procedure' AND EXISTS(SELECT 1 FROM pg_proc WHERE pronamespace = nsp.oid and proname = 'run_job' LIMIT 1))\n");
}
wxString sql;
if (connection->BackendMinimumVersion(8, 1))
{
sql = wxT("SELECT CASE WHEN nspname LIKE E'pg\\\\_temp\\\\_%' THEN 1\n")
wxT(" WHEN (nspname LIKE E'pg\\\\_%') THEN 0\n");
}
else
{
sql = wxT("SELECT CASE WHEN nspname LIKE 'pg\\\\_temp\\\\_%' THEN 1\n")
wxT(" WHEN (nspname LIKE 'pg\\\\_%') THEN 0\n");
}
sql += wxT(" ELSE 3 END AS nsptyp, nspname, nsp.oid\n")
wxT(" FROM pg_namespace nsp\n")
+ restr +
wxT(" ORDER BY 1, nspname");
pgSet *schemas = connection->ExecuteSet(sql);
wxTreeItemId parent;
if (schemas)
{
while (!schemas->Eof())
{
wxString name = schemas->GetVal(wxT("nspname"));
long nsptyp = schemas->GetLong(wxT("nsptyp"));
wxStringTokenizer tokens(settings->GetSystemSchemas(), wxT(","));
while (tokens.HasMoreTokens())
{
wxRegEx regex(tokens.GetNextToken());
if (regex.Matches(name))
{
nsptyp = SCHEMATYP_USERSYS;
break;
}
}
if (nsptyp <= SCHEMATYP_USERSYS && !settings->GetShowSystemObjects())
{
schemas->MoveNext();
continue;
}
schemasNames.Add(name);
schemasHM[name] = schemas->GetOid(wxT("oid"));
schemas->MoveNext();
}
delete schemas;
}
}
// ----- SelGenSchemaPage Implementation
ReportGridPage::ReportGridPage(wxWizard *parent, wxWizardPage *prev)
: wxWizardPage(parent)
{
wparent = (ddGenerationWizard *) parent;
m_prev = prev;
m_next = NULL;
// A top-level sizer
wxBoxSizer *topSizer = new wxBoxSizer(wxVERTICAL );
this->SetSizer(topSizer);
// Add a message
message = new wxStaticText(this, wxID_STATIC, _("Please check the choice for each table:"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
topSizer->Add(message);
topSizer->AddSpacer(10);
reportGrid = new wxDDGrid(this, DDTABSGRID);
reportGrid->SetRowLabelSize(25);
reportGrid->CreateGrid(wparent->page2->countSelTables(), 2);
populateGrid();
topSizer->Add(reportGrid, 1, wxEXPAND);
// Add Event
this->Connect(DDTABSGRID, wxEVT_GRID_CELL_LEFT_CLICK, (wxObjectEventFunction) (wxEventFunction) (wxGridEventFunction) &ReportGridPage::OnCellLeftClick);
}
ReportGridPage::~ReportGridPage()
{
}
void ReportGridPage::populateGrid()
{
reportGrid->ClearGrid();
if(reportGrid->GetNumberRows() > 0)
reportGrid->DeleteRows(0, reportGrid->GetNumberRows());
int row , max = wparent->page2->countSelTables();
reportGrid->InsertRows(0, max);
reportGrid->SetColLabelValue(0, _("Tables"));
reportGrid->SetColLabelValue(1, _("Select an action"));
wxString strChoicesSimple[1] = {_("Create table")};
wxString strChoicesAlter[4] = {_("Alter table"), _("Drop, then create"), _("Create table [conflict]"), wxT("No action")};
//Two Steps process first for tables that belongs to a schema (need alter table)
//Later for normal tables (just create table)
int useRow = 0;
for(row = 0; row < max; row++)
{
ddTableFigure *table = wparent->getDesign()->getTable(wparent->page2->getSelTableName(row));
if(table != NULL && table->getBelongsToSchema())
{
wxString labelString = wxString::Format(wxT("%i"), (row + 1));
reportGrid->SetRowLabelValue(row, labelString);
reportGrid->SetReadOnly( row, 0, true);
reportGrid->SetCellValue( row, 0, wparent->page2->getSelTableName(row));
reportGrid->SetCellRenderer(row, 1, new wxGridCellComboBoxRenderer);
reportGrid->SetCellEditor(row, 1, new dxGridCellSizedChoiceEditor(WXSIZEOF(strChoicesAlter), strChoicesAlter));
reportGrid->SetCellValue( row, 1, _("Alter table"));
reportGrid->SetCellBackgroundColour(row, 0, LIGHT_YELLOW);
reportGrid->SetCellBackgroundColour(row, 1, LIGHT_YELLOW);
useRow++;
}
else if(table == NULL)
{
wxMessageBox(_("Metadata of table to be generated not found at database designer model"), _("Error at generation process"), wxICON_ERROR | wxOK);
return;
}
}
// Normal tables (just create table)
for(row = 0; row < max; row++)
{
ddTableFigure *table = wparent->getDesign()->getTable(wparent->page2->getSelTableName(row));
if(table != NULL && !table->getBelongsToSchema())
{
reportGrid->SetCellValue( row, 0, wparent->page2->getSelTableName(row));
reportGrid->SetCellRenderer(row, 1, new wxGridCellComboBoxRenderer);
reportGrid->SetCellEditor(row, 1, new dxGridCellSizedChoiceEditor(WXSIZEOF(strChoicesSimple), strChoicesSimple));
reportGrid->SetCellValue( row, 1, _("Create table"));
useRow++;
}
else if(table == NULL)
{
wxMessageBox(_("Metadata of table to be generated not found at database designer model"), _("Error importing at generation process"), wxICON_ERROR | wxOK);
return;
}
}
reportGrid->AutoSizeColumns();
reportGrid->Fit();
}
void ReportGridPage::OnWizardPageChanging(wxWizardEvent &event)
{
if(!event.GetDirection())
{
//Reset tables after a warning
int answer = wxMessageBox(_("Going back to \"Select schema\" page will reinitialize all selections.\nDo you want to continue?"), _("Going back?"), wxYES_NO | wxCANCEL, this);
if (answer != wxYES)
{
event.Veto();
}
}
}
void ReportGridPage::OnCellLeftClick(wxGridEvent &event)
{
// Only show editor y case of column 1
if(event.GetCol() == 1)
{
reportGrid->ComboBoxEvent(event);
}
event.Skip();
}
//
// -- Special version of wxGrid to allow use of fast comboboxes and grid columns auto fit
//
wxDDGrid::wxDDGrid(wxWindow *parent, wxWindowID id):
wxGrid(parent, id, wxDefaultPosition, wxDefaultSize, wxTE_READONLY | wxTE_BESTWRAP, wxT("")),
m_selTemp(NULL), keepFit(1)
{
// Initially all columns have s-factor=1
for( unsigned i = 0; i < 10; ++i ) sf[i] = 1;
Connect( wxEVT_SIZE, wxSizeEventHandler( wxDDGrid::OnSizeEvt) );
// Adjust the default row height to be more compact
wxFont font = GetLabelFont();
int nWidth = 0;
int nHeight = 18;
GetTextExtent(wxT("W"), &nWidth, &nHeight, NULL, NULL, &font);
SetColLabelSize(nHeight + 6);
SetRowLabelSize(35);
#ifdef __WXGTK__
SetDefaultRowSize(nHeight + 8, TRUE);
#else
SetDefaultRowSize(nHeight + 4, TRUE);
#endif
}
void wxDDGrid::ComboBoxEvent(wxGridEvent &event)
{
// This forces the cell to go into edit mode directly
this->m_waitForSlowClick = TRUE;
int row = event.GetRow();
int col = event.GetCol();
this->SetGridCursor(row, col);
// Store the click co-ordinates in the editor if possible
// if an editor has created a ClientData area, we presume it's
// a wxPoint and we store the click co-ordinates
wxGridCellEditor *pEditor = this->GetCellEditor(event.GetRow(), event.GetCol());
wxPoint *pClickPoint = (wxPoint *)pEditor->GetClientData();
if (pClickPoint)
{
*pClickPoint = this->ClientToScreen(event.GetPosition());
#ifndef __WINDOWS__
EnableCellEditControl(true);
#endif
}
// hack to prevent selection from being lost when click combobox
if (this->IsInSelection(event.GetRow(), event.GetCol()))
{
this->m_selTemp = this->m_selection;
this->m_selection = NULL;
}
pEditor->DecRef();
}
void wxDDGrid::RevertSel()
{
if (m_selTemp)
{
wxASSERT(m_selection == NULL);
m_selection = m_selTemp;
m_selTemp = NULL;
}
}
void wxDDGrid::OnSizeEvt( wxSizeEvent &ev )
{
if( !StretchIt() ) ev.Skip();
}
int wxDDGrid::StretchIt()
{
int new_width = GetClientSize().GetWidth() - GetRowLabelSize() - 10;
int fixedWidth = 0, numStretches = 0, numStretched = 0;
for( int i = 0; i < GetNumberCols(); ++i )
{
if( sf[i] == 0 ) fixedWidth += GetColSize(i);
else if( sf[i] < 0 )
{
AutoSizeColumn(i, false);
fixedWidth += GetColSize(i);
}
else
{
numStretches += sf[i];
numStretched += 1;
}
}
// Now either we have space for normal layout or resort to wxGrid default behaviour
if( numStretched && ((fixedWidth + numStretched * 10) < new_width) )
{
int stretchSpace = (new_width - fixedWidth) / numStretches;
//BeginBatch();
int i, max = GetNumberCols();
for(i = 0; i < max; ++i )
if( sf[i] > 0 )
SetColSize(i, stretchSpace * sf[i]);
//EndBatch();
return 1;
}
return 0;
}

View file

@ -0,0 +1,114 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// ddModelBrowser.cpp - Tables Tree of Database Designer.
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
#include <wx/regex.h>
#include <wx/imaglist.h>
// App headers
#include "dd/ddmodel/ddModelBrowser.h"
#include "dd/ddmodel/ddDatabaseDesign.h"
#include "dd/ddmodel/ddBrowserDataContainer.h"
// Images
#include "images/table-sm.pngc"
#include "images/database-sm.pngc"
BEGIN_EVENT_TABLE(ddModelBrowser, wxTreeCtrl)
EVT_TREE_ITEM_ACTIVATED(DD_BROWSER, ddModelBrowser::OnItemActivated)
EVT_TREE_BEGIN_DRAG(DD_BROWSER, ddModelBrowser::OnBeginDrag)
END_EVENT_TABLE()
ddModelBrowser::ddModelBrowser(wxWindow *parent, wxWindowID id, const wxPoint &pos, const wxSize &size, long style, ddDatabaseDesign *design)
: wxTreeCtrl(parent, id, pos, size, style)
{
ownerDesign = design;
rootNode = (wxTreeItemId *)NULL;
createRoot(_("Database Design"));
// Create normal images list of browser
// Remember to update enum gqbImages in ddModelBrowser.h if changing the images!!
imageList = new wxImageList(16, 16);
imageList->Add(*database_sm_png_ico);
imageList->Add(*table_sm_png_ico);
this->AssignImageList(imageList);
}
// Destructor
ddModelBrowser::~ddModelBrowser()
{
this->DeleteAllItems(); // This remove and delete data inside tree's node
}
// Create root node
wxTreeItemId &ddModelBrowser::createRoot(wxString Name)
{
rootNode = this->AddRoot(Name, DD_IMG_FIG_DATABASE, DD_IMG_FIG_DATABASE);
return rootNode;
}
void ddModelBrowser::refreshFromModel()
{
this->DeleteAllItems();
createRoot(_("Database Design"));
hdIteratorBase *iterator = ownerDesign->getEditor()->modelFiguresEnumerator();
hdIFigure *tmpFigure;
ddTableFigure *table;
while(iterator->HasNext())
{
tmpFigure = (hdIFigure *)iterator->Next();
if(tmpFigure->getKindId() == DDTABLEFIGURE)
{
table = (ddTableFigure *)tmpFigure;
this->AppendItem(rootNode, table->getTableName(), DD_IMG_FIG_TABLE, DD_IMG_FIG_TABLE, new ddBrowserDataContainer(table));
}
}
delete iterator;
this->Expand(rootNode);
this->SortChildren(rootNode);
}
void ddModelBrowser::OnItemActivated(wxTreeEvent &event)
{
}
void ddModelBrowser::OnBeginDrag(wxTreeEvent &event)
{
wxTreeItemId itemId = event.GetItem();
// Simplest solution, simulate DnD but actually don't do it
ddBrowserDataContainer *object = (ddBrowserDataContainer *) GetItemData(itemId);
if(object != NULL && (object->getFigureKindId() == DDTABLEFIGURE))
{
ddTableFigure *item = (ddTableFigure *) object->getFigure();
wxString tableName = item->getTableName();
wxTextDataObject textData(tableName);
wxDropSource dragSource(this);
dragSource.SetData(textData);
wxDragResult result = dragSource.DoDragDrop(wxDrag_CopyOnly);
if(result != wxDragCopy)
{
wxMessageBox(wxT("Invalid kind of data during drag and drop operation"), wxT("Drag and drop error"), wxICON_ERROR | wxOK);
}
}
}

22
dd/ddmodel/module.mk Normal file
View file

@ -0,0 +1,22 @@
#######################################################################
#
# pgAdmin III - PostgreSQL Tools
#
# Copyright (C) 2002 - 2016, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
# module.mk - pgadmin/dd/ddmodel/ Makefile fragment
#
#######################################################################
pgadmin3_SOURCES += \
dd/ddmodel/ddDatabaseDesign.cpp \
dd/ddmodel/ddDrawingEditor.cpp \
dd/ddmodel/ddDBReverseEnginering.cpp \
dd/ddmodel/ddModelBrowser.cpp \
dd/ddmodel/ddGenerationWizard.cpp \
dd/ddmodel/ddDrawingView.cpp \
dd/ddmodel/ddBrowserDataContainer.cpp
EXTRA_DIST += \
dd/ddmodel/module.mk

17
dd/module.mk Normal file
View file

@ -0,0 +1,17 @@
#######################################################################
#
# pgAdmin III - PostgreSQL Tools
#
# Copyright (C) 2002 - 2016, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
# module.mk - pgadmin/dd/ Makefile fragment
#
#######################################################################
include dd/dditems/module.mk
include dd/ddmodel/module.mk
EXTRA_DIST += \
dd/module.mk

View file

@ -0,0 +1,79 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// ctlMessageWindow.cpp - debugger
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
// App headers
#include "debugger/ctlMessageWindow.h"
IMPLEMENT_CLASS(ctlMessageWindow, wxTextCtrl)
BEGIN_EVENT_TABLE(ctlMessageWindow, wxTextCtrl)
EVT_TIMER(wxID_ANY, ctlMessageWindow::OnTimer)
END_EVENT_TABLE()
////////////////////////////////////////////////////////////////////////////////
// ctlMessageWindow constructor
//
// Initialize the grid control and clear it out....
//
ctlMessageWindow::ctlMessageWindow(wxWindow *parent, wxWindowID id)
: wxTextCtrl(parent, wxID_ANY, wxT(""), wxPoint(0, 0), wxSize(0, 0),
wxTE_MULTILINE | wxTE_READONLY)
{
SetFont(settings->GetSQLFont());
m_timer.SetOwner(this);
}
////////////////////////////////////////////////////////////////////////////////
// AddMessage()
//
// Adds the message in the 'DBMS Messages' window.
//
void ctlMessageWindow::AddMessage(wxString message)
{
m_mutex.Lock();
m_currMsg += message + wxT("\n");
m_mutex.Unlock();
if (!m_timer.IsRunning())
m_timer.Start(100, true);
}
void ctlMessageWindow::OnTimer(wxTimerEvent &ev)
{
m_mutex.Lock();
AppendText(m_currMsg);
m_currMsg = wxEmptyString;
m_mutex.Unlock();
}
////////////////////////////////////////////////////////////////////////////////
// delMessage()
//
// Removes the given message from the 'DBMS Messages' window.
//
void ctlMessageWindow::DelMessage(const char *name)
{
SetValue(wxT(""));
}
wxString ctlMessageWindow::GetMessage(int row)
{
return(GetValue());
}

100
debugger/ctlResultGrid.cpp Normal file
View file

@ -0,0 +1,100 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// ctlResultGrid.cpp - debugger
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
// App headers
#include "debugger/ctlResultGrid.h"
IMPLEMENT_CLASS( ctlResultGrid, wxGrid )
////////////////////////////////////////////////////////////////////////////////
// ctlResultGrid constructor
//
// We use a ctlResultGrid to display the result set from a query. This class
// is a minor extension of the wxGrid class.
ctlResultGrid::ctlResultGrid( wxWindow *parent, wxWindowID id )
: wxGrid( parent, id )
{
SetFont(settings->GetSystemFont());
CreateGrid( 0, 0 );
}
////////////////////////////////////////////////////////////////////////////////
// fillGrid()
//
// Given a result set handle, this function copies the values in that result
// set into the grid.
void ctlResultGrid::FillResult(pgSet *set)
{
// Clear out the old results (if any) and resize
// grid to match the result set
if( GetNumberRows())
DeleteRows( 0, GetNumberRows());
if( GetNumberCols())
DeleteCols( 0, GetNumberCols());
if (!set)
return;
int rowCount = set->NumRows();
int colCount = set->NumCols();
// If this PGresult represents a non-query command
// (like an INSERT), there won't be any columns in
// the result set - just return
if( colCount == 0 )
return;
// Disable repaints to we don't flicker too much
BeginBatch();
AppendRows(rowCount);
AppendCols(colCount);
EnableEditing(false);
// Copy the column names from the result set into the column headers
int row = 0,
col;
for(col = 0; col < colCount; ++col)
SetColLabelValue(col, set->ColName(col));
wxGridCellAttr* pAttr = new wxGridCellAttr;
pAttr->SetBackgroundColour(wxColour(128,128,128));
//SetDefaultCellBackgroundColour(wxColour(128,128,128));
// Now copy each value from the result set into the grid
while(!set->Eof())
{
for(col = 0; col < colCount; ++col)
{
if(set->IsNull(col))
SetCellValue(row, col, wxT(""));
else
SetCellValue(row, col, set->GetVal(col));
}
row++;
set->MoveNext();
}
// Resize each column to fit its content
AutoSizeColumns(false);
// Enable repaints
EndBatch();
}

View file

@ -0,0 +1,88 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// ctlStackWindow.cpp - debugger
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
#include <wx/listimpl.cpp>
// App headers
#include "debugger/ctlStackWindow.h"
WX_DEFINE_LIST(dbgStackFrameList);
IMPLEMENT_CLASS(ctlStackWindow, wxListBox)
////////////////////////////////////////////////////////////////////////////////
// ctlStackWindow constructor
//
// Initialize the grid control and clear it out....
//
ctlStackWindow::ctlStackWindow(wxWindow *parent, wxWindowID id, const wxPoint &pos, const wxSize &size, long style, const wxString &name )
: wxListBox(parent , id, pos, size, 0, NULL, style | wxLB_HSCROLL | wxLB_NEEDED_SB )
{
SetFont(settings->GetSystemFont());
}
////////////////////////////////////////////////////////////////////////////////
// ClearStack()
//
// Remove all stack frames from the display
//
void ctlStackWindow::ClearStack()
{
Set(0, NULL);
}
////////////////////////////////////////////////////////////////////////////////
// SetStack()
//
// Add an array of stack frames to the display
//
void ctlStackWindow::SetStack(const dbgStackFrameList &stacks, int selected)
{
Set(0, NULL);
for (dbgStackFrameList::Node *node = stacks.GetFirst(); node;
node = node->GetNext())
{
dbgStackFrame *frame = node->GetData();
Append(frame->GetDescription(), (wxClientData *)frame);
}
if (selected != -1)
{
SetSelection(selected);
}
}
////////////////////////////////////////////////////////////////////////////////
// SelectFrame()
//
// Select the frame based on the input function and package
//
void ctlStackWindow::SelectFrame(const wxString &pkg, const wxString &func)
{
int cnt = GetCount();
for (int idx = 0; idx < cnt; idx++)
{
dbgStackFrame *frame = (dbgStackFrame *)GetClientObject(idx);
if (frame && frame->GetFunction() == func && frame->GetPackage() == pkg)
{
SetSelection(idx);
return;
}
}
}

175
debugger/ctlTabWindow.cpp Normal file
View file

@ -0,0 +1,175 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// ctlTabWindow.cpp - debugger
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
#include <wx/thread.h>
// App headers
#include "ctl/ctlAuiNotebook.h"
#include "debugger/ctlTabWindow.h"
#include "debugger/dbgConst.h"
IMPLEMENT_CLASS(ctlTabWindow, wxWindow)
////////////////////////////////////////////////////////////////////////////////
// ctlTabWindow constructor
//
// This constructor creates a new notebook (a tab control) and clears out the
// rest of the data members.
//
ctlTabWindow::ctlTabWindow(wxWindow *parent, wxWindowID id, const wxPoint &pos,
const wxSize &size, long style, const wxString &name)
: ctlAuiNotebook(parent, id, pos, size, style),
m_resultWindow(NULL),
m_varWindow(NULL),
m_pkgVarWindow(NULL),
m_stackWindow(NULL),
m_paramWindow(NULL),
m_messageWindow(NULL)
{
SetFont(settings->GetSystemFont());
m_tabMap = new wsTabHash();
}
void ctlTabWindow::SelectTab(wxWindowID id)
{
wsTabHash::iterator result = m_tabMap->find(id);
if (result != m_tabMap->end())
{
SetSelection(result->second);
}
}
////////////////////////////////////////////////////////////////////////////////
// GetResultWindow()
//
// This function returns a pointer to our child result window (m_resultWindow)
// and creates that window when we first need it.
//
ctlResultGrid *ctlTabWindow::GetResultWindow()
{
if (m_resultWindow == 0)
{
// We don't have a result window yet - go ahead and create one
m_resultWindow = new ctlResultGrid(this, -1);
AddPage(m_resultWindow, _("Results"), true);
}
return m_resultWindow;
}
////////////////////////////////////////////////////////////////////////////////
// GetVarWindow()
//
// This function returns a pointer to our child 'local-variables' window
// (m_varWindow) and creates that window when we first need it.
//
ctlVarWindow *ctlTabWindow::GetVarWindow(bool _create)
{
if ((m_varWindow == NULL) && _create)
{
// We don't have a variable window yet - go ahead and create one
(*m_tabMap)[ID_VARGRID] = GetPageCount();
m_varWindow = new ctlVarWindow(this, ID_VARGRID);
AddPage(m_varWindow, _("Local Variables"), true);
}
return m_varWindow;
}
////////////////////////////////////////////////////////////////////////////////
// GetPkgVarWindow()
//
// This function returns a pointer to our child 'package-variables' window
// (m_varWindow) and creates that window when we first need it.
//
ctlVarWindow *ctlTabWindow::GetPkgVarWindow(bool create)
{
if ((m_pkgVarWindow == NULL) && create)
{
// We don't have a variable window yet - go ahead and create one
(*m_tabMap)[ID_PKGVARGRID] = GetPageCount();
m_pkgVarWindow = new ctlVarWindow(this, ID_PKGVARGRID);
AddPage(m_pkgVarWindow, _("Package Variables"), true);
}
return m_pkgVarWindow;
}
////////////////////////////////////////////////////////////////////////////////
// GetParamWindow()
//
// This function returns a pointer to our child 'parameters' window
// (m_paramWindow) and creates that window when we first need it.
//
ctlVarWindow *ctlTabWindow::GetParamWindow(bool create)
{
if ((m_paramWindow == NULL) && create)
{
// We don't have a variable window yet - go ahead and create one
(*m_tabMap)[ID_PARAMGRID] = GetPageCount();
m_paramWindow = new ctlVarWindow(this, ID_PARAMGRID);
AddPage(m_paramWindow, _("Parameters"), true);
}
return m_paramWindow;
}
////////////////////////////////////////////////////////////////////////////////
// GetMessageWindow()
//
// This function returns a pointer to our child 'messages' window
// (m_messageWindow) and creates that window when we first need it.
//
ctlMessageWindow *ctlTabWindow::GetMessageWindow()
{
if (m_messageWindow == 0)
{
// We don't have a variable window yet - go ahead and create one
(*m_tabMap)[ID_MSG_PAGE] = GetPageCount();
m_messageWindow = new ctlMessageWindow(this, ID_MSG_PAGE);
AddPage(m_messageWindow, _("DBMS Messages"), true);
}
return m_messageWindow;
}
////////////////////////////////////////////////////////////////////////////////
// GetStackWindow()
//
// This function returns a pointer to our child stack-trace window
// (m_stackWindow) and creates that window when we first need it.
//
ctlStackWindow *ctlTabWindow::GetStackWindow()
{
if (m_stackWindow == 0)
{
// We don't have a stack-trace window yet - go ahead and create one
m_stackWindow = new ctlStackWindow(this, -1);
AddPage(m_stackWindow, _("Stack"), true);
}
return m_stackWindow;
}

182
debugger/ctlVarWindow.cpp Normal file
View file

@ -0,0 +1,182 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// ctlVarWindow.cpp - debugger
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
#include <wx/log.h>
// App headers
#include "debugger/ctlVarWindow.h"
IMPLEMENT_CLASS(ctlVarWindow, wxGrid)
////////////////////////////////////////////////////////////////////////////////
// ctlVarWindow constructor
//
// Initialize the grid control and clear it out....
//
ctlVarWindow::ctlVarWindow(wxWindow *parent, wxWindowID id)
: wxGrid(parent, id),
m_cells(NULL),
m_nameFont(GetDefaultCellFont())
{
SetFont(settings->GetSystemFont());
// Create the grid control
CreateGrid(0, 0);
SetRowLabelSize(0); // Turn off the row labels
// Set up three columns: name, value, and data type
AppendCols(3);
SetColLabelValue(COL_NAME, _("Name"));
SetColLabelValue(COL_TYPE, _("Type"));
SetColLabelValue(COL_VALUE, _("Value"));
EnableDragGridSize(true);
// EDB wants to hide certain PL variables. To do that, we
// keep a hash of hidden names and a hash of hidden types...
m_hiddenNames.insert(wxT("found"));
m_hiddenNames.insert(wxT("rowcount"));
m_hiddenNames.insert(wxT("sqlcode"));
m_hiddenNames.insert(wxT("sqlerrm"));
m_hiddenNames.insert(wxT("_found"));
m_hiddenNames.insert(wxT("_rowcount"));
m_hiddenNames.insert(wxT("sqlstate"));
m_hiddenTypes.insert(wxT("refcursor"));
}
////////////////////////////////////////////////////////////////////////////////
// AddVar()
//
// Adds (or updates) the given variable in the 'local variables' window. If
// we find a variabled named 'name' in the window, we simply update the value,
// otherwise, we create a new entry in the grid
//
void ctlVarWindow::AddVar(wxString name, wxString value, wxString type, bool readOnly)
{
// If this is a 'hidden' variable, just ignore it
if (m_hiddenNames.find(name) != m_hiddenNames.end())
return;
if (m_hiddenTypes.find(type) != m_hiddenTypes.end())
return;
if (m_cells == NULL)
{
// This is the first variable we're adding to this grid,
// layout the grid and set the column headers.
m_cells = new wsCellHash;
}
// Try to find an existing grid cell for this variable...
wxString key(name);
wsCellHash::iterator cell = m_cells->find(key);
if (cell == m_cells->end())
{
// Can't find this variable in the grid, go ahead and add it
gridCell newCell;
newCell.m_row = m_cells->size();
newCell.m_type = type;
newCell.m_value = value;
AppendRows(1);
SetRowLabelValue(newCell.m_row, key);
SetCellValue(newCell.m_row, COL_NAME, key);
SetCellValue(newCell.m_row, COL_TYPE, type);
SetCellValue(newCell.m_row, COL_VALUE, value);
SetCellFont(newCell.m_row, COL_NAME, m_nameFont);
SetReadOnly(newCell.m_row, COL_NAME, true);
SetReadOnly(newCell.m_row, COL_TYPE, true);
SetReadOnly(newCell.m_row, COL_VALUE, readOnly);
(*m_cells)[key] = newCell;
}
else
{
// This variable is already in the grid, update the value
// and hilite it so the user knows that it has changed.
cell->second.m_value = value;
if (GetCellValue(cell->second.m_row, COL_VALUE).IsSameAs(value))
SetCellTextColour(cell->second.m_row, COL_VALUE, *wxBLACK);
else
SetCellTextColour(cell->second.m_row, COL_VALUE, *wxRED);
SetCellValue(cell->second.m_row, COL_VALUE, value);
// FIXME: why is this part conditional?
// FIXME: why do we need this code? can the type ever change?
if (GetCellValue(cell->second.m_row, COL_TYPE) == wxT(""))
{
SetCellValue(cell->second.m_row, COL_TYPE, type);
}
}
// AutoSizeColumns(false);
}
////////////////////////////////////////////////////////////////////////////////
// delVar()
//
// Removes the given variable from the 'local variables' window.
//
void ctlVarWindow::DelVar(wxString name)
{
if (name.IsEmpty())
{
delete m_cells;
m_cells = NULL;
if (GetNumberRows())
DeleteRows(0, GetNumberRows());
}
else
{
for (int row = 0; row < GetNumberRows(); row++)
{
if (GetCellValue(row, COL_NAME) == name)
{
DeleteRows(row, 1);
break;
}
}
}
}
wxString ctlVarWindow::GetVarName(int row)
{
return(GetCellValue(row, COL_NAME));
}
wxString ctlVarWindow::GetVarValue(int row)
{
return(GetCellValue(row, COL_VALUE));
}

View file

@ -0,0 +1,23 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// dbgBreakPoint.cpp - debugger
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
#include <wx/listimpl.cpp>
// App headers
#include "debugger/dbgBreakPoint.h"
WX_DEFINE_LIST( dbgBreakPointList );

913
debugger/dbgController.cpp Normal file
View file

@ -0,0 +1,913 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// dbgController.cpp - debugger controller
// - Flow and data controller for the debugger
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
#include "ctl/ctlAuiNotebook.h"
#include "db/pgConn.h"
#include "db/pgQueryThread.h"
#include "db/pgQueryResultEvent.h"
#include "schema/pgObject.h"
#include "schema/pgTrigger.h"
#include "debugger/dbgConst.h"
#include "debugger/dbgBreakPoint.h"
#include "debugger/dbgController.h"
#include "debugger/dbgModel.h"
#include "debugger/ctlStackWindow.h"
#include "debugger/ctlMessageWindow.h"
#include "debugger/ctlTabWindow.h"
#include "debugger/frmDebugger.h"
#include "debugger/dlgDirectDbg.h"
#include "utils/pgDefs.h"
#include <stdexcept>
#define LOG_DBG_MUTEX_LOCKING 0
#if LOG_DBG_MUTEX_LOCKING
#define LOCKMUTEX(m) \
wxPrintf(wxT("Mutex locking @ %s:%d\n"), wxString::FromAscii(__WXFUNCTION__).c_str(), __LINE__); \
m.Lock(); \
wxPrintf(wxT("Mutex locked @ %s:%d\n"), wxString::FromAscii(__WXFUNCTION__).c_str(), __LINE__);
#define UNLOCKMUTEX(m) \
wxPrintf(wxT("Mutex unlocking @ %s:%d\n"), wxString::FromAscii(__WXFUNCTION__).c_str(), __LINE__); \
m.Unlock(); \
wxPrintf(wxT("Mutex unlocked @ %s:%d\n"), wxString::FromAscii(__WXFUNCTION__).c_str(), __LINE__);
#else
#define LOCKMUTEX(m) \
m.Lock();
#define UNLOCKMUTEX(m) \
m.Unlock();
#endif
const wxString dbgController::ms_cmdDebugSPLV1(
wxT("SELECT edb_oid_debug(%lu, %lu)"));
const wxString dbgController::ms_cmdDebugSPLV2(
wxT("SELECT edb_oid_debug(%lu)"));
const wxString dbgController::ms_cmdDebugPLPGSQLV1(
wxT("SELECT plpgsql_oid_debug(%lu, %lu)"));
const wxString dbgController::ms_cmdDebugPLPGSQLV2(
wxT("SELECT plpgsql_oid_debug(%lu)"));
const wxString dbgController::ms_cmdAttachToPort(
wxT("SELECT * FROM pldbg_attach_to_port(%s)"));
const wxString dbgController::ms_cmdWaitForBreakpointV1(
wxT("SELECT\n")
wxT(" p.pkg AS pkg, p.func AS func, p.targetName AS targetName,\n")
wxT(" p.linenumber AS linenumber, pldbg_get_source($1::INTEGER, p.pkg, p.func) AS src,\n")
wxT(" (SELECT\n")
wxT(" s.args\n")
wxT(" FROM pldbg_get_stack($1::INTEGER) s\n")
wxT(" WHERE s.func = p.func AND s.pkg = p.pkg) AS args\n")
wxT("FROM pldbg_wait_for_breakpoint($1::INTEGER) p"));
const wxString dbgController::ms_cmdWaitForBreakpointV2(
wxT("SELECT\n")
wxT(" p.func AS func, p.targetName AS targetName, \n")
wxT(" p.linenumber AS linenumber,\n")
wxT(" pldbg_get_source($1::INTEGER, p.func) AS src,\n")
wxT(" (SELECT\n")
wxT(" s.args\n")
wxT(" FROM pldbg_get_stack($1::INTEGER) s\n")
wxT(" WHERE s.func = p.func) AS args\n")
wxT("FROM pldbg_wait_for_breakpoint($1::INTEGER) p"));
const wxString dbgController::ms_cmdGetVars(
wxT("SELECT\n")
wxT(" name, varClass, value,\n")
wxT(" pg_catalog.format_type(dtype, NULL) as dtype, isconst\n")
wxT("FROM pldbg_get_variables(%s) ORDER BY varClass"));
const wxString dbgController::ms_cmdGetStack(
wxT("SELECT * FROM pldbg_get_stack(%s) ORDER BY level"));
const wxString dbgController::ms_cmdGetBreakpoints(
wxT("SELECT * FROM pldbg_get_breakpoints(%s)"));
const wxString dbgController::ms_cmdStepOverV1(
wxT("SELECT\n")
wxT(" p.pkg AS pkg, p.func AS func, p.targetName AS targetName,\n")
wxT(" p.linenumber AS linenumber, pldbg_get_source($1::INTEGER, p.pkg, p.func) AS src,\n")
wxT(" (SELECT\n")
wxT(" s.args\n")
wxT(" FROM pldbg_get_stack($1::INTEGER) s\n")
wxT(" WHERE s.func = p.func AND s.pkg = p.pkg) AS args\n")
wxT("FROM pldbg_step_over($1::INTEGER) p"));
const wxString dbgController::ms_cmdStepOverV2(
wxT("SELECT\n")
wxT(" p.func, p.targetName, p.linenumber,\n")
wxT(" pldbg_get_source($1::INTEGER, p.func) AS src,\n")
wxT(" (SELECT\n")
wxT(" s.args\n")
wxT(" FROM pldbg_get_stack($1::INTEGER) s\n")
wxT(" WHERE s.func = p.func) AS args\n")
wxT("FROM pldbg_step_over($1::INTEGER) p"));
const wxString dbgController::ms_cmdStepIntoV1(
wxT("SELECT\n")
wxT(" p.pkg AS pkg, p.func AS func, p.targetName AS targetName,\n")
wxT(" p.linenumber AS linenumber, pldbg_get_source($1::INTEGER, p.pkg, p.func) AS src,\n")
wxT(" (SELECT\n")
wxT(" s.args\n")
wxT(" FROM pldbg_get_stack($1::INTEGER) s\n")
wxT(" WHERE s.func = p.func AND s.pkg = p.pkg) AS args\n")
wxT("FROM pldbg_step_into($1::INTEGER) p"));
const wxString dbgController::ms_cmdStepIntoV2(
wxT("SELECT\n")
wxT(" p.func, p.targetName, p.linenumber,\n")
wxT(" pldbg_get_source($1::INTEGER, p.func) AS src,\n")
wxT(" (SELECT\n")
wxT(" s.args\n")
wxT(" FROM pldbg_get_stack($1::INTEGER) s\n")
wxT(" WHERE s.func = p.func) AS args\n")
wxT("FROM pldbg_step_into($1::INTEGER) p"));
const wxString dbgController::ms_cmdContinueV1(
wxT("SELECT\n")
wxT(" p.pkg AS pkg, p.func AS func, p.targetName AS targetName,\n")
wxT(" p.linenumber AS linenumber, pldbg_get_source($1::INTEGER, p.pkg, p.func) AS src,\n")
wxT(" (SELECT\n")
wxT(" s.args\n")
wxT(" FROM pldbg_get_stack($1::INTEGER) s\n")
wxT(" WHERE s.func = p.func AND s.pkg = p.pkg) AS args\n")
wxT("FROM pldbg_continue($1::INTEGER) p"));
const wxString dbgController::ms_cmdContinueV2(
wxT("SELECT\n")
wxT(" p.func, p.targetName, p.linenumber,\n")
wxT(" pldbg_get_source($1::INTEGER, p.func) AS src,\n")
wxT(" (SELECT\n")
wxT(" s.args\n")
wxT(" FROM pldbg_get_stack($1::INTEGER) s\n")
wxT(" WHERE s.func = p.func) AS args\n")
wxT("FROM pldbg_continue($1::INTEGER) p"));
const wxString dbgController::ms_cmdSetBreakpointV1(
wxT("SELECT * FROM pldbg_set_breakpoint(%s,%s,%s,%d)"));
const wxString dbgController::ms_cmdSetBreakpointV2(
wxT("SELECT * FROM pldbg_set_breakpoint(%s,%s,%d)"));
const wxString dbgController::ms_cmdClearBreakpointV1(
wxT("SELECT * FROM pldbg_drop_breakpoint(%s,%s,%s,%d)"));
const wxString dbgController::ms_cmdClearBreakpointV2(
wxT("SELECT * FROM pldbg_drop_breakpoint(%s,%s,%d)"));
const wxString dbgController::ms_cmdSelectFrameV1(
wxT("SELECT\n")
wxT(" p.pkg AS pkg, p.func AS func, p.targetName AS targetName,\n")
wxT(" p.linenumber AS linenumber,\n")
wxT(" CASE WHEN p.func <> 0 THEN pldbg_get_source($1::INTEGER, p.func, p.pkg) ELSE '<No source available>' END AS src,\n")
wxT(" (SELECT\n")
wxT(" s.args\n")
wxT(" FROM pldbg_get_stack($1::INTEGER) s\n")
wxT(" WHERE s.func = p.func AND s.pkg = p.pkg) AS args\n")
wxT("FROM pldbg_select_frame($1::INTEGER, $2::INTEGER) p"));
const wxString dbgController::ms_cmdSelectFrameV2(
wxT("SELECT\n")
wxT(" p.func AS func, p.targetName AS targetName, p.linenumber AS linenumber,\n")
wxT(" CASE WHEN p.func <> 0 THEN pldbg_get_source($1::INTEGER, p.func) ELSE '<No source available>' END AS src,\n")
wxT(" (SELECT\n")
wxT(" s.args\n")
wxT(" FROM pldbg_get_stack($1::INTEGER) s\n")
wxT(" WHERE s.func = p.func) AS args\n")
wxT("FROM pldbg_select_frame($1::INTEGER, $2::INTEGER) p"));
const wxString dbgController::ms_cmdDepositValue(
wxT("SELECT * FROM pldbg_deposit_value(%s,%s,%d,%s)"));
const wxString dbgController::ms_cmdAbortTarget(
wxT("SELECT * FROM pldbg_abort_target(%s)"));
const wxString dbgController::ms_cmdAddBreakpointEDB(
wxT("SELECT * FROM pldbg_set_global_breakpoint(%s, %s, %s, -1, %s)"));
const wxString dbgController::ms_cmdAddBreakpointPG(
wxT("SELECT * FROM pldbg_set_global_breakpoint(%s, %s, -1, %s)"));
const wxString dbgController::ms_cmdCreateListener(
wxT("SELECT * from pldbg_create_listener()"));
const wxString dbgController::ms_cmdWaitForTarget(
wxT("SELECT * FROM pldbg_wait_for_target(%s)"));
const wxString dbgController::ms_cmdIsBackendRunning(
wxT("SELECT COUNT(*) FROM (SELECT pg_catalog.pg_stat_get_backend_idset() AS bid) AS s WHERE pg_catalog.pg_stat_get_backend_pid(s.bid) = '%d'::integer;"));
dbgController::dbgController(frmMain *main, pgObject *_obj, bool _directDebugging)
: m_ver(DEBUGGER_UNKNOWN_API), m_sessionType(DBG_SESSION_TYPE_UNKNOWN),
m_lineOffset(1), m_terminated(false), m_frm(NULL), m_dbgConn(NULL),
m_dbgThread(NULL), m_execConnThread(NULL), m_model(NULL),
m_isStopping(false)
{
// Create the connection for listening the debugger port and doing the
// debugging operations.
// i.e.
// - step in, step over, continue, fetch stack-frames, fetch variables, etc.
m_dbgConn = _obj->GetConnection()->Duplicate(
wxT("pgAdmin Debugger - Global Listener"));
if (!m_dbgConn->IsAlive())
{
wxLogError(_("Couldn't create a connection for the debugger"));
delete m_dbgConn;
m_dbgConn = NULL;
throw (std::runtime_error("Couldn't create a connection for the debugger"));
}
m_dbgConn->ExecuteVoid(wxT("SET client_min_messages TO error"));
if (!m_dbgConn->BackendMinimumVersion(9, 1))
m_lineOffset = 0;
OID target;
if (_obj->GetMetaType() == PGM_TRIGGER)
{
target = ((pgTrigger *)_obj)->GetFunctionOid();
}
else
{
target = _obj->GetOid();
}
// Fetch the target information
try
{
m_model = new dbgModel((Oid)target, m_dbgConn);
}
catch (const std::runtime_error &error)
{
// Catching the runtime error thrown by dbgTargetInfo, so that we can
// delete the created connection object and then rethrow it
m_dbgConn->Close();
delete m_dbgConn;
m_dbgConn = NULL;
// Rethrow the error
throw error;
}
if (m_model->GetTarget()->HasVariadic() &&
m_model->GetTarget()->GetLanguage() == wxT("edbspl"))
{
wxLogError(
_("An 'edbspl' target with a variadic argument is not supported by this version of pgAdmin debugger!"));
throw (std::runtime_error(
"Unsupported target specified!"));
}
// Fetch the debugger version
wxString strCntProxyInfo = m_dbgConn->ExecuteScalar(
wxT("SELECT COUNT(*) FROM pg_catalog.pg_proc p\n")
wxT(" LEFT JOIN pg_catalog.pg_namespace n ON p.pronamespace = n.oid\n")
wxT("WHERE n.nspname = ANY(current_schemas(false))\n")
wxT(" AND p.proname = 'pldbg_get_proxy_info'"), false);
if(!strCntProxyInfo.IsEmpty() && m_dbgConn->GetLastResultStatus() == PGRES_TUPLES_OK)
{
if (strCntProxyInfo == wxT("0"))
{
m_ver = DEBUGGER_V1_API;
}
wxString strVer = m_dbgConn->ExecuteScalar(
wxT("SELECT proxyapiver FROM pldbg_get_proxy_info()"), false);
if (!strVer.IsEmpty() && m_dbgConn->GetLastResultStatus() == PGRES_TUPLES_OK)
{
long ver;
strVer.ToLong(&ver);
switch (ver)
{
case DEBUGGER_V3_API:
case DEBUGGER_V2_API:
m_ver = (DebuggerApiVersion)ver;
break;
default:
wxLogError(
wxString::Format(
_("pgAdmin does not support this version of the debugger plugin (v:%ld)"),
ver));
m_dbgConn->Close();
delete m_dbgConn;
m_dbgConn = NULL;
delete m_model;
m_model = NULL;
throw (std::runtime_error(
"Unsupported version of debugger plugin installed"));
break;
}
}
else
{
wxLogError(
wxString::Format(
_("Couldn't determine the debugger plugin version.\n%s"),
m_dbgConn->GetLastError().c_str()));
m_dbgConn->Close();
delete m_dbgConn;
m_dbgConn = NULL;
delete m_model;
m_model = NULL;
throw (std::runtime_error(
"Couldn't determine the debugger version (function execution error)."));
}
}
else
{
wxLogError(
wxString::Format(
_("Couldn't determine the debugger plugin version.\n%s"),
m_dbgConn->GetLastError().c_str()));
m_dbgConn->Close();
delete m_dbgConn;
m_dbgConn = NULL;
delete m_model;
m_model = NULL;
throw (std::runtime_error(
"Couldn't determine the debugger version (function check)"));
}
// Store debugging type
if (_directDebugging)
{
m_sessionType = DBG_SESSION_TYPE_DIRECT;
m_frm = new frmDebugger(
main, this, wxString::Format(
_("Debugger - %s"),
m_model->GetTarget()->GetQualifiedName().c_str()));
}
else
{
m_sessionType = DBG_SESSION_TYPE_INCONTEXT;
m_frm = new frmDebugger(
main, this, _("Global Debugger"));
}
if (!Start())
m_frm->EnableToolsAndMenus(false);
}
dbgController::~dbgController()
{
if (m_dbgConn)
{
m_dbgConn->Close();
delete m_dbgConn;
m_dbgConn = NULL;
}
if (m_model)
{
delete m_model;
m_model = NULL;
}
}
bool dbgController::Start()
{
m_frm->EnableToolsAndMenus(false);
m_frm->Show(true);
if(m_frm->GetTabWindow()->GetPageCount()) // Clearing message window, result grid, and re-setting the status message during the module re-exeuction.
{
m_frm->GetMessageWindow()->Clear();
m_frm->GetResultWindow()->ClearGrid();
m_frm->LaunchWaitingDialog(_("Initializing..."));
}
if (!m_dbgConn->IsAlive())
{
if (!m_dbgConn->Reconnect())
{
// Unable to re-establish the connection for debugger
return false;
}
}
LOCKMUTEX(m_dbgThreadLock);
m_dbgThread = new pgQueryThread(
m_dbgConn, this, &(dbgController::NoticeHandler), this);
m_dbgThread->SetEventOnCancellation(false);
if (m_dbgThread->Create() != wxTHREAD_NO_ERROR)
{
delete m_dbgThread;
m_dbgThread = NULL;
UNLOCKMUTEX(m_dbgThreadLock);
return false;
}
m_dbgThread->Run();
UNLOCKMUTEX(m_dbgThreadLock);
wxTheApp->Yield(true);
m_terminated = false;
m_isStopping = false;
if (m_sessionType == DBG_SESSION_TYPE_DIRECT)
{
pgConn *conn = m_dbgConn->Duplicate(
wxT("pgAdmin Debugger - Target Invoker"));
if (m_model->GetTarget()->RequireUserInput())
{
dlgDirectDbg dlg(m_frm, this, conn);
if (dlg.ShowModal() != wxID_OK)
{
// Close the connection for the debugging listener
m_dbgConn->Close();
// Close the connection for the debugging executor
conn->Close();
delete conn;
conn = NULL;
// Declare that execution has been cancelled
m_terminated = true;
m_frm->EnableToolsAndMenus(false);
m_frm->CloseProgressBar();
return false;
}
}
m_execConnThread = new pgQueryThread(
conn, this, &(dbgController::NoticeHandler), this);
m_execConnThread->SetEventOnCancellation(false);
if (m_execConnThread->Create() != wxTHREAD_NO_ERROR)
{
delete m_execConnThread;
m_execConnThread = NULL;
conn->Close();
delete conn;
conn = NULL;
Stop();
return false;
}
return ExecuteTarget();
}
else if (m_sessionType == DBG_SESSION_TYPE_INCONTEXT)
{
// TODO:: Ask for the target session and different targets
dbgBreakPointList &breakpoints = m_model->GetBreakPoints();
WX_CLEAR_ARRAY(breakpoints);
dbgTargetInfo *target = m_model->GetTarget();
breakpoints.Append(new dbgBreakPoint(
wxString::Format(wxT("%lu"), target->GetOid()),
wxString::Format(wxT("%lu"), target->GetPkgOid())));
LOCKMUTEX(m_dbgThreadLock);
m_dbgThread->AddQuery(ms_cmdCreateListener, NULL, (long)RESULT_ID_LISTENER_CREATED);
UNLOCKMUTEX(m_dbgThreadLock);
}
return true;
}
bool dbgController::ExecuteTarget()
{
dbgTargetInfo *target = m_model->GetTarget();
wxString strDebugCmdPkgInitializer, strDebugCmdTarget;
if (m_ver <= DEBUGGER_V2_API)
{
if (target->GetLanguage() == wxT("edbspl"))
{
strDebugCmdPkgInitializer = wxString::Format(
ms_cmdDebugSPLV1, target->GetPkgOid(), target->GetPkgInitOid());
strDebugCmdTarget = wxString::Format(
ms_cmdDebugSPLV1, target->GetPkgOid(), target->GetOid());
}
else
{
strDebugCmdPkgInitializer = wxString::Format(
ms_cmdDebugPLPGSQLV1, target->GetPkgOid(), target->GetPkgInitOid());
strDebugCmdTarget = wxString::Format(
ms_cmdDebugPLPGSQLV1, target->GetPkgOid(), target->GetOid());
}
}
else
{
if (target->GetLanguage() == wxT("edbspl"))
{
strDebugCmdPkgInitializer = wxString::Format(
ms_cmdDebugSPLV2, target->GetPkgInitOid());
strDebugCmdTarget = wxString::Format(ms_cmdDebugSPLV2, target->GetOid());
}
else
{
strDebugCmdPkgInitializer = wxString::Format(
ms_cmdDebugPLPGSQLV2, target->GetPkgInitOid());
strDebugCmdTarget = wxString::Format(ms_cmdDebugPLPGSQLV2, target->GetOid());
}
}
if (target->DebugPackageConstructor())
{
m_execConnThread->AddQuery(strDebugCmdPkgInitializer);
}
m_execConnThread->AddQuery(strDebugCmdTarget);
if (target->AddForExecution(m_execConnThread))
{
// Start the execution thread
m_execConnThread->Run();
}
else
{
wxLogError(_("Couldn't determine how to execute the selected target."));
Stop();
return false;
}
return true;
}
void dbgController::NoticeHandler(void *_arg, const char *_msg)
{
dbgController *controller = (dbgController *)_arg;
if (controller->m_terminated)
return;
wxMBConv *conv = controller->m_dbgConn->GetConv();
wxString strMsg(_msg, *conv);
if (strMsg.EndsWith(wxT("\n")))
{
strMsg.RemoveLast();
}
if (strMsg.Find(wxT("PLDBGBREAK")) != wxNOT_FOUND)
{
// NOTICE: PLDBGBREAK 1
wxStringTokenizer tokens(strMsg, wxT(":\n"));
tokens.GetNextToken(); // NOTICE:
tokens.GetNextToken(); // PLDBGBREAK
controller->m_model->GetPort() = tokens.GetNextToken(); // <port number>
wxCommandEvent btnEvt(wxEVT_COMMAND_BUTTON_CLICKED, MENU_ID_START_DEBUGGING);
controller->AddPendingEvent(btnEvt);
}
else
{
wxCommandEvent btnEvt(wxEVT_COMMAND_BUTTON_CLICKED, MENU_ID_NOTICE_RECEIVED);
btnEvt.SetString(strMsg);
controller->AddPendingEvent(btnEvt);
}
}
// Debugging actions (called from the frmDebugger)
void dbgController::ClearBreakpoint(int _lineNo)
{
if (m_terminated || m_isStopping)
return;
if (m_ver <= DEBUGGER_V2_API)
{
LOCKMUTEX(m_dbgThreadLock);
m_dbgThread->AddQuery(
wxString::Format(
ms_cmdClearBreakpointV1, m_model->GetSession().c_str(),
m_model->GetDisplayedPackage().c_str(),
m_model->GetDisplayedFunction().c_str(), _lineNo + 1),
NULL, (long) RESULT_ID_NEW_BREAKPOINT);
UNLOCKMUTEX(m_dbgThreadLock);
}
else
{
LOCKMUTEX(m_dbgThreadLock);
m_dbgThread->AddQuery(
wxString::Format(
ms_cmdClearBreakpointV2, m_model->GetSession().c_str(),
m_model->GetDisplayedFunction().c_str(), _lineNo + 1),
NULL, RESULT_ID_NEW_BREAKPOINT);
UNLOCKMUTEX(m_dbgThreadLock);
}
}
void dbgController::SetBreakpoint(int _lineNo)
{
if (m_terminated || m_isStopping)
return;
if (m_ver <= DEBUGGER_V2_API)
{
LOCKMUTEX(m_dbgThreadLock);
m_dbgThread->AddQuery(
wxString::Format(
ms_cmdSetBreakpointV1, m_model->GetSession().c_str(),
m_model->GetDisplayedPackage().c_str(),
m_model->GetDisplayedFunction().c_str(), _lineNo + 1),
NULL, RESULT_ID_NEW_BREAKPOINT);
UNLOCKMUTEX(m_dbgThreadLock);
}
else
{
LOCKMUTEX(m_dbgThreadLock);
m_dbgThread->AddQuery(
wxString::Format(
ms_cmdSetBreakpointV2, m_model->GetSession().c_str(),
m_model->GetDisplayedFunction().c_str(), _lineNo + 1),
NULL, RESULT_ID_NEW_BREAKPOINT);
UNLOCKMUTEX(m_dbgThreadLock);
}
}
void dbgController::Countinue()
{
if (m_terminated || m_isStopping)
return;
pgParamsArray *params = new pgParamsArray;
params->Add(new pgParam(PGOID_TYPE_INT4, &(m_model->GetSession())));
if (m_ver <= DEBUGGER_V2_API)
{
m_dbgThread->AddQuery(ms_cmdContinueV1, params, RESULT_ID_BREAKPOINT);
}
else
{
m_dbgThread->AddQuery(ms_cmdContinueV2, params, RESULT_ID_BREAKPOINT);
}
// Do not allow any action at this time
m_frm->EnableToolsAndMenus(false);
}
void dbgController::StepOver()
{
if (m_terminated || m_isStopping)
return;
pgParamsArray *params = new pgParamsArray;
params->Add(new pgParam(PGOID_TYPE_INT4, &(m_model->GetSession())));
if (m_ver <= DEBUGGER_V2_API)
{
LOCKMUTEX(m_dbgThreadLock);
m_dbgThread->AddQuery(ms_cmdStepOverV1, params, RESULT_ID_BREAKPOINT);
UNLOCKMUTEX(m_dbgThreadLock);
}
else
{
LOCKMUTEX(m_dbgThreadLock);
m_dbgThread->AddQuery(ms_cmdStepOverV2, params, RESULT_ID_BREAKPOINT);
UNLOCKMUTEX(m_dbgThreadLock);
}
// Do not allow any action at this time
m_frm->EnableToolsAndMenus(false);
}
void dbgController::StepInto()
{
if (m_terminated || m_isStopping)
return;
pgParamsArray *params = new pgParamsArray;
params->Add(new pgParam(PGOID_TYPE_INT4, &(m_model->GetSession())));
if (m_ver <= DEBUGGER_V2_API)
{
LOCKMUTEX(m_dbgThreadLock);
m_dbgThread->AddQuery(ms_cmdStepIntoV1, params, RESULT_ID_BREAKPOINT);
UNLOCKMUTEX(m_dbgThreadLock);
}
else
{
LOCKMUTEX(m_dbgThreadLock);
m_dbgThread->AddQuery(ms_cmdStepIntoV2, params, RESULT_ID_BREAKPOINT);
UNLOCKMUTEX(m_dbgThreadLock);
}
// Do not allow any action at this time
m_frm->EnableToolsAndMenus(false);
}
bool dbgController::Stop()
{
if (m_terminated)
return true;
if (m_isStopping)
return false;
LOCKMUTEX(m_dbgThreadLock);
m_frm->EnableToolsAndMenus(false);
m_frm->LaunchWaitingDialog(_("Waiting for target stop execution..."));
m_isStopping = true;
switch(m_sessionType)
{
case DBG_SESSION_TYPE_DIRECT:
{
// We must send the abort target command to the executor
// session
if (m_execConnThread)
{
if (m_execConnThread->IsRunning())
{
// Ask the direct debugging executor to not to handle any of the error or
// result any more.
m_execConnThread->CancelExecution();
if (m_dbgConn->GetStatus() != CONNECTION_BAD && m_model->GetSession() != wxEmptyString)
{
// And then, we will ask the backend to abort the target, so that the
// target backend will wait for any commands from debugging proxy.
pgConn *conn = m_dbgThread->GetConn();
pgSet *set = conn->ExecuteSet(
wxString::Format(ms_cmdAbortTarget, m_model->GetSession().c_str()));
if (set)
delete set;
}
while (m_execConnThread && m_execConnThread->IsRunning())
{
wxTheApp->Yield(true);
wxMilliSleep(3);
}
if (m_execConnThread)
m_execConnThread->Wait();
}
if (m_execConnThread)
{
// We also need to deallocate the momory for the connection
pgConn *conn = m_execConnThread->GetConn();
delete m_execConnThread;
m_execConnThread = NULL;
conn->Close();
delete conn;
conn = NULL;
}
}
}
break;
default:
break;
}
if (m_dbgThread)
{
if (m_dbgThread->IsRunning())
{
m_dbgThread->CancelExecution();
m_dbgThread->Wait();
}
delete m_dbgThread;
m_dbgThread = NULL;
}
// Disconnect the debugger connection
m_dbgConn->Close();
m_frm->CloseProgressBar();
m_terminated = true;
m_isStopping = false;
UNLOCKMUTEX(m_dbgThreadLock);
return true;
}
void dbgController::DepositValue(const wxString &_name, const wxString &_val)
{
if (m_terminated || m_isStopping)
return;
LOCKMUTEX(m_dbgThreadLock);
m_dbgThread->AddQuery(
wxString::Format(
ms_cmdDepositValue, m_model->GetSession().c_str(),
m_dbgConn->qtDbString(_name).c_str(), -1,
m_dbgConn->qtDbString(_val).c_str()),
NULL, RESULT_ID_DEPOSIT_VALUE);
UNLOCKMUTEX(m_dbgThreadLock);
}
bool dbgController::SelectFrame(int _frameNo)
{
LOCKMUTEX(m_dbgThreadLock);
if (m_terminated || m_isStopping)
{
UNLOCKMUTEX(m_dbgThreadLock);
return true;
}
ctlStackWindow *stackWin = m_frm->GetStackWindow();
dbgStackFrame *frame = (dbgStackFrame *)stackWin->GetClientObject(_frameNo);
if (!frame || frame->GetFunction() == wxT("0"))
{
UNLOCKMUTEX(m_dbgThreadLock);
wxMessageBox(
_("Cannot fetch information about the anonymous block!"),
_("Invalid Stack"), wxICON_ERROR | wxOK);
return false;
}
wxString strLevel = frame->GetLevel();
pgParamsArray *params = new pgParamsArray;
params->Add(new pgParam(PGOID_TYPE_INT4, &(m_model->GetSession())));
params->Add(new pgParam(PGOID_TYPE_INT4, &strLevel));
if (m_ver <= DEBUGGER_V2_API)
{
m_dbgThread->AddQuery(ms_cmdSelectFrameV1, params, RESULT_ID_BREAKPOINT);
}
else
{
m_dbgThread->AddQuery(ms_cmdSelectFrameV2, params, RESULT_ID_BREAKPOINT);
}
UNLOCKMUTEX(m_dbgThreadLock);
return true;
}
void dbgController::UpdateBreakpoints()
{
if (m_terminated || m_isStopping)
return;
LOCKMUTEX(m_dbgThreadLock);
m_dbgThread->AddQuery(
wxString::Format(ms_cmdGetBreakpoints, m_model->GetSession().c_str()),
NULL, RESULT_ID_GET_BREAKPOINTS);
UNLOCKMUTEX(m_dbgThreadLock);
}
// Closing Debugger
bool dbgController::CloseDebugger()
{
if (Stop())
{
if (m_dbgConn)
{
delete m_dbgConn;
m_dbgConn = NULL;
}
return true;
}
return false;
}
dbgTargetInfo *dbgController::GetTargetInfo()
{
return m_model->GetTarget();
}

974
debugger/dbgEvents.cpp Normal file
View file

@ -0,0 +1,974 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// dbgEvents.cpp - debugger controller events
// - This files contains the functions related to the event handling of the
// debugger controller.
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
#include <wx/tokenzr.h>
#include "ctl/ctlAuiNotebook.h"
#include "db/pgConn.h"
#include "db/pgQueryThread.h"
#include "db/pgQueryResultEvent.h"
#include "schema/pgObject.h"
#include "debugger/dbgConst.h"
#include "debugger/dbgBreakPoint.h"
#include "debugger/dbgController.h"
#include "debugger/dbgModel.h"
#include "debugger/ctlStackWindow.h"
#include "debugger/ctlMessageWindow.h"
#include "debugger/ctlTabWindow.h"
#include "debugger/frmDebugger.h"
#include "debugger/dlgDirectDbg.h"
#include "utils/pgDefs.h"
#define LOG_DBG_MUTEX_LOCKING 0
#if LOG_DBG_MUTEX_LOCKING
#define LOCKMUTEX(m) \
wxPrintf(wxT("Mutex locking @ %s:%d\n"), wxString::FromAscii(__WXFUNCTION__).c_str(), __LINE__); \
m.Lock(); \
wxPrintf(wxT("Mutex locked @ %s:%d\n"), wxString::FromAscii(__WXFUNCTION__).c_str(), __LINE__);
#define UNLOCKMUTEX(m) \
wxPrintf(wxT("Mutex unlocking @ %s:%d\n"), wxString::FromAscii(__WXFUNCTION__).c_str(), __LINE__); \
m.Unlock(); \
wxPrintf(wxT("Mutex unlocked @ %s:%d\n"), wxString::FromAscii(__WXFUNCTION__).c_str(), __LINE__);
#else
#define LOCKMUTEX(m) \
m.Lock();
#define UNLOCKMUTEX(m) \
m.Unlock();
#endif
BEGIN_EVENT_TABLE(dbgController, wxEvtHandler)
EVT_BUTTON(MENU_ID_NOTICE_RECEIVED, dbgController::OnNoticeReceived)
EVT_BUTTON(MENU_ID_START_DEBUGGING, dbgController::OnStartDebugging)
EVT_PGQUERYRESULT(RESULT_ID_DIRECT_TARGET_COMPLETE, dbgController::ResultTargetComplete)
EVT_PGQUERYRESULT(RESULT_ID_ATTACH_TO_PORT, dbgController::ResultPortAttach)
EVT_PGQUERYRESULT(RESULT_ID_LISTENER_CREATED, dbgController::ResultListenerCreated)
EVT_PGQUERYRESULT(RESULT_ID_TARGET_READY, dbgController::ResultTargetReady)
EVT_PGQUERYRESULT(RESULT_ID_DEL_BREAKPOINT, dbgController::ResultDeletedBreakpoint)
EVT_PGQUERYRESULT(RESULT_ID_BREAKPOINT, dbgController::ResultBreakpoint)
EVT_PGQUERYRESULT(RESULT_ID_NEW_BREAKPOINT, dbgController::ResultNewBreakpoint)
EVT_PGQUERYRESULT(RESULT_ID_NEW_BREAKPOINT_WAIT, dbgController::ResultNewBreakpointWait)
EVT_PGQUERYRESULT(RESULT_ID_GET_VARS, dbgController::ResultVarList)
EVT_PGQUERYRESULT(RESULT_ID_GET_STACK, dbgController::ResultStack)
EVT_PGQUERYRESULT(RESULT_ID_GET_BREAKPOINTS, dbgController::ResultBreakpoints)
EVT_PGQUERYRESULT(RESULT_ID_DEPOSIT_VALUE, dbgController::ResultDepositValue)
END_EVENT_TABLE()
// Event functions
void dbgController::OnNoticeReceived(wxCommandEvent &_ev)
{
m_frm->GetMessageWindow()->AddMessage(_ev.GetString());
m_frm->GetTabWindow()->SelectTab(ID_MSG_PAGE);
}
void dbgController::OnStartDebugging(wxCommandEvent &_ev)
{
LOCKMUTEX(m_dbgThreadLock);
// Do not bother to process the result, if the debugger thread is not
// running or not exists
if (m_dbgThread && m_dbgThread->IsRunning())
{
m_dbgThread->AddQuery(
wxString::Format(ms_cmdAttachToPort, m_model->GetPort().c_str()),
NULL, RESULT_ID_ATTACH_TO_PORT);
}
UNLOCKMUTEX(m_dbgThreadLock);
}
void dbgController::ResultTargetComplete(pgQueryResultEvent &_ev)
{
pgBatchQuery *qry = _ev.GetQuery();
m_frm->EnableToolsAndMenus(false);
m_terminated = true;
m_frm->SetStatusText(_("Debugging Completed"));
m_frm->CloseProgressBar();
wxTheApp->Yield(true);
if (m_execConnThread)
{
switch(qry->ReturnCode())
{
case pgQueryResultEvent::PGQ_CONN_LOST:
wxLogInfo(
wxString::Format(
_("Debugger(%ld): Execution of the debugging target terminated due to connection loss.\n"),
m_execConnThread->GetId()));
break;
// This is very unlikely, unless we made mistake creating the query
case pgQueryResultEvent::PGQ_STRING_INVALID:
wxLogInfo(
wxString::Format(
_("Debugger(%ld): Execution of the debugging target terminated due to empty query string.\n"),
m_execConnThread->GetId()));
break;
case pgQueryResultEvent::PGQ_ERROR_PREPARE_CALLABLE:
case pgQueryResultEvent::PGQ_ERROR_EXECUTE_CALLABLE:
case pgQueryResultEvent::PGQ_ERROR_SEND_QUERY:
case pgQueryResultEvent::PGQ_ERROR_CONSUME_INPUT:
wxLogInfo(
wxString::Format(
_("Debugger(%ld): Execution of the debugging target terminated due to execution error (%d).\n"),
m_execConnThread->GetId(), qry->ReturnCode()));
break;
case pgQueryResultEvent::PGQ_EXECUTION_CANCELLED:
m_frm->SetStatusText(_("Debugging Cancelled"));
wxLogInfo(
wxString::Format(
_("Debugger(%ld): Execution of the debugging target cancelled.\n"),
m_execConnThread->GetId()));
break;
case pgQueryResultEvent::PGQ_RESULT_ERROR:
{
wxString strErr = qry->GetErrorMessage();
m_frm->SetStatusText(_("Execution completed with an error."));
wxLogInfo(
wxString::Format(
_("Debugger(%ld): Function/Procedure terminated with an error.\n%s"),
m_execConnThread->GetId(), strErr.c_str()));
m_frm->GetMessageWindow()->AddMessage(qry->GetMessage());
m_frm->GetMessageWindow()->SetFocus();
}
break;
case PGRES_FATAL_ERROR:
case PGRES_NONFATAL_ERROR:
case PGRES_BAD_RESPONSE:
{
wxString strErr = qry->GetErrorMessage();
m_frm->SetStatusText(_("Execution completed with an error."));
wxLogInfo(
wxString::Format(
_("Debugger(%ld): Function/Procedure terminated with an error.\n%s"),
m_execConnThread->GetId(), strErr.c_str()));
m_frm->GetMessageWindow()->AddMessage(qry->GetMessage());
m_frm->GetMessageWindow()->SetFocus();
}
break;
case PGRES_TUPLES_OK:
{
m_frm->SetStatusText(_("Execution completed."));
pgSet *set = qry->ResultSet();
m_frm->GetMessageWindow()->AddMessage(set->GetCommandStatus());
m_frm->GetResultWindow()->FillResult(set);
}
break;
default:
m_frm->SetStatusText(_("Execution completed."));
wxLogInfo(
wxString::Format(
_("Debugger(%ld): Execution of the debugging function/procedure completed\n"),
m_execConnThread->GetId()));
m_frm->GetMessageWindow()->AddMessage(qry->GetMessage());
m_frm->GetMessageWindow()->SetFocus();
break;
}
if (m_execConnThread->IsRunning())
{
m_execConnThread->CancelExecution();
m_execConnThread->Wait();
}
pgConn *execConn = m_execConnThread->GetConn();
delete m_execConnThread;
m_execConnThread = NULL;
execConn->Close();
delete execConn;
}
LOCKMUTEX(m_dbgThreadLock);
pgQueryThread *oldDebugThread = m_dbgThread;
m_dbgThread = NULL;
UNLOCKMUTEX(m_dbgThreadLock);
if (oldDebugThread)
{
oldDebugThread->CancelExecution();
oldDebugThread->Wait();
delete oldDebugThread;
}
// We won't keep the connection open, we will open it only when required
if (m_dbgConn)
m_dbgConn->Close();
if (m_frm)
m_frm->EnableToolsAndMenus(false);
}
void dbgController::ResultPortAttach(pgQueryResultEvent &_ev)
{
pgBatchQuery *qry = _ev.GetQuery();
if (!HandleQuery(qry, _("Error attaching the proxy session.")))
{
return;
}
if (qry->ReturnCode() != PGRES_TUPLES_OK)
{
Stop();
return;
}
m_frm->EnableToolsAndMenus(false);
wxString strSession = qry->ResultSet()->GetVal(0);
m_model->GetSession() = strSession;
pgParamsArray *params = new pgParamsArray;
params->Add(new pgParam(PGOID_TYPE_INT4, &strSession));
LOCKMUTEX(m_dbgThreadLock);
if (m_dbgThread && m_dbgThread->IsRunning())
{
if (m_ver <= DEBUGGER_V2_API)
{
m_dbgThread->AddQuery(ms_cmdWaitForBreakpointV1, params, RESULT_ID_BREAKPOINT);
}
else
{
m_dbgThread->AddQuery(ms_cmdWaitForBreakpointV2, params, RESULT_ID_BREAKPOINT);
}
qry->Release();
// Now create any breakpoints that the user created in last execution. We
// start by asking the server to resolve the breakpoint name into an OID (or,
// a pair of OID's if the target is defined in a package). As each
// (targetInof) result arrives, we add a break-point at the resulting OID.
dbgBreakPointList breakpoints = m_model->GetBreakPoints();
for (dbgBreakPointList::Node *node = breakpoints.GetFirst(); node;
node = node->GetNext())
{
dbgBreakPoint *breakpoint = node->GetData();
/*
* In EnterpriseDB versions <= 9.1 the
* pldbg_set_global_breakpoint function took five arguments,
* the 2nd argument being the package's OID, if any. Starting
* with 9.2, the package OID argument is gone, and the function
* takes four arguments like the community version has always
* done.
*/
if (m_dbgConn->GetIsEdb() && !m_dbgConn->BackendMinimumVersion(9, 2))
{
m_dbgThread->AddQuery(
wxString::Format(
ms_cmdSetBreakpointV1, m_model->GetSession().c_str(),
breakpoint->GetPackageOid().c_str(),
breakpoint->GetFunctionOid().c_str(),
breakpoint->GetLineNo()),
NULL, RESULT_ID_NEW_BREAKPOINT);
}
else
{
m_dbgThread->AddQuery(
wxString::Format(
ms_cmdSetBreakpointV2, m_model->GetSession().c_str(),
breakpoint->GetFunctionOid().c_str(),
breakpoint->GetLineNo()),
NULL, RESULT_ID_NEW_BREAKPOINT);
}
}
}
UNLOCKMUTEX(m_dbgThreadLock);
}
bool dbgController::HandleQuery(pgBatchQuery *_qry, const wxString &_err)
{
if (!_qry)
return false;
LOCKMUTEX(m_dbgThreadLock);
// It is possible that we found one error, while running the previous query
// and, called Stop function from it, because of an error found.
// That may have released the debugger thread
//
// In this case, we should exit this flow gracefully instead showing any
// error
if (!m_dbgThread || !m_dbgThread->IsRunning())
{
UNLOCKMUTEX(m_dbgThreadLock);
return false;
}
switch(_qry->ReturnCode())
{
case pgQueryResultEvent::PGQ_CONN_LOST:
// This is very unlikely, unless we made mistake creating the query
case pgQueryResultEvent::PGQ_STRING_INVALID:
case PGRES_EMPTY_QUERY:
case pgQueryResultEvent::PGQ_ERROR_PREPARE_CALLABLE:
case pgQueryResultEvent::PGQ_ERROR_EXECUTE_CALLABLE:
case pgQueryResultEvent::PGQ_ERROR_SEND_QUERY:
case pgQueryResultEvent::PGQ_ERROR_CONSUME_INPUT:
case pgQueryResultEvent::PGQ_RESULT_ERROR:
case PGRES_BAD_RESPONSE:
case PGRES_NONFATAL_ERROR:
case PGRES_FATAL_ERROR:
break;
// This is unlikely, we do not generate an event, when execution is
// cancelled
case pgQueryResultEvent::PGQ_EXECUTION_CANCELLED:
break;
case PGRES_COMMAND_OK:
case PGRES_TUPLES_OK:
UNLOCKMUTEX(m_dbgThreadLock);
return true;
// Hmm - where did it come from?
// We never call a function, which results into these results
// Anyway - we will return true as we have the result
case PGRES_COPY_IN:
case PGRES_COPY_OUT:
UNLOCKMUTEX(m_dbgThreadLock);
return true;
}
m_frm->EnableToolsAndMenus(false);
wxTheApp->Yield(true);
if (!m_dbgConn->IsAlive())
{
UNLOCKMUTEX(m_dbgThreadLock);
wxMessageBox(
_("Connection to the database server lost, debugging cannot continue!"),
_("Connection Lost"), wxICON_ERROR | wxOK);
}
else
{
wxString strErr;
if (!_err.IsEmpty())
strErr = _err + wxT("\n\n");
strErr += _qry->GetErrorMessage();
UNLOCKMUTEX(m_dbgThreadLock);
if (_qry->ReturnCode() == PGRES_FATAL_ERROR)
{
// We will start start listening for new in-context session, if the
// current session is closed. On which, the query/target was
// running.
//
// This allows us to have the same behaviour as the old one.
//
if (m_sessionType == DBG_SESSION_TYPE_INCONTEXT && m_currTargetPid != wxT(""))
{
// Let's check if the target pid has stopped or exited after
// successful debugging, let's move on and wait for the next
// target to hit.
wxString isTargetRunning = m_dbgConn->ExecuteScalar(wxString::Format(ms_cmdIsBackendRunning, m_currTargetPid.c_str()));
if (isTargetRunning == wxT("0"))
{
// Reset the current backend-pid of the target
m_currTargetPid = wxT("");
m_dbgThread->AddQuery(
wxString::Format(
ms_cmdWaitForTarget, m_model->GetSession().c_str()),
NULL, RESULT_ID_TARGET_READY);
m_frm->LaunchWaitingDialog();
return false;
}
}
else
{
wxMessageBox(_("The calling connection was closed or lost."), _("Connection Lost"), wxICON_ERROR | wxOK);
}
}
else
{
wxMessageBox(strErr, _("Execution Error"), wxICON_ERROR | wxOK);
}
wxLogQuietError(strErr);
}
Stop();
m_terminated = true;
m_frm->SetStatusText(_("Debugging aborting..."));
return false;
}
void dbgController::ResultBreakpoint(pgQueryResultEvent &_ev)
{
if (m_frm)
m_frm->CloseProgressBar();
pgBatchQuery *qry = _ev.GetQuery();
if (!HandleQuery(qry, wxEmptyString))
return;
LOCKMUTEX(m_dbgThreadLock);
// Do not bother to process the result, if the debugger thread is not
// running or not exists
if (m_dbgThread && m_dbgThread->IsRunning())
{
if (qry->ReturnCode() == PGRES_TUPLES_OK)
{
wxString func,
pkg = wxT("0");
pgSet *set = qry->ResultSet();
if (set->HasColumn(wxT("pkg")))
{
pkg = set->GetVal(wxT("pkg"));
}
func = set->GetVal(wxT("func"));
m_model->GetFocusedPackage() = pkg;
m_model->GetFocusedFunction() = func;
int lineNo = (int)(set->GetLong(wxT("linenumber")) - 1);
if (lineNo < 0)
lineNo = -1;
else
lineNo -= m_lineOffset;
m_model->GetCurrLineNo() = lineNo;
wxString strSource = set->GetVal(wxT("src"));
if (strSource.IsEmpty())
strSource = _("<source not available>");
dbgCachedStack src(pkg, func, set->GetVal(wxT("targetname")),
set->GetVal(wxT("args")), strSource);
m_model->AddSource(func, src);
m_frm->DisplaySource(src);
m_dbgThread->AddQuery(
wxString::Format(ms_cmdGetStack, m_model->GetSession().c_str()),
NULL, RESULT_ID_GET_STACK);
m_frm->EnableToolsAndMenus(true);
}
// Release the result-set
qry->Release();
}
UNLOCKMUTEX(m_dbgThreadLock);
}
void dbgController::ResultVarList(pgQueryResultEvent &_ev)
{
pgBatchQuery *qry = _ev.GetQuery();
if (!HandleQuery(qry, _("Error fetching variables.")))
return;
LOCKMUTEX(m_dbgThreadLock);
// Do not bother to process the result, if the debugger thread is not
// running or not exists
if (m_dbgThread && m_dbgThread->IsRunning())
{
if (qry->ReturnCode() == PGRES_TUPLES_OK)
{
ctlVarWindow *paramWin = NULL, *pkgVarWin = NULL, *varWin = NULL;
pgSet *set = qry->ResultSet();
while (!set->Eof())
{
switch((char)(set->GetVal(wxT("varclass"))[0]))
{
case 'A':
{
if (paramWin == NULL)
paramWin = m_frm->GetParamWindow(true);
paramWin->AddVar(
set->GetVal(wxT("name")),
set->GetVal(wxT("value")),
set->GetVal(wxT("dtype")),
set->GetBool(wxT("isconst")));
break;
}
case 'P':
{
if (pkgVarWin == NULL)
pkgVarWin = m_frm->GetPkgVarWindow(true);
pkgVarWin->AddVar(
set->GetVal(wxT("name")),
set->GetVal(wxT("value")),
set->GetVal(wxT("dtype")),
set->GetBool(wxT("isconst")));
break;
}
default:
{
if (varWin == NULL)
varWin = m_frm->GetVarWindow(true);
varWin->AddVar(
set->GetVal(wxT("name")),
set->GetVal(wxT("value")),
set->GetVal(wxT("dtype")),
set->GetBool(wxT("isconst")));
break;
}
}
set->MoveNext();
}
}
// Release the result-set
qry->Release();
}
UNLOCKMUTEX(m_dbgThreadLock);
}
void dbgController::ResultStack(pgQueryResultEvent &_ev)
{
pgBatchQuery *qry = _ev.GetQuery();
if (!HandleQuery(qry, _("Error fetching the call stack.")))
return;
LOCKMUTEX(m_dbgThreadLock);
// Do not bother to process the result, if the debugger thread is not
// running or not exists
if (m_dbgThread && m_dbgThread->IsRunning())
{
if (qry->ReturnCode() == PGRES_TUPLES_OK)
{
pgSet *set = qry->ResultSet();
dbgStackFrameList stacks;
ctlStackWindow *stackWin = m_frm->GetStackWindow();
// Fetched the stack, now fetch the break-points
m_dbgThread->AddQuery(
wxString::Format(ms_cmdGetBreakpoints, m_model->GetSession().c_str()),
NULL, RESULT_ID_GET_BREAKPOINTS);
stackWin->ClearStack();
int selected = 0,
levelCol = set->ColNumber(wxT("level")),
pkgCol = set->HasColumn(wxT("pkg")) ? set->ColNumber(wxT("level")) : -1,
funCol = set->ColNumber(wxT("func")),
targetCol = set->ColNumber(wxT("targetname")),
argsCol = set->ColNumber(wxT("args")),
lineCol = set->ColNumber(wxT("linenumber"));
while (!set->Eof())
{
// The result set contains one tuple per frame:
// package, function, linenumber, args
dbgStackFrame *frame = new dbgStackFrame(
set->GetVal(levelCol),
pkgCol != -1 ? set->GetVal(wxT("pkg")) : wxT("0"),
set->GetVal(funCol),
wxString::Format(
wxT("%s(%s)@%s"),
set->GetVal(targetCol).c_str(),
set->GetVal(argsCol).c_str(),
set->GetVal(lineCol).c_str()));
// Select this one in the stack window
if (frame->GetFunction() == m_model->GetDisplayedFunction() &&
frame->GetPackage() == m_model->GetDisplayedPackage())
{
selected = set->CurrentPos() - 1;
}
stacks.Append(frame);
set->MoveNext();
}
stackWin->SetStack(stacks, selected);
}
// Release the result-set
qry->Release();
}
UNLOCKMUTEX(m_dbgThreadLock);
}
void dbgController::ResultBreakpoints(pgQueryResultEvent &_ev)
{
pgBatchQuery *qry = _ev.GetQuery();
if (!HandleQuery(qry, _("Error fetching breakpoints.")))
return;
LOCKMUTEX(m_dbgThreadLock);
// Do not bother to process the result, if the debugger thread is not
// running or not exists
if (m_dbgThread && m_dbgThread->IsRunning())
{
if (qry->ReturnCode() == PGRES_TUPLES_OK)
{
pgSet *set = qry->ResultSet();
int pkgCol = -1, funcCol = set->ColNumber(wxT("func")),
lineCol = set->ColNumber(wxT("linenumber"));
if (set->HasColumn(wxT("pkg")))
{
pkgCol = set->ColNumber(wxT("pkg"));
}
m_dbgThread->AddQuery(
wxString::Format(ms_cmdGetVars, m_model->GetSession().c_str()),
NULL, RESULT_ID_GET_VARS);
m_frm->ClearBreakpointMarkers();
dbgBreakPointList &breakpoints = m_model->GetBreakPoints();
WX_CLEAR_ARRAY(breakpoints);
while (!set->Eof())
{
// The result set contains one tuple per breakpoint:
// pkg, func, linenumber, target
// or,
// func, linenumber, target
wxString pkg = (pkgCol == -1) ? wxT("0") : set->GetVal(pkgCol);
wxString func = set->GetVal(funcCol);
int lineNumber = (int)set->GetLong(lineCol);
// Save this break-points in break-point list
breakpoints.Append(new dbgBreakPoint(func, pkg, lineNumber));
// Mark the break-point in the viewer
if (pkg == m_model->GetDisplayedPackage() &&
func == m_model->GetDisplayedFunction())
{
m_frm->MarkBreakpoint(lineNumber - 1);
}
set->MoveNext();
}
}
// Release the result-set
qry->Release();
}
UNLOCKMUTEX(m_dbgThreadLock);
}
void dbgController::ResultNewBreakpoint(pgQueryResultEvent &_ev)
{
// We will wait for the last new break-point, which will be handled in
// dbgController::ResultNewBreakpointWait function
if (!HandleQuery(_ev.GetQuery(), _("Could not create the breakpoint.")))
return;
LOCKMUTEX(m_dbgThreadLock);
// Do not bother to process the result, if the debugger thread is not
// running or not exists
if (m_dbgThread && m_dbgThread->IsRunning())
{
// Release the result-set
_ev.GetQuery()->Release();
}
UNLOCKMUTEX(m_dbgThreadLock);
}
void dbgController::ResultNewBreakpointWait(pgQueryResultEvent &_ev)
{
pgBatchQuery *qry = _ev.GetQuery();
if (!HandleQuery(qry, _("Could not create the breakpoint.")))
return;
LOCKMUTEX(m_dbgThreadLock);
// Do not bother to process the result, if the debugger thread is not
// running or not exists
if (m_dbgThread && m_dbgThread->IsRunning())
{
if (qry->ReturnCode() == PGRES_TUPLES_OK)
{
pgSet *set = _ev.GetQuery()->ResultSet();
m_frm->EnableToolsAndMenus(false);
m_dbgThread->AddQuery(
wxString::Format(
ms_cmdWaitForTarget, m_model->GetSession().c_str()),
NULL, RESULT_ID_TARGET_READY);
m_frm->LaunchWaitingDialog();
}
// Release the result-set
qry->Release();
}
UNLOCKMUTEX(m_dbgThreadLock);
}
void dbgController::ResultDeletedBreakpoint(pgQueryResultEvent &_ev)
{
pgBatchQuery *qry = _ev.GetQuery();
if (!HandleQuery(qry, _("Could not drop the breakpoint.")))
return;
LOCKMUTEX(m_dbgThreadLock);
// Do not bother to process the result, if the debugger thread is not
// running or not exists
if (m_dbgThread && m_dbgThread->IsRunning())
{
if (qry->ReturnCode() == PGRES_TUPLES_OK)
{
pgSet *set = qry->ResultSet();
if (set->GetBool(0))
{
m_frm->SetStatusText(_("Breakpoint dropped"));
}
}
// Release the result-set
qry->Release();
}
UNLOCKMUTEX(m_dbgThreadLock);
}
////////////////////////////////////////////////////////////////////////////////
// ResultDepositValue()
//
// This event handler is called when the proxy completes a 'deposit' operation
// (in response to an earlier call to pldbg_deposit_value()).
//
// We schedule a refresh of our variable window(s) for the next idle period.
//
void dbgController::ResultDepositValue(pgQueryResultEvent &_ev)
{
pgBatchQuery *qry = _ev.GetQuery();
if (!HandleQuery(qry, _("Could not deposit the new value.")))
return;
LOCKMUTEX(m_dbgThreadLock);
// Do not bother to process the result, if the debugger thread is not
// running or not exists
if (m_dbgThread && m_dbgThread->IsRunning())
{
if (qry->ReturnCode() == PGRES_TUPLES_OK)
{
pgSet *set = qry->ResultSet();
if (set->GetBool(0))
{
m_frm->SetStatusText(_("Value changed"));
m_dbgThread->AddQuery(
wxString::Format(ms_cmdGetVars, m_model->GetSession().c_str()),
NULL, RESULT_ID_GET_VARS);
}
else
{
wxLogError(_("Could not deposit the new value."));
}
}
// Release the result-set
qry->Release();
}
UNLOCKMUTEX(m_dbgThreadLock);
}
void dbgController::ResultListenerCreated(pgQueryResultEvent &_ev)
{
pgBatchQuery *qry = _ev.GetQuery();
if (!HandleQuery(qry,
_("Could not create the proxy listener for in-process debugging.")))
return;
LOCKMUTEX(m_dbgThreadLock);
// Do not bother to process the result, if the debugger thread is not
// running or not exists
if (m_dbgThread && m_dbgThread->IsRunning())
{
if (qry->ReturnCode() == PGRES_TUPLES_OK)
{
pgSet *set = qry->ResultSet();
// We now have a global listener and a session handle. Grab the session
// handle (we'll need it for just about everything else).
m_model->GetSession() = set->GetVal(0);
// Release the result-set
qry->Release();
// Now create any global breakpoints that the user requested. We start
// by asking the server to resolve the breakpoint name into an OID (or,
// a pair of OID's if the target is defined in a package). As each
// (targetInof) result arrives, we add a break-point at the resulting
// OID.
unsigned int idx = 1;
long resultId;
dbgBreakPointList breakpoints = m_model->GetBreakPoints();
for (dbgBreakPointList::Node *node = breakpoints.GetFirst(); node;
node = node->GetNext(), ++idx)
{
dbgBreakPoint *breakpoint = node->GetData();
if(idx < breakpoints.GetCount())
{
resultId = RESULT_ID_NEW_BREAKPOINT;
}
else
{
resultId = RESULT_ID_NEW_BREAKPOINT_WAIT;
}
/*
* In EnterpriseDB versions <= 9.1 the
* pldbg_set_global_breakpoint function took five arguments,
* the 2nd argument being the package's OID, if any. Starting
* with 9.2, the package OID argument is gone, and the function
* takes four arguments like the community version has always
* done.
*/
if (m_dbgConn->GetIsEdb() && !m_dbgConn->BackendMinimumVersion(9, 2))
{
m_dbgThread->AddQuery(
wxString::Format(
ms_cmdAddBreakpointEDB, m_model->GetSession().c_str(),
breakpoint->GetPackageOid().c_str(),
breakpoint->GetFunctionOid().c_str(),
m_model->GetTargetPid().c_str()),
NULL, resultId);
}
else
{
m_dbgThread->AddQuery(
wxString::Format(
ms_cmdAddBreakpointPG, m_model->GetSession().c_str(),
breakpoint->GetFunctionOid().c_str(),
m_model->GetTargetPid().c_str()),
NULL, resultId);
}
}
UNLOCKMUTEX(m_dbgThreadLock);
}
else
{
// Release the result-set
qry->Release();
UNLOCKMUTEX(m_dbgThreadLock);
}
}
else
{
UNLOCKMUTEX(m_dbgThreadLock);
}
}
void dbgController::ResultTargetReady(pgQueryResultEvent &_ev)
{
pgBatchQuery *qry = _ev.GetQuery();
if (m_frm)
{
m_frm->CloseProgressBar();
m_frm->EnableToolsAndMenus(false);
}
if (!HandleQuery(qry,
_("Error fetching target information.")))
return;
LOCKMUTEX(m_dbgThreadLock);
// Do not bother to process the result, if the debugger thread is not
// running or not exists
if (m_dbgThread && m_dbgThread->IsRunning())
{
bool goAhead = false;
goAhead = (qry->ReturnCode() == PGRES_TUPLES_OK);
pgSet *set = qry->ResultSet();
// Save the current running target pid
m_currTargetPid = set->GetVal(0);
// Next line release the actual result-set
set = NULL;
// Release the result-set
qry->Release();
UNLOCKMUTEX(m_dbgThreadLock);
pgParamsArray *params = new pgParamsArray;
params->Add(new pgParam(PGOID_TYPE_INT4, &(m_model->GetSession())));
if (m_ver <= DEBUGGER_V2_API)
{
m_dbgThread->AddQuery(ms_cmdWaitForBreakpointV1, params, RESULT_ID_BREAKPOINT);
}
else
{
m_dbgThread->AddQuery(ms_cmdWaitForBreakpointV2, params, RESULT_ID_BREAKPOINT);
}
}
else
{
UNLOCKMUTEX(m_dbgThreadLock);
}
}

63
debugger/dbgModel.cpp Normal file
View file

@ -0,0 +1,63 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// dbgModel.cpp - debugger model
// - It contains the data and information related the debugging session
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
#include "db/pgConn.h"
#include "db/pgQueryThread.h"
#include "db/pgQueryResultEvent.h"
#include "debugger/dbgModel.h"
dbgModel::dbgModel(Oid _target, pgConn *_conn)
: m_target(NULL), m_currLineNo(-1), m_targetPid(wxT("NULL"))
{
m_target = new dbgTargetInfo(_target, _conn);
}
bool dbgModel::GetSource(const wxString &_funcOid, dbgCachedStack *_cached)
{
dbgSourceHash::iterator match = m_sourceMap.find(_funcOid);
if (match == m_sourceMap.end())
return false;
else
{
if (_cached)
{
*_cached = match->second;
}
return true;
}
}
void dbgModel::ClearCachedSource()
{
m_sourceMap.clear();
// Put a dummy entry for invalid function OID to the cache. This is
// displayed at least for inline code blocks, as we currently have no way
// to fetch the source for those
m_sourceMap[wxT("0")] = dbgCachedStack(wxT("0"), wxT("0"), wxT(""), wxT(""), _("<source not available>"));
}
void dbgModel::AddSource(const wxString &_funcOid, const dbgCachedStack &_source)
{
m_sourceMap[_funcOid] = _source;
}

647
debugger/dbgTargetInfo.cpp Normal file
View file

@ -0,0 +1,647 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// dbgTargetInfo.cpp - debugger
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
#include "utils/pgDefs.h"
// wxWindows headers
#include <wx/wx.h>
// App headers
#include "debugger/dbgTargetInfo.h"
#include "debugger/dbgConst.h"
#include "utils/misc.h"
#include "utils/pgfeatures.h"
#include <stdexcept>
////////////////////////////////////////////////////////////////////////////////
// dbgTargetInfo constructor
//
// This class implements a container that holds information necessary to invoke
// a debugger target (a function or procedure).
//
// When the constructor is called, it sends a query to the server to retreive:
// the OID of the target,
// the name of the target,
// the name of the schema in which the target is defined
// the name of the language in which the target is defined
// the number of arguments expected by the target
// the argument names
// the argument types
// the argument modes (IN, OUT, or INOUT)
// the target type (function or procedure)
//
// This class offers a number of (inline) member functions that you can call
// to extract the above information after it's been queried from the server.
dbgTargetInfo::dbgTargetInfo(Oid _target, pgConn *_conn)
: m_args(NULL), m_inputParamCnt(0), m_hasVariadic(false)
{
wxMBConv *conv = _conn->GetConv();
wxString targetQuery =
wxT("SELECT\n")
wxT(" p.proname AS name, l.lanname, p.proretset, p.prorettype, y.typname AS rettype,\n")
wxT(" CASE WHEN proallargtypes IS NOT NULL THEN\n")
wxT(" pg_catalog.array_to_string(ARRAY(\n")
wxT(" SELECT\n")
wxT(" pg_catalog.format_type(p.proallargtypes[s.i], NULL)\n")
wxT(" FROM\n")
wxT(" pg_catalog.generate_series(0, pg_catalog.array_upper(\n")
wxT(" p.proallargtypes, 1)) AS s(i)), ',')\n")
wxT(" ELSE\n")
wxT(" pg_catalog.array_to_string(ARRAY(\n")
wxT(" SELECT\n")
wxT(" pg_catalog.format_type(p.proargtypes[s.i], NULL)\n")
wxT(" FROM\n")
wxT(" pg_catalog.generate_series(0, pg_catalog.array_upper(\n")
wxT(" p.proargtypes, 1)) AS s(i)), ',')\n")
wxT(" END AS proargtypenames,\n")
wxT(" CASE WHEN proallargtypes IS NOT NULL THEN\n")
wxT(" pg_catalog.array_to_string(ARRAY(\n")
wxT(" SELECT proallargtypes[s.i] FROM\n")
wxT(" pg_catalog.generate_series(0, pg_catalog.array_upper(proallargtypes, 1)) s(i)), ',')\n")
wxT(" ELSE\n")
wxT(" pg_catalog.array_to_string(ARRAY(\n")
wxT(" SELECT proargtypes[s.i] FROM\n")
wxT(" pg_catalog.generate_series(0, pg_catalog.array_upper(proargtypes, 1)) s(i)), ',')\n")
wxT(" END AS proargtypes,\n")
wxT(" pg_catalog.array_to_string(p.proargnames, ',') AS proargnames,\n")
wxT(" pg_catalog.array_to_string(proargmodes, ',') AS proargmodes,\n");
if (_conn->GetIsEdb())
{
targetQuery +=
wxT(" CASE WHEN n.nspparent <> 0 THEN n.oid ELSE 0 END AS pkg,\n")
wxT(" CASE WHEN n.nspparent <> 0 THEN n.nspname ELSE '' END AS pkgname,\n")
wxT(" CASE WHEN n.nspparent <> 0 THEN (SELECT oid FROM pg_proc WHERE pronamespace=n.oid AND proname='cons') ELSE 0 END AS pkgconsoid,\n")
wxT(" CASE WHEN n.nspparent <> 0 THEN g.oid ELSE n.oid END AS schema,\n")
wxT(" CASE WHEN n.nspparent <> 0 THEN g.nspname ELSE n.nspname END AS schemaname,\n")
wxT(" NOT (l.lanname = 'edbspl' AND protype = '1') AS isfunc,\n");
}
else
{
targetQuery +=
wxT(" 0 AS pkg,\n")
wxT(" '' AS pkgname,\n")
wxT(" 0 AS pkgconsoid,\n")
wxT(" n.oid AS schema,\n")
wxT(" n.nspname AS schemaname,\n")
wxT(" true AS isfunc,\n");
}
if (_conn->BackendMinimumVersion(8, 4))
{
targetQuery += wxT(" pg_catalog.pg_get_function_identity_arguments(p.oid) AS signature,");
}
else if (_conn->BackendMinimumVersion(8, 1))
{
targetQuery +=
wxT(" CASE\n")
wxT(" WHEN proallargtypes IS NOT NULL THEN pg_catalog.array_to_string(ARRAY(\n")
wxT(" SELECT\n")
wxT(" CASE\n")
wxT(" WHEN p.proargmodes[s.i] = 'i' THEN ''\n")
wxT(" WHEN p.proargmodes[s.i] = 'o' THEN 'OUT '\n")
wxT(" WHEN p.proargmodes[s.i] = 'b' THEN 'INOUT '\n")
wxT(" WHEN p.proargmodes[s.i] = 'v' THEN 'VARIADIC '\n")
wxT(" END ||\n")
wxT(" CASE WHEN COALESCE(p.proargnames[s.i], '') = '' THEN ''\n")
wxT(" ELSE p.proargnames[s.i] || ' '\n")
wxT(" END ||\n")
wxT(" pg_catalog.format_type(p.proallargtypes[s.i], NULL)\n")
wxT(" FROM\n")
wxT(" pg_catalog.generate_series(1, pg_catalog.array_upper(p.proallargtypes, 1)) AS s(i)\n")
wxT(" WHERE p.proargmodes[s.i] != 't'\n")
wxT(" ), ', ')\n")
wxT(" ELSE\n")
wxT(" pg_catalog.array_to_string(ARRAY(\n")
wxT(" SELECT\n")
wxT(" CASE\n")
wxT(" WHEN COALESCE(p.proargnames[s.i+1], '') = '' THEN ''\n")
wxT(" ELSE p.proargnames[s.i+1] || ' '\n")
wxT(" END ||\n")
wxT(" pg_catalog.format_type(p.proargtypes[s.i], NULL)\n")
wxT(" FROM\n")
wxT(" pg_catalog.generate_series(1, pg_catalog.array_upper(p.proargtypes, 1)) AS s(i)\n")
wxT(" ), ', ')\n")
wxT(" END AS signature,\n");
}
else
{
targetQuery += wxT(" '' AS signature,");
}
if (_conn->HasFeature(FEATURE_FUNCTION_DEFAULTS))
{
// EnterpriseDB 8.3R2
if(!_conn->BackendMinimumVersion(8, 4))
{
targetQuery +=
wxT(" pg_catalog.array_to_string(ARRAY(\n")
wxT(" SELECT\n")
wxT(" CASE WHEN p.proargdefvals[x.j] != '-' THEN\n")
wxT(" pg_catalog.pg_get_expr(p.proargdefvals[x.j], 'pg_catalog.pg_class'::regclass, true)\n")
wxT(" ELSE '-' END\n")
wxT(" FROM\n")
wxT(" pg_catalog.generate_series(1, pg_catalog.array_upper(p.proargdefvals, 1)) AS x(j)\n")
wxT(" ), ',') AS proargdefaults,\n")
wxT(" CASE WHEN p.proargdefvals IS NULL THEN '0'\n")
wxT(" ELSE pg_catalog.array_upper(p.proargdefvals, 1)::text END AS pronargdefaults\n");
}
else
{
targetQuery +=
wxT(" pg_catalog.pg_get_expr(p.proargdefaults, 'pg_catalog.pg_class'::regclass, false) AS proargdefaults,\n")
wxT(" p.pronargdefaults\n");
}
}
else
{
targetQuery +=
wxT(" '' AS proargdefaults, 0 AS pronargdefaults\n");
}
targetQuery +=
wxT("FROM\n")
wxT(" pg_catalog.pg_proc p\n")
wxT(" LEFT JOIN pg_catalog.pg_namespace n ON p.pronamespace = n.oid\n")
wxT(" LEFT JOIN pg_catalog.pg_language l ON p.prolang = l.oid\n")
wxT(" LEFT JOIN pg_catalog.pg_type y ON p.prorettype = y.oid\n");
if(_conn->GetIsEdb())
{
targetQuery +=
wxT(" LEFT JOIN pg_catalog.pg_namespace g ON n.nspparent = g.oid\n");
}
targetQuery +=
wxString::Format(wxT("WHERE p.oid = %ld"), (long)_target);
pgSet *set = _conn->ExecuteSet(targetQuery);
if (conv == NULL)
{
conv = &wxConvLibc;
}
if (!set || _conn->GetLastResultStatus() != PGRES_TUPLES_OK)
{
if (set)
delete set;
wxLogError(_("Could not fetch information about the debugger target.\n") +
_conn->GetLastError());
throw (std::runtime_error(
(const char *)(_conn->GetLastError().c_str())));
}
if (set->NumRows() == 0)
{
delete set;
wxLogError(_("Can't find the debugging target"));
throw (std::runtime_error("Can't find target!"));
}
m_oid = _target;
m_name = set->GetVal(wxT("name"));
m_schema = set->GetVal(wxT("schemaname"));
m_package = set->GetVal(wxT("pkgname"));
m_language = set->GetVal(wxT("lanname"));
m_returnType = set->GetVal(wxT("rettype"));
m_funcSignature = set->GetVal(wxT("signature"));
m_isFunction = set->GetBool(wxT("isfunc"));
m_returnsSet = set->GetBool(wxT("proretset"));
m_pkgOid = set->GetOid(wxT("pkg"));
m_pkgInitOid = set->GetOid(wxT("pkgconsoid"));
m_schemaOid = set->GetOid(wxT("schema"));
m_fqName = qtIdent(m_schema) + wxT(".") +
(m_pkgOid == 0 ? wxT("") : (qtIdent(m_package) + wxT("."))) + qtIdent(m_name);
wxArrayString argModes, argNames, argTypes, argTypeOids, argDefVals,
argBaseTypes;
// Fetch Argument Modes (if available)
if (!set->IsNull(set->ColNumber(wxT("proargmodes"))))
{
wxString tmp;
tmp = set->GetVal(wxT("proargmodes"));
if (!tmp.IsEmpty())
getArrayFromCommaSeparatedList(tmp, argModes);
}
// Fetch Argument Names (if available)
if (!set->IsNull(set->ColNumber(wxT("proargnames"))))
{
wxString tmp;
tmp = set->GetVal(wxT("proargnames"));
if (!tmp.IsEmpty())
getArrayFromCommaSeparatedList(tmp, argNames);
}
// Fetch Argument Type-Names (if available)
if (!set->IsNull(set->ColNumber(wxT("proargtypenames"))))
{
wxString tmp;
tmp = set->GetVal(wxT("proargtypenames"));
if (!tmp.IsEmpty())
getArrayFromCommaSeparatedList(tmp, argTypes);
}
// Fetch Argument Type-Names (if available)
if (!set->IsNull(set->ColNumber(wxT("proargtypes"))))
{
wxString tmp;
tmp = set->GetVal(wxT("proargtypes"));
if (!tmp.IsEmpty())
getArrayFromCommaSeparatedList(tmp, argTypeOids);
}
size_t nArgDefs = (size_t)set->GetLong(wxT("pronargdefaults"));
// Fetch Argument Default Values (if available)
if (!set->IsNull(set->ColNumber(wxT("proargdefaults"))) && nArgDefs != 0)
{
wxString tmp;
tmp = set->GetVal(wxT("proargdefaults"));
if (!tmp.IsEmpty())
getArrayFromCommaSeparatedList(tmp, argDefVals);
}
wxString argName, argDefVal;
short argMode;
Oid argTypeOid;
size_t argCnt = argTypes.Count();
// This function/procedure does not take any arguments
if (argCnt == 0)
{
return;
}
size_t idx = 0;
m_args = new pgDbgArgs();
for (; idx < argCnt; idx++)
{
argTypeOid = (Oid)strtoul(argTypeOids[idx].mb_str(wxConvUTF8), 0, 10);
argDefVal = wxEmptyString;
argName = wxEmptyString;
if (idx < argNames.Count())
argName = argNames[idx];
if (argName.IsEmpty())
argName.Printf( wxT( "dbgParam%d" ), (idx + 1));
if (idx < argModes.Count())
{
wxString tmp = argModes[idx];
switch ((char)(tmp.c_str())[0])
{
case 'i':
argMode = pgParam::PG_PARAM_IN;
m_inputParamCnt++;
break;
case 'b':
m_inputParamCnt++;
argMode = pgParam::PG_PARAM_INOUT;
break;
case 'o':
argMode = pgParam::PG_PARAM_OUT;
break;
case 'v':
m_inputParamCnt++;
argMode = pgParam::PG_PARAM_VARIADIC;
m_hasVariadic = true;
break;
case 't':
continue;
default:
m_inputParamCnt++;
argMode = pgParam::PG_PARAM_IN;
break;
}
}
else
{
m_inputParamCnt++;
argMode = pgParam::PG_PARAM_IN;
}
// In EDBAS 90, if an SPL-function has both an OUT-parameter
// and a return value (which is not possible on PostgreSQL otherwise),
// the return value is transformed into an extra OUT-parameter
// named "_retval_"
if (argName == wxT("_retval_") && _conn->EdbMinimumVersion(9, 0))
{
// this will be the return type for this object
m_returnType = argTypes[idx];
continue;
}
m_args->Add(new dbgArgInfo(argName, argTypes[idx], argTypeOid, argMode));
}
if (m_args->GetCount() == 0)
{
delete m_args;
m_args = NULL;
return;
}
if (nArgDefs != 0)
{
argCnt = m_args->GetCount();
// Set the default as the value for the argument
for (idx = argCnt - 1;; idx--)
{
dbgArgInfo *arg = (dbgArgInfo *)((*m_args)[idx]);
if (arg->GetMode() == pgParam::PG_PARAM_INOUT ||
arg->GetMode() == pgParam::PG_PARAM_IN)
{
nArgDefs--;
if (argDefVals[nArgDefs] != wxT("-"))
{
arg->SetDefault(argDefVals[nArgDefs]);
}
if (nArgDefs == 0)
{
break;
}
}
if (idx == 0)
break;
}
}
}
////////////////////////////////////////////////////////////////////////////////
// operator[]
//
// This operator function makes it easy to index into the m_args array
// using concise syntax.
dbgArgInfo *dbgTargetInfo::operator[](int index)
{
if (m_args == NULL)
return (dbgArgInfo *)NULL;
if (index < 0 || index >= (int)m_args->GetCount())
return (dbgArgInfo *)NULL;
return (dbgArgInfo *)((*m_args)[index]);
}
dbgArgInfo::dbgArgInfo(const wxString &_name, const wxString &_type, Oid _typeOid,
short _mode)
: m_name(_name), m_type(_type), m_typeOid(_typeOid), m_mode(_mode),
m_hasDefault(false), m_useDefault(false), m_null(false)
{
if (!_type.EndsWith(wxT("[]"), &m_baseType))
{
m_baseType = wxEmptyString;
}
}
pgParam *dbgArgInfo::GetParam(wxMBConv *_conv)
{
return new pgParam(m_typeOid,
(m_null ? (wxString *)NULL : &m_val),
_conv, m_mode);
}
bool dbgTargetInfo::AddForExecution(pgQueryThread *_thread)
{
wxASSERT(_thread != NULL);
if (_thread == NULL)
return false;
pgConn *conn = _thread->GetConn();
pgParamsArray *params = NULL;
wxString strQuery;
bool useCallable = false;
// Basically - we can call the function/target three ways:
// 1. If it is a edbspl procedure, we can use callable statement
// 2. If the database server is of type EnterpriseDB, and
// function/procedure is type 'edbspl', we should use the anonymous
// function block for:
// a. Version < 9.0
// b. Package function/procedure
// 3. Otherwise, we should use the simple function call (except using EXEC
// for the procedure in 'edbspl' instead of using SELECT)
if (_thread->SupportCallableStatement() &&
m_language == wxT("edbspl") &&
!m_isFunction)
{
useCallable = true;
strQuery = wxT("CALL ") + m_fqName + wxT("(");
if (m_args)
{
params = new pgParamsArray();
wxMBConv *conv = conn->GetConv();
for(int idx = 0; idx < (int)m_args->GetCount(); idx++)
{
params->Add(((*m_args)[idx])->GetParam(conv));
if (idx != 0)
strQuery += wxT(", ");
strQuery += wxString::Format(wxT("$%d::"), idx + 1) +
((*m_args)[idx])->GetTypeName();
}
}
strQuery += wxT(")");
}
else if (conn->GetIsEdb() && !conn->BackendMinimumVersion(9, 3))
{
wxString strDeclare, strStatement, strResult;
bool useAnonymousBlock = false;
if (m_language == wxT("edbspl"))
{
useAnonymousBlock = true;
if (!m_isFunction)
{
strStatement = wxT("\tEXEC ") + m_fqName;
}
else if (m_returnType == wxT("void") || m_returnsSet ||
!conn->BackendMinimumVersion(8, 4))
{
strStatement = wxT("\tPERFORM ") + m_fqName;
}
else
{
wxString resultVar = wxT("v_retVal");
strStatement = wxT("\t") + resultVar + wxT(" := ") + m_fqName;
strDeclare.Append(wxT("\t"))
.Append(resultVar)
.Append(wxT(" "))
.Append(m_returnType)
.Append(wxT(";\n"));
strResult = wxT("\tDBMS_OUTPUT.PUT_LINE(E'\\n\\nResult:\\n--------\\n' || ") +
resultVar +
wxT("::text || E'\\n\\nNOTE: This is the result generated during the function execution by the debugger.\\n');\n");
}
}
else
{
if (m_returnType == wxT("record"))
{
strStatement = wxT("\tSELECT ") + m_fqName;
}
else
{
strStatement = wxT("\tSELECT * FROM ") + m_fqName;
}
}
if (m_args && m_args->GetCount() > 0)
{
strStatement.Append(wxT("("));
for(int idx = 0, firstProcessed = false; idx < (int)m_args->GetCount(); idx++)
{
dbgArgInfo *arg = (*m_args)[idx];
if (!conn->EdbMinimumVersion(8, 4) &&
arg->GetMode() == pgParam::PG_PARAM_OUT &&
(!m_isFunction || m_language == wxT("edbspl")))
{
if (firstProcessed)
strStatement.Append(wxT(", "));
firstProcessed = true;
strStatement.Append(wxT("NULL::")).Append(arg->GetTypeName());
}
else if (conn->EdbMinimumVersion(8, 4) && useAnonymousBlock &&
(arg->GetMode() == pgParam::PG_PARAM_OUT ||
arg->GetMode() == pgParam::PG_PARAM_INOUT))
{
wxString strParam = wxString::Format(wxT("p_param%d"), idx);
strDeclare.Append(wxT("\t"))
.Append(strParam)
.Append(wxT(" "))
.Append(arg->GetTypeName());
if (arg->GetMode() == pgParam::PG_PARAM_INOUT)
{
strDeclare.Append(wxT(" := "))
.Append(arg->Null() ? wxT("NULL") : conn->qtDbString(arg->Value()))
.Append(wxT("::"))
.Append(arg->GetTypeName());
}
strDeclare.Append(wxT(";\n"));
if (firstProcessed)
strStatement.Append(wxT(", "));
firstProcessed = true;
strStatement.Append(strParam);
}
else if (arg->GetMode() != pgParam::PG_PARAM_OUT)
{
if (firstProcessed)
strStatement.Append(wxT(", "));
firstProcessed = true;
if (arg->GetMode() == pgParam::PG_PARAM_VARIADIC)
strStatement += wxT("VARIADIC ");
strStatement
.Append(arg->Null() ? wxT("NULL") : conn->qtDbString(arg->Value()))
.Append(wxT("::"))
.Append(arg->GetTypeName());
}
}
strStatement.Append(wxT(")"));
strQuery = strStatement;
}
else if (!m_isFunction && m_language == wxT("edbspl"))
{
strQuery = strStatement;
}
else
{
strQuery = strStatement.Append(wxT("()"));
}
if (useAnonymousBlock)
{
strQuery = wxT("DECLARE\n") +
strDeclare + wxT("BEGIN\n") + strStatement + wxT(";\n") + strResult + wxT("END;");
}
}
else
{
if (!m_isFunction)
{
strQuery = wxT("EXEC ") + m_fqName + wxT("(");
}
else if (m_returnType == wxT("record"))
{
strQuery = wxT("SELECT ") + m_fqName + wxT("(");
}
else
{
strQuery = wxT("SELECT * FROM ") + m_fqName + wxT("(");
}
if (m_args)
{
params = new pgParamsArray();
wxMBConv *conv = conn->GetConv();
unsigned int noInParams = 0;
for(int idx = 0; idx < (int)m_args->GetCount(); idx++)
{
dbgArgInfo *arg = (*m_args)[idx];
if (arg->GetMode() != pgParam::PG_PARAM_OUT)
{
params->Add(arg->GetParam(conv));
if (noInParams != 0)
strQuery += wxT(", ");
if (arg->GetMode() == pgParam::PG_PARAM_VARIADIC)
strQuery += wxT("VARIADIC ");
noInParams++;
strQuery += wxString::Format(wxT("$%d::"), noInParams) +
((*m_args)[idx])->GetTypeName();
}
}
/*
* The function may not have IN/IN OUT/VARIADIC arguments, but only
* OUT one(s).
*/
if (params->GetCount() == 0)
{
delete params;
params = NULL;
}
}
strQuery += wxT(")");
}
_thread->AddQuery(strQuery, params, RESULT_ID_DIRECT_TARGET_COMPLETE, NULL, useCallable);
return true;
}

273
debugger/debugger.cpp Normal file
View file

@ -0,0 +1,273 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// debugger.cpp - Debugger factories
//
//////////////////////////////////////////////////////////////////////////
// wxWindows headers
#include <wx/wx.h>
// App headers
#include "pgAdmin3.h"
#include "frm/frmMain.h"
#include "debugger/debugger.h"
#include "debugger/dbgController.h"
#include "schema/pgFunction.h"
#include "schema/pgTrigger.h"
#include "schema/edbPackageFunction.h"
#include <stdexcept>
///////////////////////////////////////////////////
// Debugger factory
///////////////////////////////////////////////////
debuggerFactory::debuggerFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar) : actionFactory(list)
{
mnu->Append(id, _("&Debug"), _("Debug the selected object"));
}
wxWindow *debuggerFactory::StartDialog(frmMain *form, pgObject *obj)
{
// Check here to make sure the function still exists before proceeding.
// There is still a very small window in which it might be dropped, but
// that will be handled by a cache lookup failure error in the database
// We also make sure the function name doesn't contain a : as that will
// sent the debugger API nuts.
pgSet *res = obj->GetConnection()->ExecuteSet(wxT("SELECT proname FROM pg_proc WHERE oid = ") + NumToStr((long)obj->GetOid()));
if (res->NumRows() != 1)
{
wxLogError(_("The selected function could not be found."));
ctlTree *browser = form->GetBrowser();
wxTreeItemId item = browser->GetSelection();
if (obj == browser->GetObject(item))
{
form->Refresh(obj);
}
delete res;
return (wxWindow *)NULL;
}
if (res->GetVal(wxT("proname")).Contains(wxT(":")))
{
wxLogError(_("Functions with a colon in the name cannot be debugged."));
delete res;
return (wxWindow *)NULL;
}
try
{
new dbgController(form, obj, true);
}
catch (const std::runtime_error &)
{
// just ignore this errors, we already logged them in native messages to
// the end-user
}
delete res;
return (wxWindow *)NULL;
}
bool debuggerFactory::CheckEnable(pgObject *obj)
{
if (!obj)
return false;
// Can't debug catalog objects.
if (obj->GetSchema() && obj->GetSchema()->GetMetaType() == PGM_CATALOG)
return false;
// Must be a super user or object owner to create breakpoints of any kind.
if (!obj->GetServer() || !(obj->GetServer()->GetSuperUser() || obj->GetServer()->GetUsername() == obj->GetOwner()))
return false;
// EnterpriseDB 8.3 or earlier does not support debugging by the non-superuser
if (!obj->GetServer()->GetSuperUser() && !obj->GetConnection()->EdbMinimumVersion(8, 4) && obj->GetConnection()->GetIsEdb())
return false;
if (!obj->IsCollection())
{
switch (obj->GetMetaType())
{
case PGM_FUNCTION:
{
pgFunction *func = (pgFunction *)obj;
// If this is an EDB wrapped function, no debugging allowed
if (obj->GetConnection()->GetIsEdb() && func->GetSource().Trim(false).StartsWith(wxT("$__EDBwrapped__$")))
return false;
if (func->GetReturnType() != wxT("trigger") &&
func->GetReturnType() != wxT("\"trigger\""))
{
if (func->GetLanguage() == wxT("plpgsql") &&
obj->GetDatabase()->CanDebugPlpgsql())
return true;
else if (func->GetLanguage() == wxT("edbspl") &&
obj->GetDatabase()->CanDebugEdbspl())
return true;
else
return false;
}
else
return false;
}
break;
case EDB_PACKAGEFUNCTION:
if (obj->GetDatabase()->GetConnection()->EdbMinimumVersion(8, 2) &&
obj->GetDatabase()->CanDebugEdbspl() &&
obj->GetName() != wxT("cons") &&
((edbPackageFunction *)obj)->GetSource() != wxEmptyString &&
(!((edbPackageFunction *)obj)->GetSource().Trim(false).StartsWith(wxT("$__EDBwrapped__$"))))
return true;
break;
default:
break;
}
}
return false;
}
///////////////////////////////////////////////////
// Breakpoint factory
///////////////////////////////////////////////////
breakpointFactory::breakpointFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar) : actionFactory(list)
{
mnu->Append(id, _("&Set breakpoint"), _("Set a breakpoint on the selected object"));
}
wxWindow *breakpointFactory::StartDialog(frmMain *form, pgObject *obj)
{
wxString dbgOid;
if (obj->GetMetaType() == PGM_TRIGGER)
dbgOid = NumToStr((long)((pgTrigger *)obj)->GetFunctionOid());
else
dbgOid = NumToStr((long)obj->GetOid());
// Check here to make sure the function still exists before proceeding.
// There is still a very small window in which it might be dropped, but
// we should be able to handle most cases here without having to do this
// deep down in query threads.
// We also make sure the function name doesn't contain a : as that will
// sent the debugger API nuts.
pgSet *res = obj->GetConnection()->ExecuteSet(
wxT("SELECT count(*) AS count, proname FROM pg_proc WHERE oid = ") + dbgOid + wxT(" GROUP BY proname"));
if (res->GetVal(wxT("proname")).Contains(wxT(":")))
{
wxLogError(_("Functions with a colon in the name cannot be debugged."));
delete res;
return (wxWindow *)NULL;
}
if (res->GetLong(wxT("count")) != 1)
{
wxLogError(_("The selected function could not be found."));
ctlTree *browser = form->GetBrowser();
wxTreeItemId item = browser->GetSelection();
if (obj == browser->GetObject(item))
{
pgCollection *coll = browser->GetParentCollection(obj->GetId());
browser->DeleteChildren(coll->GetId());
coll->ShowTreeDetail(browser);
}
delete res;
return (wxWindow *)NULL;
}
try
{
new dbgController(form, obj, false);
}
catch (const std::runtime_error &)
{
// just ignore this errors, we already logged them in native messages to
// the end-user
}
delete res;
return (wxWindow *)NULL;
}
bool breakpointFactory::CheckEnable(pgObject *obj)
{
if (!obj)
return false;
// Can't debug catalog objects.
if (obj->GetSchema() && obj->GetSchema()->GetMetaType() == PGM_CATALOG)
return false;
// Must be a super user to create breakpoints of any kind.
if (!obj->GetServer() || !obj->GetServer()->GetSuperUser())
return false;
if (!obj->IsCollection())
{
switch (obj->GetMetaType())
{
case PGM_FUNCTION:
{
pgFunction *func = (pgFunction *)obj;
// If this is an EDB wrapped function, no debugging allowed
if (obj->GetConnection()->GetIsEdb() &&
func->GetSource().Trim(false).StartsWith(wxT("$__EDBwrapped__$")))
return false;
if (func->GetLanguage() == wxT("plpgsql") &&
obj->GetDatabase()->CanDebugPlpgsql())
return true;
else if (func->GetLanguage() == wxT("edbspl") &&
obj->GetDatabase()->CanDebugEdbspl())
return true;
else
return false;
}
break;
case EDB_PACKAGEFUNCTION:
if (obj->GetDatabase()->GetConnection()->EdbMinimumVersion(8, 2) &&
obj->GetDatabase()->CanDebugEdbspl() &&
obj->GetName() != wxT("cons") &&
((edbPackageFunction *)obj)->GetSource() != wxEmptyString &&
(!((edbPackageFunction *)obj)->GetSource().Trim(false).StartsWith(wxT("$__EDBwrapped__$"))))
return true;
break;
case PGM_TRIGGER:
{
pgTrigger *trig = (pgTrigger *)obj;
// If this is an EDB wrapped function, no debugging allowed
if (obj->GetConnection()->GetIsEdb() &&
trig->GetSource().Trim(false).StartsWith(wxT("$__EDBwrapped__$")))
return false;
if (trig->GetLanguage() == wxT("plpgsql") &&
obj->GetDatabase()->CanDebugPlpgsql())
return true;
else if (trig->GetLanguage() == wxT("edbspl") &&
obj->GetDatabase()->CanDebugEdbspl())
return true;
else
return false;
}
break;
default:
break;
}
}
return false;
}

Some files were not shown because too many files have changed in this diff Show more