////////////////////////////////////////////////////////////////////////// // // 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 // 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) : pgDatabaseObject(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"), GetName()); 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(_("Owner"), GetOwner()); properties->AppendItem(_("Runas"), GetRunAs()); properties->AppendItem(_("Message"), GetMessage()); properties->AppendItem(_("Comment"), firstLineOnly(GetComment())); //wxDateTime dt= GetNextSchedule_At(GetStarted(),-1); wxDateTime t = wxDateTime::Now(); wxDateTime nextdt = GetNextSchedule_At(t, 1); properties->AppendItem(_("Next start job"), nextdt); wxDateTime dt = GetStarted(); if (!dt.IsValid()) dt = t; wxDateTime prev; while (dt 0) str += "),("; str += "'"+dt.FormatISODate() + wxT(" ") + dt.FormatISOTime()+ "'"; } wxString sql = "with at(dt) as(select to_timestamp(t.dt,'YYY-MM-DD HH24:MI') dt from (values("+str+")) as t(dt))\n"; sql += "select dt,status from at left join schedule.get_log() b on b.scheduled_at=dt and b.cron="+ NumToStr(GetRecId())+" where b.scheduled_at is null;"; pgSet* jobs = GetConnection()->ExecuteSet(sql); if (jobs) { wxString str; while (!jobs->Eof()) { str+=jobs->GetVal(wxT("dt"))+";"; jobs->MoveNext(); } delete jobs; if (str.Len()>0) properties->AppendItem(_("Previous sched time run skip"), str); } } } bool pgproJob::NeedRefresh() { wxDateTime t = wxDateTime::Now(); if (t >= nextrefresh) { wxTimeSpan m(0, 5); t.Add(m); nextrefresh = t; return true; } return false; } 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,min(scheduled_at) scheduled_at_min from schedule.get_log() b group by cron)") wxT(", 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(",scheduled_at_min") 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,j.days,j.hours,j.wdays,j.months,j.minutes,* 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,days,hours,wdays,months,minutes from jsonb_to_record(c.rule) as (days int[],hours int[],wdays int[],months int[],crontab text,minutes 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(jobs->GetVal(wxT("name"))); job->iSetStatus(status); job->iSetOwner(jobs->GetVal(wxT("owner"))); //job->iSetServer(collection->GetServer()); pgDatabase* db = collection->GetDatabase(); if (db == NULL) { assert(db == NULL); } job->iSetDatabase(collection->GetDatabase()); 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("]")); // days,hours,wdays,months,minutes job->iSetSched(job->GetRecId(), jobs->GetVal("minutes"), jobs->GetVal("hours"), jobs->GetVal("days"), jobs->GetVal("wdays"), jobs->GetVal("months")); job->iSetSchedMin(jobs->GetDateTime(wxT("scheduled_at_min"))); job->iSetCommands(tmp); job->iSetRunAs(jobs->GetVal(wxT("run_as"))); job->iSetStarted(jobs->GetDateTime(wxT("started"))); job->iSetSameTransaction(jobs->GetBool(wxT("use_same_transaction"))); 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("minutes") ||colname == wxT("days") ||colname == wxT("wdays") ||colname == wxT("hours") || colname == wxT("active") ||colname == wxT("months") ||colname == wxT("scheduled_at_min") ||colname==wxT("owner")) continue; if (colname==wxT("broken")) break; if (columnCount) { tmp += wxT("\n,"); } 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,hint from pg_log l where l.log_time>'") + DateToAnsiStr(GetStarted())+ wxT("'::timestamp - interval '1min' and l.log_time<='")+ wxDTend + wxT("'::timestamp + interval '1min' and case when length(detail)>2 then 20 else detail::int end >=0"); //+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, pgDatabase *db) : pgDatabaseObjCollection(factory, db) { } 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() : pgDatabaseObjFactory(__("pgpro_Scheduler 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(), (pgDatabase *) 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?"), _("Enabled or disabled job"), 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(); }