mirror of
https://github.com/levinsv/pgadmin3.git
synced 2026-05-15 14:15:49 -06:00
462 lines
13 KiB
C++
462 lines
13 KiB
C++
//////////////////////////////////////////////////////////////////////////
|
||
//
|
||
// pgAdmin III - PostgreSQL Tools
|
||
//
|
||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||
// This software is released under the PostgreSQL Licence
|
||
//
|
||
// pgproJob.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 "pro_scheduler/pgproJob.h"
|
||
|
||
pgproJob::pgproJob(const wxString &newName)
|
||
: pgServerObject(projobFactory, newName)
|
||
{
|
||
}
|
||
|
||
wxString pgproJob::GetTranslatedMessage(int kindOfMessage) const
|
||
{
|
||
wxString message = wxEmptyString;
|
||
|
||
switch (kindOfMessage)
|
||
{
|
||
case RETRIEVINGDETAILS:
|
||
message = _("Retrieving details on pro_scheduler job");
|
||
break;
|
||
case REFRESHINGDETAILS:
|
||
message = _("Refreshing pro_scheduler job");
|
||
break;
|
||
case PROPERTIESREPORT:
|
||
message = _("pro_scheduler job properties report");
|
||
break;
|
||
case PROPERTIES:
|
||
message = _("pgAgent job properties");
|
||
break;
|
||
case DDLREPORT:
|
||
message = _("pro_scheduler job DDL report");
|
||
break;
|
||
case DEPENDENCIESREPORT:
|
||
message = _("pro_scheduler job dependencies report");
|
||
break;
|
||
case DEPENDENCIES:
|
||
message = _("pro_scheduler job dependencies");
|
||
break;
|
||
case DEPENDENTSREPORT:
|
||
message = _("pro_scheduler job dependents report");
|
||
break;
|
||
case DEPENDENTS:
|
||
message = _("pro_scheduler 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 pgproJob::GetIconId()
|
||
{
|
||
if (GetEnabled())
|
||
if (GetStatus()==wxT("working")) return projobFactory.GetRunId();
|
||
else
|
||
return projobFactory.GetIconId();
|
||
else
|
||
return projobFactory.GetDisabledId();
|
||
}
|
||
|
||
|
||
wxMenu *pgproJob::GetNewMenu()
|
||
{
|
||
wxMenu *menu = pgObject::GetNewMenu();
|
||
if (1) // check priv.
|
||
{
|
||
//stepFactory.AppendMenu(menu);
|
||
//scheduleFactory.AppendMenu(menu);
|
||
}
|
||
return menu;
|
||
}
|
||
wxString pgproJob::GetSql(ctlTree *browser)
|
||
{
|
||
if (sql.IsNull())
|
||
{
|
||
sql = wxT("-- pro_scheduler job: ") + GetName() + wxT("\n\n");
|
||
sql += wxT("-- SELECT schedule.set_job_attributes( ") + NumToStr(GetRecId()) + wxT(", { });\n\n");
|
||
sql += wxT("SELECT schedule.create_job(")+GetConnection()->qtDbString(GetRule())+wxT(");");
|
||
}
|
||
return sql;
|
||
}
|
||
|
||
|
||
bool pgproJob::DropObject(wxFrame *frame, ctlTree *browser, bool cascaded)
|
||
{
|
||
return GetConnection()->ExecuteVoid(wxT("select schedule.drop_job(") + NumToStr(GetRecId())+wxT(")"));
|
||
}
|
||
|
||
|
||
void pgproJob::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"), GetTryName());
|
||
properties->AppendItem(_("ID"), GetRecId());
|
||
properties->AppendYesNoItem(_("Enabled"), GetEnabled());
|
||
//properties->AppendItem(_("Host agent"), GetHostAgent());
|
||
// properties->AppendItem(_("Job class"), GetJobclass());
|
||
properties->AppendItem(_("Started"), GetStarted());
|
||
properties->AppendItem(_("Finished"), GetFinished());
|
||
properties->AppendItem(_("Result"), GetStatus());
|
||
properties->AppendItem(_("Crontab"), GetCrontab());
|
||
//properties->AppendItem(_("Last result"), GetLastresult());
|
||
properties->AppendItem(_("Runas"), GetRunAs());
|
||
properties->AppendItem(_("Message"), GetMessage());
|
||
properties->AppendItem(_("Comment"), firstLineOnly(GetComment()));
|
||
}
|
||
}
|
||
|
||
|
||
|
||
pgObject *pgproJob::Refresh(ctlTree *browser, const wxTreeItemId item)
|
||
{
|
||
pgObject *job = 0;
|
||
|
||
pgObject *obj = browser->GetObject(browser->GetItemParent(item));
|
||
if (obj && obj->IsCollection())
|
||
job = projobFactory.CreateObjects((pgCollection *)obj, 0, wxT("\n WHERE c.id=") + NumToStr(GetRecId()));
|
||
GetIconId();
|
||
UpdateIcon(browser);
|
||
return job;
|
||
}
|
||
|
||
|
||
|
||
pgObject *pgproJobFactory::CreateObjects(pgCollection *collection, ctlTree *browser, const wxString &restriction)
|
||
{
|
||
pgproJob *job = 0;
|
||
wxString sql= wxT("with last_job as (select cron,max(started) started from schedule.get_log() b group by cron), a as (select cron,scheduled_at,started,finished,status,message from schedule.get_active_jobs()), lastj as (select b.cron,")
|
||
wxT("coalesce((select scheduled_at from a where a.cron=b.cron),b.scheduled_at) scheduled_at")
|
||
wxT(",coalesce((select started from a where a.cron=b.cron),b.started) started")
|
||
wxT(",case when (select status from a where a.cron=b.cron)='working' then null else b.finished end finished")
|
||
wxT(",coalesce((select status from a where a.cron=b.cron),b.status) status")
|
||
wxT(",coalesce((select message from a where a.cron=b.cron),b.message) message")
|
||
wxT(" from schedule.get_log() b,last_job where b.cron=last_job.cron and b.started=last_job.started")
|
||
wxT(") select j.crontab cron,* from (select c.*,null stop,l.* from schedule.get_cron() c left join lastj l on c.id=l.cron) c")
|
||
wxT(" join lateral (select crontab from jsonb_to_record(c.rule) as (d int[],h int[],wd int[],m int[],crontab text,mi int[]) ) j on true")
|
||
wxT("")
|
||
wxT("")
|
||
wxT(" ") + restriction;
|
||
|
||
if (!collection->GetConnection()->IsSuperuser()) {
|
||
size_t a=sql.Replace(wxT("get_log()"),wxT("get_user_log()"));
|
||
a=sql.Replace(wxT("get_cron()"),wxT("get_user_cron()"));
|
||
}
|
||
|
||
pgSet *jobs = collection->GetConnection()->ExecuteSet(sql);
|
||
|
||
|
||
if (jobs)
|
||
{
|
||
int nCols = jobs->NumCols();
|
||
while (!jobs->Eof())
|
||
{
|
||
wxString status;
|
||
status=jobs->GetVal(wxT("status"));
|
||
if (status.IsNull()) status = _("Not run");
|
||
|
||
wxString name=jobs->GetVal(wxT("name"))+wxT("(")+jobs->GetVal(wxT("id"))+wxT(")");
|
||
job = new pgproJob(name);
|
||
job->iSetStatus(status);
|
||
job->iSetTryName(jobs->GetVal(wxT("name")));
|
||
job->iSetServer(collection->GetServer());
|
||
job->iSetRecId(jobs->GetLong(wxT("id")));
|
||
job->iSetComment(jobs->GetVal(wxT("comments")));
|
||
|
||
job->iSetEnabled(jobs->GetBool(wxT("active")));
|
||
|
||
wxString tmp;
|
||
tmp = jobs->GetVal(wxT("commands"));
|
||
if (tmp.find('{',0)==0) { tmp=wxT("[")+tmp.Mid(1,tmp.Len()-2)+wxT("]"); }
|
||
//tmp.Replace(wxT("{"), wxT("["),false);
|
||
//tmp.Replace(wxT("}"), wxT("]"));
|
||
|
||
|
||
job->iSetCommands(tmp);
|
||
job->iSetRunAs(jobs->GetVal(wxT("run_as")));
|
||
job->iSetStarted(jobs->GetDateTime(wxT("started")));
|
||
|
||
job->iSetFinished(jobs->GetDateTime(wxT("finished")));
|
||
job->iSetCrontab(jobs->GetVal(wxT("crontab")));
|
||
//job->iSetNextrun(jobs->GetDateTime(wxT("jobnextrun")));
|
||
|
||
|
||
job->iSetMessage(jobs->GetVal(wxT("message")));
|
||
tmp=wxT("");
|
||
int columnCount=0;
|
||
for (int col = 0 ; col < nCols ; col++)
|
||
{
|
||
wxString colname=jobs->ColName(col);
|
||
wxString vl=jobs->GetVal(col);
|
||
if (vl.IsEmpty()) continue;
|
||
if (colname==wxT("id")
|
||
||colname==wxT("node")
|
||
||colname==wxT("rule")
|
||
||colname==wxT("owner")) continue;
|
||
|
||
if (colname==wxT("broken")) break;
|
||
if (columnCount)
|
||
{
|
||
tmp += wxT(", ");
|
||
}
|
||
if (colname==wxT("commands")) vl=vl.Mid(1,vl.Length()-2);
|
||
else vl=wxT("\"")+vl+wxT("\"");
|
||
|
||
tmp += wxT("\"")+colname+wxT("\"")+wxT(": ")+vl;
|
||
columnCount++;
|
||
}
|
||
job->iSetRule(wxT("{")+tmp+wxT("}"));
|
||
if (browser)
|
||
{
|
||
browser->AppendObject(collection, job);
|
||
jobs->MoveNext();
|
||
}
|
||
else
|
||
break;
|
||
}
|
||
|
||
delete jobs;
|
||
}
|
||
return job;
|
||
}
|
||
|
||
void pgproJob::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());
|
||
wxString bu=GetConnection()->ExecuteScalar(wxT("select coalesce((select pg_table_is_visible(oid) from pg_class where relname='pg_log'),false)"));
|
||
// Add the statistics view columns
|
||
statistics->ClearAll();
|
||
statistics->AddColumn(_("Log_time"), 95);
|
||
statistics->AddColumn(_("Critical"), 30);
|
||
statistics->AddColumn(_("Message"), 300);
|
||
statistics->AddColumn(_("Stage"), 40);
|
||
if ((bu==wxT("f"))||(!settings->GetASUTPstyle())||(DateToAnsiStr(GetStarted()).IsEmpty())) return ;
|
||
|
||
wxString wxDTend = DateToAnsiStr(GetFinished());
|
||
if (wxDTend.IsEmpty()) wxDTend=DateToAnsiStr(wxDateTime::Now());
|
||
sql=wxT("select log_time,detail critical,message,application_name from pg_log l where l.log_time>'") + DateToAnsiStr(GetStarted())+
|
||
wxT("'::timestamp - interval '1min' and l.log_time<='")+ wxDTend +
|
||
wxT("'::timestamp + interval '1min' and hint='")+GetTryName()+wxT("'");
|
||
|
||
pgSet *stats = GetConnection()->ExecuteSet(sql);
|
||
wxString critical;
|
||
wxDateTime startTime;
|
||
wxDateTime endTime;
|
||
|
||
if (stats)
|
||
{
|
||
while (!stats->Eof())
|
||
{
|
||
startTime.ParseDateTime(stats->GetVal(0));
|
||
//endTime.ParseDateTime(stats->GetVal(4));
|
||
critical=stats->GetVal(1);
|
||
long pos = statistics->AppendItem(DateToStr(startTime), critical, stats->GetVal(2));
|
||
statistics->SetItem(pos, 3, stats->GetVal(3));
|
||
//statistics->SetItem(pos, 4, stats->GetVal(3));
|
||
|
||
stats->MoveNext();
|
||
}
|
||
delete stats;
|
||
}
|
||
}
|
||
}
|
||
|
||
bool pgproJob::RunNow()
|
||
{
|
||
//if (!GetConnection()->ExecuteVoid(wxT("select schedule.drop_job(") + NumToStr(GetRecId())+wxT(")"))) return false;
|
||
|
||
return true;
|
||
}
|
||
|
||
|
||
pgproJobCollection::pgproJobCollection(pgaFactory *factory, pgServer *sv)
|
||
: pgServerObjCollection(factory, sv)
|
||
{
|
||
}
|
||
|
||
|
||
wxString pgproJobCollection::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;
|
||
}
|
||
void pgproJob::SetEnabled(ctlTree *browser, const bool b)
|
||
{
|
||
if (GetRecId() > 0 && ((enabled && !b) || (!enabled && b)))
|
||
{
|
||
wxString sql = wxT("select schedule.");
|
||
if (enabled && !b)
|
||
sql += wxT("deactivate_job(");
|
||
else if (!enabled && b)
|
||
sql += wxT("activate_job(");
|
||
|
||
sql += NumToStr(GetRecId())+wxT(")");
|
||
GetConnection()->ExecuteVoid(sql);
|
||
}
|
||
enabled = b;
|
||
UpdateIcon(browser);
|
||
}
|
||
|
||
dlgProperty *pgproJobFactory::CreateDialog(frmMain *frame, pgObject *node, pgObject *parent)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
|
||
/////////////////////////////
|
||
|
||
|
||
#include "images/job.pngc"
|
||
#include "images/jobs.pngc"
|
||
#include "images/jobdisabled.pngc"
|
||
#include "images/jobrun.pngc"
|
||
|
||
pgproJobFactory::pgproJobFactory()
|
||
: pgServerObjFactory(__("pgAgent Job"), __("New Job"), __("Create a new Job."), job_png_img)
|
||
{
|
||
metaType = PGM_PROJOB;
|
||
disabledId = addIcon(jobdisabled_png_img);
|
||
runId = addIcon(jobrun_png_img);
|
||
|
||
}
|
||
|
||
|
||
pgCollection *pgproJobFactory::CreateCollection(pgObject *obj)
|
||
{
|
||
return new pgproJobCollection(GetCollectionFactory(), (pgServer *)obj);
|
||
}
|
||
|
||
|
||
pgproJobFactory projobFactory;
|
||
static pgaCollectionFactory cf(&projobFactory, __("Jobs"), jobs_png_img);
|
||
|
||
prorunNowFactory::prorunNowFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar) : contextActionFactory(list)
|
||
{
|
||
mnu->Append(id, _("&Run now"), _("Reschedule the job to run now."));
|
||
}
|
||
|
||
|
||
wxWindow *prorunNowFactory::StartDialog(frmMain *form, pgObject *obj)
|
||
{
|
||
if (!((pgproJob *)(obj))->RunNow())
|
||
{
|
||
wxLogError(_("Failed to reschedule the job."));
|
||
}
|
||
|
||
form->Refresh(obj);
|
||
|
||
return 0;
|
||
}
|
||
|
||
|
||
bool prorunNowFactory::CheckEnable(pgObject *obj)
|
||
{
|
||
if (obj)
|
||
{
|
||
if (obj->GetMetaType() == PGM_PROJOB && !obj->IsCollection())
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
enabledisableJobFactory::enabledisableJobFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar) : contextActionFactory(list)
|
||
{
|
||
mnu->Append(id, _("Enabled?"), _("<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>."), wxITEM_CHECK);
|
||
}
|
||
|
||
wxWindow *enabledisableJobFactory::StartDialog(frmMain *form, pgObject *obj)
|
||
{
|
||
((pgproJob *)obj)->SetEnabled(form->GetBrowser(), !((pgproJob *)obj)->GetEnabled());
|
||
//((pgproJob *)obj)->SetDirty();
|
||
|
||
wxTreeItemId item = form->GetBrowser()->GetSelection();
|
||
if (obj == form->GetBrowser()->GetObject(item))
|
||
{
|
||
//form->GetBrowser()->DeleteChildren(item);
|
||
obj->ShowTreeDetail(form->GetBrowser(), 0, form->GetProperties());
|
||
//form->GetSqlPane()->SetReadOnly(false);
|
||
//form->GetSqlPane()->SetText(((pgproJob *)obj)->GetSql(form->GetBrowser()));
|
||
//form->GetSqlPane()->SetReadOnly(true);
|
||
}
|
||
form->GetMenuFactories()->CheckMenu(obj, form->GetMenuBar(), (ctlMenuToolbar *)form->GetToolBar());
|
||
|
||
return 0;
|
||
}
|
||
|
||
bool enabledisableJobFactory::CheckEnable(pgObject *obj)
|
||
{
|
||
return obj && obj->IsCreatedBy(projobFactory);
|
||
}
|
||
|
||
bool enabledisableJobFactory::CheckChecked(pgObject *obj)
|
||
{
|
||
return obj && obj->IsCreatedBy(projobFactory) && ((pgproJob *)obj)->GetEnabled();
|
||
}
|