1 //////////////////////////////////////////////////////////////////////////
2 //
3 // pgAdmin III - PostgreSQL Tools
4 //
5 // Copyright (C) 2002 - 2016, The pgAdmin Development Team
6 // This software is released under the PostgreSQL Licence
7 //
8 // pgaJob.h - PostgreSQL Agent Job
9 //
10 //////////////////////////////////////////////////////////////////////////
11
12 // wxWindows headers
13 #include <wx/wx.h>
14
15 // App headers
16 #include "pgAdmin3.h"
17 #include "utils/misc.h"
18 #include "frm/frmMain.h"
19 #include "schema/pgObject.h"
20 #include "schema/pgCollection.h"
21 #include "schema/pgDatabase.h"
22 #include "agent/pgaJob.h"
23 #include "agent/pgaStep.h"
24 #include "agent/pgaSchedule.h"
25
pgaJob(const wxString & newName)26 pgaJob::pgaJob(const wxString &newName)
27 : pgServerObject(jobFactory, newName)
28 {
29 }
30
GetTranslatedMessage(int kindOfMessage) const31 wxString pgaJob::GetTranslatedMessage(int kindOfMessage) const
32 {
33 wxString message = wxEmptyString;
34
35 switch (kindOfMessage)
36 {
37 case RETRIEVINGDETAILS:
38 message = _("Retrieving details on pgAgent job");
39 break;
40 case REFRESHINGDETAILS:
41 message = _("Refreshing pgAgent job");
42 break;
43 case PROPERTIESREPORT:
44 message = _("pgAgent job properties report");
45 break;
46 case PROPERTIES:
47 message = _("pgAgent job properties");
48 break;
49 case DDLREPORT:
50 message = _("pgAgent job DDL report");
51 break;
52 case DEPENDENCIESREPORT:
53 message = _("pgAgent job dependencies report");
54 break;
55 case DEPENDENCIES:
56 message = _("pgAgent job dependencies");
57 break;
58 case DEPENDENTSREPORT:
59 message = _("pgAgent job dependents report");
60 break;
61 case DEPENDENTS:
62 message = _("pgAgent job dependents");
63 break;
64 case DROPEXCLUDINGDEPS:
65 message = wxString::Format(_("Are you sure you wish to drop job \"%s\"?"),
66 GetFullIdentifier().c_str());
67 break;
68 case DROPTITLE:
69 message = _("Drop job?");
70 break;
71 }
72
73 if (!message.IsEmpty() && !(kindOfMessage == DROPEXCLUDINGDEPS || kindOfMessage == DROPTITLE))
74 message += wxT(" - ") + GetName();
75
76 return message;
77 }
78
GetIconId()79 int pgaJob::GetIconId()
80 {
81 if (GetEnabled())
82 return jobFactory.GetIconId();
83 else
84 return jobFactory.GetDisabledId();
85 }
86
87
GetNewMenu()88 wxMenu *pgaJob::GetNewMenu()
89 {
90 wxMenu *menu = pgObject::GetNewMenu();
91 if (1) // check priv.
92 {
93 stepFactory.AppendMenu(menu);
94 scheduleFactory.AppendMenu(menu);
95 }
96 return menu;
97 }
98
99
DropObject(wxFrame * frame,ctlTree * browser,bool cascaded)100 bool pgaJob::DropObject(wxFrame *frame, ctlTree *browser, bool cascaded)
101 {
102 return GetConnection()->ExecuteVoid(wxT("DELETE FROM pgagent.pga_job WHERE jobid=") + NumToStr(GetRecId()));
103 }
104
105
ShowTreeDetail(ctlTree * browser,frmMain * form,ctlListView * properties,ctlSQLBox * sqlPane)106 void pgaJob::ShowTreeDetail(ctlTree *browser, frmMain *form, ctlListView *properties, ctlSQLBox *sqlPane)
107 {
108 if (!expandedKids)
109 {
110 expandedKids = true;
111
112 browser->RemoveDummyChild(this);
113
114 // Log
115 wxLogInfo(wxT("Adding child objects to Job."));
116
117 browser->AppendCollection(this, scheduleFactory);
118 browser->AppendCollection(this, stepFactory);
119 }
120
121 if (properties)
122 {
123 CreateListColumns(properties);
124
125 properties->AppendItem(_("Name"), GetName());
126 properties->AppendItem(_("ID"), GetRecId());
127 properties->AppendYesNoItem(_("Enabled"), GetEnabled());
128 properties->AppendItem(_("Host agent"), GetHostAgent());
129 properties->AppendItem(_("Job class"), GetJobclass());
130 properties->AppendItem(_("Created"), GetCreated());
131 properties->AppendItem(_("Changed"), GetChanged());
132 properties->AppendItem(_("Next run"), GetNextrun());
133 properties->AppendItem(_("Last run"), GetLastrun());
134 properties->AppendItem(_("Last result"), GetLastresult());
135 if (!GetCurrentAgent().IsEmpty())
136 properties->AppendItem(_("Running at"), GetCurrentAgent());
137 else
138 properties->AppendItem(_("Running at"), _("Not currently running"));
139
140 properties->AppendItem(_("Comment"), firstLineOnly(GetComment()));
141 }
142 }
143
144
145
Refresh(ctlTree * browser,const wxTreeItemId item)146 pgObject *pgaJob::Refresh(ctlTree *browser, const wxTreeItemId item)
147 {
148 pgObject *job = 0;
149
150 pgObject *obj = browser->GetObject(browser->GetItemParent(item));
151 if (obj && obj->IsCollection())
152 job = jobFactory.CreateObjects((pgCollection *)obj, 0, wxT("\n WHERE j.jobid=") + NumToStr(GetRecId()));
153
154 return job;
155 }
156
157
158
CreateObjects(pgCollection * collection,ctlTree * browser,const wxString & restriction)159 pgObject *pgaJobFactory::CreateObjects(pgCollection *collection, ctlTree *browser, const wxString &restriction)
160 {
161 pgaJob *job = 0;
162
163 pgSet *jobs = collection->GetConnection()->ExecuteSet(
164 wxT("SELECT j.*, cl.*, ag.*, sub.jlgstatus AS joblastresult ")
165 wxT(" FROM pgagent.pga_job j JOIN")
166 wxT(" pgagent.pga_jobclass cl ON cl.jclid=jobjclid LEFT OUTER JOIN")
167 wxT(" pgagent.pga_jobagent ag ON ag.jagpid=jobagentid LEFT OUTER JOIN")
168 wxT(" (SELECT DISTINCT ON (jlgjobid) jlgstatus, jlgjobid")
169 wxT(" FROM pgagent.pga_joblog")
170 wxT(" ORDER BY jlgjobid, jlgid desc) sub ON sub.jlgjobid = j.jobid ")
171 + restriction +
172 wxT("ORDER BY jobname;"));
173
174 if (jobs)
175 {
176 while (!jobs->Eof())
177 {
178 wxString status;
179 if (jobs->GetVal(wxT("joblastresult")) == wxT("r"))
180 status = _("Running");
181 else if (jobs->GetVal(wxT("joblastresult")) == wxT("s"))
182 status = _("Successful");
183 else if (jobs->GetVal(wxT("joblastresult")) == wxT("f"))
184 status = _("Failed");
185 else if (jobs->GetVal(wxT("joblastresult")) == wxT("d"))
186 status = _("Aborted");
187 else if (jobs->GetVal(wxT("joblastresult")) == wxT("i"))
188 status = _("No steps");
189 else
190 status = _("Unknown");
191
192 job = new pgaJob(jobs->GetVal(wxT("jobname")));
193 job->iSetServer(collection->GetServer());
194 job->iSetRecId(jobs->GetLong(wxT("jobid")));
195 job->iSetComment(jobs->GetVal(wxT("jobdesc")));
196
197 job->iSetEnabled(jobs->GetBool(wxT("jobenabled")));
198 job->iSetJobclass(jobs->GetVal(wxT("jclname")));
199 job->iSetHostAgent(jobs->GetVal(wxT("jobhostagent")));
200 job->iSetCreated(jobs->GetDateTime(wxT("jobcreated")));
201 job->iSetChanged(jobs->GetDateTime(wxT("jobchanged")));
202 job->iSetNextrun(jobs->GetDateTime(wxT("jobnextrun")));
203 job->iSetLastrun(jobs->GetDateTime(wxT("joblastrun")));
204 job->iSetLastresult(status);
205 job->iSetCurrentAgent(jobs->GetVal(wxT("jagstation")));
206
207 if (browser)
208 {
209 browser->AppendObject(collection, job);
210 jobs->MoveNext();
211 }
212 else
213 break;
214 }
215
216 delete jobs;
217 }
218 return job;
219 }
220
ShowStatistics(frmMain * form,ctlListView * statistics)221 void pgaJob::ShowStatistics(frmMain *form, ctlListView *statistics)
222 {
223 wxString sql =
224 wxT("SELECT jlgid")
225 wxT(", jlgstatus")
226 wxT(", jlgstart")
227 wxT(", jlgduration")
228 wxT(", (jlgstart + jlgduration) AS endtime")
229 wxT(" FROM pgagent.pga_joblog\n")
230 wxT(" WHERE jlgjobid = ") + NumToStr(GetRecId()) +
231 wxT(" ORDER BY jlgstart DESC") +
232 wxT(" LIMIT ") + NumToStr(settings->GetMaxRows());
233
234 if (statistics)
235 {
236 wxLogInfo(wxT("Displaying statistics for job %s"), GetFullIdentifier().c_str());
237
238 // Add the statistics view columns
239 statistics->ClearAll();
240 statistics->AddColumn(_("Run"), 50);
241 statistics->AddColumn(_("Status"), 60);
242 statistics->AddColumn(_("Start time"), 95);
243 statistics->AddColumn(_("End time"), 95);
244 statistics->AddColumn(_("Duration"), 70);
245
246 pgSet *stats = GetConnection()->ExecuteSet(sql);
247 wxString status;
248 wxDateTime startTime;
249 wxDateTime endTime;
250
251 if (stats)
252 {
253 while (!stats->Eof())
254 {
255 if (stats->GetVal(1) == wxT("r"))
256 status = _("Running");
257 else if (stats->GetVal(1) == wxT("s"))
258 status = _("Successful");
259 else if (stats->GetVal(1) == wxT("f"))
260 status = _("Failed");
261 else if (stats->GetVal(1) == wxT("d"))
262 status = _("Aborted");
263 else if (stats->GetVal(1) == wxT("i"))
264 status = _("No steps");
265 else
266 status = _("Unknown");
267
268 startTime.ParseDateTime(stats->GetVal(2));
269 endTime.ParseDateTime(stats->GetVal(4));
270
271 long pos = statistics->AppendItem(stats->GetVal(0), status, startTime.Format());
272 if (stats->GetVal(4).Length() > 0)
273 statistics->SetItem(pos, 3, endTime.Format());
274 statistics->SetItem(pos, 4, stats->GetVal(3));
275
276 stats->MoveNext();
277 }
278 delete stats;
279 }
280 }
281 }
282
RunNow()283 bool pgaJob::RunNow()
284 {
285 if (!GetConnection()->ExecuteVoid(wxT("UPDATE pgagent.pga_job SET jobnextrun = now() WHERE jobid=") + NumToStr(GetRecId())))
286 return false;
287
288 return true;
289 }
290
291
pgaJobCollection(pgaFactory * factory,pgServer * sv)292 pgaJobCollection::pgaJobCollection(pgaFactory *factory, pgServer *sv)
293 : pgServerObjCollection(factory, sv)
294 {
295 }
296
297
GetTranslatedMessage(int kindOfMessage) const298 wxString pgaJobCollection::GetTranslatedMessage(int kindOfMessage) const
299 {
300 wxString message = wxEmptyString;
301
302 switch (kindOfMessage)
303 {
304 case RETRIEVINGDETAILS:
305 message = _("Retrieving details on pgAgent jobs");
306 break;
307 case REFRESHINGDETAILS:
308 message = _("Refreshing pgAgent jobs");
309 break;
310 case OBJECTSLISTREPORT:
311 message = _("pgAgent jobs list report");
312 break;
313 }
314
315 return message;
316 }
317
318
pgaJobObject(pgaJob * _job,pgaFactory & factory,const wxString & newName)319 pgaJobObject::pgaJobObject(pgaJob *_job, pgaFactory &factory, const wxString &newName)
320 : pgServerObject(factory, newName)
321 {
322 job = _job;
323 server = job->GetServer();
324 }
325
326
pgaJobObjCollection(pgaFactory * factory,pgaJob * _job)327 pgaJobObjCollection::pgaJobObjCollection(pgaFactory *factory, pgaJob *_job)
328 : pgServerObjCollection(factory, _job->GetServer())
329 {
330 job = _job;
331 }
332
333
CanCreate()334 bool pgaJobObjCollection::CanCreate()
335 {
336 return job->CanCreate();
337 }
338
339
CreateCollection(pgObject * obj)340 pgCollection *pgaJobObjFactory::CreateCollection(pgObject *obj)
341 {
342 return new pgaJobObjCollection(GetCollectionFactory(), (pgaJob *)obj);
343 }
344
345
346 /////////////////////////////
347
348
349 #include "images/job.pngc"
350 #include "images/jobs.pngc"
351 #include "images/jobdisabled.pngc"
352
pgaJobFactory()353 pgaJobFactory::pgaJobFactory()
354 : pgServerObjFactory(__("pgAgent Job"), __("New Job"), __("Create a new Job."), job_png_img)
355 {
356 metaType = PGM_JOB;
357 disabledId = addIcon(jobdisabled_png_img);
358 }
359
360
CreateCollection(pgObject * obj)361 pgCollection *pgaJobFactory::CreateCollection(pgObject *obj)
362 {
363 return new pgaJobCollection(GetCollectionFactory(), (pgServer *)obj);
364 }
365
366
367 pgaJobFactory jobFactory;
368 static pgaCollectionFactory cf(&jobFactory, __("Jobs"), jobs_png_img);
369
runNowFactory(menuFactoryList * list,wxMenu * mnu,ctlMenuToolbar * toolbar)370 runNowFactory::runNowFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar) : contextActionFactory(list)
371 {
372 mnu->Append(id, _("&Run now"), _("Reschedule the job to run now."));
373 }
374
375
StartDialog(frmMain * form,pgObject * obj)376 wxWindow *runNowFactory::StartDialog(frmMain *form, pgObject *obj)
377 {
378 if (!((pgaJob *)(obj))->RunNow())
379 {
380 wxLogError(_("Failed to reschedule the job."));
381 }
382
383 form->Refresh(obj);
384
385 return 0;
386 }
387
388
CheckEnable(pgObject * obj)389 bool runNowFactory::CheckEnable(pgObject *obj)
390 {
391 if (obj)
392 {
393 if (obj->GetMetaType() == PGM_JOB && !obj->IsCollection())
394 return true;
395 }
396 return false;
397 }
398