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 // pgaStep.cpp - PostgreSQL Agent Step
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 "schema/pgObject.h"
19 #include "schema/pgDatabase.h"
20 #include "schema/pgCollection.h"
21 #include "agent/pgaStep.h"
22 #include "agent/pgaSchedule.h"
23 
pgaStep(pgCollection * _collection,const wxString & newName)24 pgaStep::pgaStep(pgCollection *_collection, const wxString &newName)
25 	: pgaJobObject(_collection->GetJob(), stepFactory, newName)
26 {
27 }
28 
GetTranslatedMessage(int kindOfMessage) const29 wxString pgaStep::GetTranslatedMessage(int kindOfMessage) const
30 {
31 	wxString message = wxEmptyString;
32 
33 	switch (kindOfMessage)
34 	{
35 		case RETRIEVINGDETAILS:
36 			message = _("Retrieving details on pgAgent step");
37 			break;
38 		case REFRESHINGDETAILS:
39 			message = _("Refreshing pgAgent step");
40 			break;
41 		case PROPERTIESREPORT:
42 			message = _("pgAgent step properties report");
43 			break;
44 		case PROPERTIES:
45 			message = _("pgAgent step properties");
46 			break;
47 		case DDLREPORT:
48 			message = _("pgAgent step DDL report");
49 			break;
50 		case DEPENDENCIESREPORT:
51 			message = _("pgAgent step dependencies report");
52 			break;
53 		case DEPENDENCIES:
54 			message = _("pgAgent step dependencies");
55 			break;
56 		case DEPENDENTSREPORT:
57 			message = _("pgAgent step dependents report");
58 			break;
59 		case DEPENDENTS:
60 			message = _("pgAgent step dependents");
61 			break;
62 		case DROPEXCLUDINGDEPS:
63 			message = wxString::Format(_("Are you sure you wish to drop step \"%s\"?"),
64 			                           GetFullIdentifier().c_str());
65 			break;
66 		case DROPTITLE:
67 			message = _("Drop step?");
68 			break;
69 	}
70 
71 	if (!message.IsEmpty() && !(kindOfMessage == DROPEXCLUDINGDEPS || kindOfMessage == DROPTITLE))
72 		message += wxT(" - ") + GetName();
73 
74 	return message;
75 }
76 
IsUpToDate()77 bool pgaStep::IsUpToDate()
78 {
79 	wxString sql = wxT("SELECT xmin FROM pgagent.pga_jobstep WHERE jstid = ") + NumToStr(GetRecId());
80 	if (!GetConnection() || GetConnection()->ExecuteScalar(sql) != NumToStr(GetXid()))
81 		return false;
82 	else
83 		return true;
84 }
85 
DropObject(wxFrame * frame,ctlTree * browser,bool cascaded)86 bool pgaStep::DropObject(wxFrame *frame, ctlTree *browser, bool cascaded)
87 {
88 	return GetConnection()->ExecuteVoid(wxT("DELETE FROM pgagent.pga_jobstep WHERE jstid=") + NumToStr(GetRecId()));
89 }
90 
91 
ShowTreeDetail(ctlTree * browser,frmMain * form,ctlListView * properties,ctlSQLBox * sqlPane)92 void pgaStep::ShowTreeDetail(ctlTree *browser, frmMain *form, ctlListView *properties, ctlSQLBox *sqlPane)
93 {
94 	if (!expandedKids)
95 	{
96 		expandedKids = true;
97 	}
98 
99 	if (properties)
100 	{
101 		CreateListColumns(properties);
102 
103 		properties->AppendItem(_("Name"), GetName());
104 		properties->AppendItem(_("ID"), GetRecId());
105 		properties->AppendYesNoItem(_("Enabled"), GetEnabled());
106 		properties->AppendItem(_("Kind"), GetKind());
107 		if (GetConnStr().IsEmpty())
108 			properties->AppendItem(_("Database"), GetDbname());
109 		else
110 			properties->AppendItem(_("Connection String"), GetConnStr());
111 		properties->AppendItem(_("Code"), GetCode());
112 		properties->AppendItem(_("On error"), GetOnError());
113 
114 		properties->AppendItem(_("Comment"), firstLineOnly(GetComment()));
115 	}
116 }
117 
118 
119 
Refresh(ctlTree * browser,const wxTreeItemId item)120 pgObject *pgaStep::Refresh(ctlTree *browser, const wxTreeItemId item)
121 {
122 	pgObject *step = 0;
123 
124 	pgCollection *coll = browser->GetParentCollection(item);
125 	if (coll)
126 		step = stepFactory.CreateObjects(coll, 0, wxT("\n   AND jstid=") + NumToStr(GetRecId()));
127 
128 	return step;
129 }
130 
131 
132 
CreateObjects(pgCollection * collection,ctlTree * browser,const wxString & restriction)133 pgObject *pgaStepFactory::CreateObjects(pgCollection *collection, ctlTree *browser, const wxString &restriction)
134 {
135 	pgaStep *step = 0;
136 
137 	pgSet *steps = collection->GetConnection()->ExecuteSet(
138 	                   wxT("SELECT xmin, * FROM pgagent.pga_jobstep\n")
139 	                   wxT(" WHERE jstjobid=") + NumToStr(collection->GetJob()->GetRecId()) + wxT("\n")
140 	                   + restriction +
141 	                   wxT(" ORDER BY jstname"));
142 
143 	if (steps)
144 	{
145 		while (!steps->Eof())
146 		{
147 
148 			step = new pgaStep(collection, steps->GetVal(wxT("jstname")));
149 			step->iSetRecId(steps->GetLong(wxT("jstid")));
150 			step->iSetXid(steps->GetOid(wxT("xmin")));
151 			step->iSetDbname(steps->GetVal(wxT("jstdbname")));
152 			if (steps->HasColumn(wxT("jstconnstr")))
153 				step->iSetConnStr(steps->GetVal(wxT("jstconnstr")));
154 			step->iSetCode(steps->GetVal(wxT("jstcode")));
155 			step->iSetEnabled(steps->GetBool(wxT("jstenabled")));
156 
157 			wxChar kindc = *steps->GetVal(wxT("jstkind")).c_str();
158 			wxString kinds;
159 			switch (kindc)
160 			{
161 				case 'b':
162 					kinds = _("Batch");
163 					break;
164 				case 's':
165 					kinds = wxT("SQL");
166 					break;
167 			}
168 			step->iSetKindChar(kindc);
169 			step->iSetKind(kinds);
170 
171 			wxChar onerrc = *steps->GetVal(wxT("jstonerror")).c_str();
172 			wxString onerrs;
173 			switch (onerrc)
174 			{
175 				case 's':
176 					onerrs = _("Succeed");
177 					break;
178 				case 'f':
179 					onerrs = _("Fail");
180 					break;
181 				case 'i':
182 					onerrs = _("Ignore");
183 					break;
184 
185 
186 			}
187 
188 			step->iSetOnErrorChar(onerrc);
189 			step->iSetOnError(onerrs);
190 			step->iSetComment(steps->GetVal(wxT("jstdesc")));
191 
192 
193 			if (browser)
194 			{
195 				browser->AppendObject(collection, step);
196 				steps->MoveNext();
197 			}
198 			else
199 				break;
200 		}
201 
202 		delete steps;
203 	}
204 	return step;
205 }
206 
207 
ShowStatistics(frmMain * form,ctlListView * statistics)208 void pgaStep::ShowStatistics(frmMain *form, ctlListView *statistics)
209 {
210 	wxString sql =
211 	    wxT("SELECT jsljlgid")
212 	    wxT(", jslstatus")
213 	    wxT(", jslresult")
214 	    wxT(", jslstart")
215 	    wxT(", jslduration")
216 	    wxT(", (jslstart + jslduration) AS endtime")
217 	    wxT(", jsloutput")
218 	    wxT("  FROM pgagent.pga_jobsteplog\n")
219 	    wxT(" WHERE jsljstid = ") + NumToStr(GetRecId()) +
220 	    wxT(" ORDER BY jslstart DESC")
221 	    wxT(" LIMIT ") + NumToStr(settings->GetMaxRows());
222 
223 	if (statistics)
224 	{
225 		wxLogInfo(wxT("Displaying statistics for job %s"), GetFullIdentifier().c_str());
226 
227 		// Add the statistics view columns
228 		statistics->ClearAll();
229 		statistics->AddColumn(_("Run"), 50);
230 		statistics->AddColumn(_("Status"), 60);
231 		statistics->AddColumn(_("Result"), 60);
232 		statistics->AddColumn(_("Start time"), 95);
233 		statistics->AddColumn(_("End time"), 95);
234 		statistics->AddColumn(_("Duration"), 70);
235 		statistics->AddColumn(_("Output"), 200);
236 
237 		pgSet *stats = GetConnection()->ExecuteSet(sql);
238 		wxString status;
239 		wxDateTime startTime;
240 		wxDateTime endTime;
241 
242 		if (stats)
243 		{
244 			while (!stats->Eof())
245 			{
246 				if (stats->GetVal(1) == wxT("r"))
247 					status = _("Running");
248 				else if (stats->GetVal(1) == wxT("s"))
249 					status = _("Successful");
250 				else if (stats->GetVal(1) == wxT("f"))
251 					status = _("Failed");
252 				else if (stats->GetVal(1) == wxT("i"))
253 					status = _("Ignored");
254 				else if (stats->GetVal(1) == wxT("d"))
255 					status = _("Aborted");
256 				else
257 					status = _("Unknown");
258 
259 				startTime.ParseDateTime(stats->GetVal(3));
260 				endTime.ParseDateTime(stats->GetVal(5));
261 
262 				long pos = statistics->AppendItem(stats->GetVal(0), status, stats->GetVal(2));
263 				statistics->SetItem(pos, 3, startTime.Format());
264 				if (stats->GetVal(5).Length() > 0)
265 					statistics->SetItem(pos, 4, endTime.Format());
266 				statistics->SetItem(pos, 5, stats->GetVal(4));
267 				statistics->SetItem(pos, 6, stats->GetVal(6));
268 
269 				stats->MoveNext();
270 			}
271 			delete stats;
272 		}
273 	}
274 }
275 
276 
277 /////////////////////////////
278 
279 
pgaStepCollection(pgaFactory * factory,pgaJob * job)280 pgaStepCollection::pgaStepCollection(pgaFactory *factory, pgaJob *job)
281 	: pgaJobObjCollection(factory, job)
282 {
283 }
284 
285 
GetTranslatedMessage(int kindOfMessage) const286 wxString pgaStepCollection::GetTranslatedMessage(int kindOfMessage) const
287 {
288 	wxString message = wxEmptyString;
289 
290 	switch (kindOfMessage)
291 	{
292 		case RETRIEVINGDETAILS:
293 			message = _("Retrieving details on pgAgent steps");
294 			break;
295 		case REFRESHINGDETAILS:
296 			message = _("Refreshing pgAgent steps");
297 			break;
298 		case OBJECTSLISTREPORT:
299 			message = _("pgAgent steps list report");
300 			break;
301 	}
302 
303 	return message;
304 }
305 
306 
307 /////////////////////////////
308 
309 
310 #include "images/step.pngc"
311 #include "images/steps.pngc"
312 
pgaStepFactory()313 pgaStepFactory::pgaStepFactory()
314 	: pgaJobObjFactory(__("Step"), __("New Step"), __("Create a new Step."), step_png_img)
315 {
316 	metaType = PGM_STEP;
317 }
318 
319 
CreateCollection(pgObject * obj)320 pgCollection *pgaStepFactory::CreateCollection(pgObject *obj)
321 {
322 	return new pgaStepCollection(GetCollectionFactory(), (pgaJob *)obj);
323 }
324 
325 
326 pgaStepFactory stepFactory;
327 static pgaCollectionFactory cf(&stepFactory, __("Steps"), steps_png_img);
328