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 // dlgDirectDbg.cpp - debugger
9 //
10 //////////////////////////////////////////////////////////////////////////
11 
12 #include "pgAdmin3.h"
13 
14 // wxWindows headers
15 #include <wx/wx.h>
16 #include <wx/grid.h>
17 
18 // App headers
19 #include "ctl/ctlAuiNotebook.h"
20 #include "db/pgConn.h"
21 #include "db/pgQueryThread.h"
22 #include "db/pgQueryResultEvent.h"
23 #include "schema/pgObject.h"
24 #include "debugger/dbgConst.h"
25 #include "debugger/dbgBreakPoint.h"
26 #include "debugger/dbgController.h"
27 #include "debugger/dbgModel.h"
28 #include "debugger/ctlStackWindow.h"
29 #include "debugger/ctlMessageWindow.h"
30 #include "debugger/ctlTabWindow.h"
31 #include "debugger/frmDebugger.h"
32 #include "debugger/dlgDirectDbg.h"
33 
34 #include "images/debugger.pngc"
35 
36 #define lblMessage                  CTRL_STATIC("lblMessage")
37 #define grdParams                   CTRL("grdParams", wxGrid)
38 #define chkPkgInit                  CTRL_CHECKBOX("chkPkgInit")
39 #define btnDebug                    CTRL_BUTTON("wxID_OK")
40 
IMPLEMENT_CLASS(dlgDirectDbg,pgDialog)41 IMPLEMENT_CLASS(dlgDirectDbg, pgDialog)
42 
43 BEGIN_EVENT_TABLE(dlgDirectDbg, pgDialog)
44 	EVT_BUTTON(wxID_OK,                               dlgDirectDbg::OnOk)
45 	EVT_BUTTON(wxID_CANCEL,                           dlgDirectDbg::OnCancel)
46 	EVT_GRID_CMD_LABEL_LEFT_CLICK(XRCID("grdParams"), dlgDirectDbg::OnClickGridLabel)
47 	EVT_MENU(RESULT_ID_ARGS_UPDATED,                  dlgDirectDbg::ResultArgsUpdated)
48 	EVT_MENU(RESULT_ID_ARGS_UPDATE_ERROR,             dlgDirectDbg::ResultArgsUpdateError)
49 END_EVENT_TABLE()
50 
51 
52 ////////////////////////////////////////////////////////////////////////////////
53 // dlgDirectDbg constructor
54 //
55 //    This class implements 'direct-debugging'. In direct-debugging, the user
56 //  provides a function signature, procedure signature, or OID on the command
57 //  line (this identifies the debug target).  We query the server for the
58 //  names, types, and in/out modes for each target parameter and then prompt
59 //    the user to enter a value for each of the IN (and IN/OUT) parameters.
60 //
61 //  When the user fills in the parameter values and clicks OK, we set a
62 //  breakpoint at the target and then execute a SELECT statement or an
63 //  EXEC statement that invokes the target (with the parameter values
64 //  provided by the user).
65 
66 dlgDirectDbg::dlgDirectDbg(frmDebugger *_parent, dbgController *_controller,
67                            pgConn *_conn)
68 	: m_controller(_controller), m_thread(NULL), m_conn(_conn)
69 {
70 	int width, height, totalWidth = 0;
71 
72 	SetFont(settings->GetSystemFont());
73 	LoadResource(_parent, wxT("dlgDirectDbg"));
74 
75 	// Icon
76 	SetIcon(*debugger_png_ico);
77 	RestorePosition();
78 
79 	grdParams->SetRowLabelSize(wxGRID_AUTOSIZE);
80 	grdParams->SetColLabelSize(20);
81 	grdParams->AutoSizeColumns(true);
82 
83 	chkPkgInit->SetValue(false);
84 
85 	PopulateParamGrid();
86 
87 	LoadSettings();
88 
89 	for(int i = 0; i < grdParams->GetNumberCols(); i++)
90 		grdParams->AutoSizeColumn(i, true);
91 
92 	if (grdParams->GetNumberCols() > 1)
93 	{
94 		// Extend grid to it's parent width
95 		grdParams->GetClientSize(&width, &height);
96 		for (int i = 0; i < grdParams->GetNumberCols(); i++)
97 		{
98 			totalWidth += grdParams->GetColumnWidth(i);
99 		}
100 		// Total client width - total six column widths - the first (an empty) column
101 		// width
102 		grdParams->SetColumnWidth(COL_DEF_VAL, width - totalWidth - 100);
103 	}
104 }
105 
106 
107 ////////////////////////////////////////////////////////////////////////////////
108 // PopulateParamGrid()
109 //
110 //    This function reads parameter descriptions from m_targetInfo and adds a
111 //    new row to the grid control for each IN (or IN/OUT, VARAIADIC) parameter.
112 //    Each row displays the parameter name, the data type, and an entry box
113 //    where the user can type in a value for that parameter
114 //
PopulateParamGrid()115 void dlgDirectDbg::PopulateParamGrid()
116 {
117 	pgDbgArgs *args = m_controller->GetTargetInfo()->GetArgs();
118 
119 	// If the target is defined within package, offer the user
120 	// a chance to debug the initializer (there may or may not
121 	// be an initializer, we don't really know at this point)
122 	if(m_controller->GetTargetInfo()->GetPkgOid() == 0)
123 		chkPkgInit->Disable();
124 	else
125 		chkPkgInit->Enable();
126 
127 	if (!args)
128 	{
129 		grdParams->CreateGrid(0, 1);
130 		grdParams->SetColLabelValue(COL_NAME, _("No arguments required"));
131 		grdParams->SetColSize(0, 350);
132 
133 		return;
134 	}
135 
136 	// Add seven columns to the grid control:
137 	// - Name, Type, NULL?, EXPR?, Value, Default?, And Default Value
138 	grdParams->CreateGrid(0, 7);
139 
140 	grdParams->SetColLabelValue(COL_NAME, _("Name"));
141 	grdParams->SetColLabelValue(COL_TYPE, _("Type"));
142 	grdParams->SetColLabelValue(COL_NULL, _("Null?"));
143 	grdParams->SetColLabelValue(COL_EXPR, _("Expression?"));
144 	grdParams->SetColLabelValue(COL_VALUE, _("Value"));
145 	grdParams->SetColLabelValue(COL_USE_DEF, _("Use default?"));
146 	grdParams->SetColLabelValue(COL_DEF_VAL, _("Default Value"));
147 
148 	size_t idx = 0;
149 
150 	for(size_t pIdx = 0; pIdx < args->Count(); pIdx++)
151 	{
152 		dbgArgInfo *arg = (*args)[pIdx];
153 
154 		// If this is an IN parameter (or an IN/OUT parameter), add
155 		// a new row to the grid
156 		if(arg->GetMode() != pgParam::PG_PARAM_OUT)
157 		{
158 			grdParams->AppendRows(1);
159 			grdParams->SetCellValue(idx, COL_NAME,  arg->GetName());
160 
161 			// Make it obvious which are variadics
162 			if (arg->GetMode() != pgParam::PG_PARAM_VARIADIC)
163 			{
164 				grdParams->SetRowLabelValue(idx, wxEmptyString);
165 				grdParams->SetCellValue(idx, COL_TYPE, arg->GetTypeName());
166 
167 				if (!arg->IsArray())
168 				{
169 					// Is the value an expression?
170 					grdParams->SetCellEditor(idx, COL_EXPR, new ctlGridCellBoolEditor());
171 					grdParams->SetCellRenderer(idx, COL_EXPR, new wxGridCellBoolRenderer());
172 					grdParams->SetCellValue(idx, COL_EXPR, wxT(""));
173 				}
174 				else
175 				{
176 					grdParams->SetCellBackgroundColour(idx, COL_EXPR, wxColour(235, 235, 235, 90));
177 					grdParams->SetCellBackgroundColour(idx, COL_VALUE, wxColour(235, 235, 235, 90));
178 				}
179 
180 				// Use the default value?
181 				grdParams->SetCellRenderer(idx, COL_USE_DEF, new wxGridCellBoolRenderer());
182 				grdParams->SetCellEditor(idx, COL_USE_DEF, new ctlGridCellBoolEditor());
183 			}
184 			else
185 			{
186 				grdParams->SetCellValue(idx, COL_TYPE, wxT("VARIADIC ") + arg->GetTypeName());
187 				grdParams->SetRowLabelValue(idx, wxEmptyString);
188 
189 				grdParams->AppendRows(1);
190 				grdParams->SetRowLabelValue(idx + 1, wxT("+"));
191 				grdParams->SetCellValue(idx + 1, COL_NAME, _("Click '+/-' to add/remove value to variadic"));
192 
193 				grdParams->SetCellBackgroundColour(idx + 1, COL_EXPR,    wxColour(235, 235, 235, 90));
194 				grdParams->SetCellBackgroundColour(idx + 1, COL_VALUE,   wxColour(235, 235, 235, 90));
195 				grdParams->SetCellBackgroundColour(idx + 1, COL_USE_DEF, wxColour(235, 235, 235, 90));
196 			}
197 
198 			// Set value to NULL?
199 			grdParams->SetCellEditor(idx, COL_NULL, new ctlGridCellBoolEditor(arg));
200 			grdParams->SetCellRenderer(idx, COL_NULL, new wxGridCellBoolRenderer());
201 			grdParams->SetCellValue(idx, COL_NULL, wxT(""));
202 
203 			if (arg->HasDefault())
204 			{
205 				// Whenever the default value is available, use that by default
206 				grdParams->SetCellValue(idx, COL_USE_DEF, wxT("1"));
207 				grdParams->SetCellValue(idx, COL_DEF_VAL, arg->Default());
208 
209 				// When "Use Defalut?" is enabled, disable VALUE, NULL? and
210 				// EXPR? columns
211 				grdParams->SetReadOnly(idx, COL_USE_DEF, false);
212 				grdParams->SetReadOnly(idx, COL_VALUE, false);
213 				grdParams->SetReadOnly(idx, COL_EXPR, false);
214 			}
215 			else
216 			{
217 				grdParams->SetCellValue(idx, COL_USE_DEF, wxT(""));
218 				grdParams->SetCellValue(idx, COL_DEF_VAL, _("<No default value>"));
219 
220 				// When default value is not available, we should ask the user
221 				// to enter them
222 				grdParams->SetReadOnly(idx, COL_USE_DEF, true);
223 				grdParams->SetReadOnly(idx, COL_VALUE, arg->IsArray());
224 				grdParams->SetReadOnly(idx, COL_EXPR, arg->IsArray());
225 
226 				grdParams->SetCellBackgroundColour(idx, COL_USE_DEF, wxColour(235, 235, 235, 90));
227 				if (arg->IsArray())
228 				{
229 					grdParams->SetCellBackgroundColour(idx, COL_VALUE, wxColour(235, 235, 235, 90));
230 					grdParams->SetCellBackgroundColour(idx, COL_EXPR, wxColour(235, 235, 235, 90));
231 				}
232 			}
233 
234 			grdParams->SetReadOnly(idx, COL_NULL, false);
235 			grdParams->SetReadOnly(idx, COL_NAME, true);
236 			grdParams->SetReadOnly(idx, COL_TYPE, true);
237 			grdParams->SetReadOnly(idx, COL_DEF_VAL, true);
238 			grdParams->SetCellBackgroundColour(idx, COL_NAME,    wxColour(235, 235, 235, 90));
239 			grdParams->SetCellBackgroundColour(idx, COL_TYPE,    wxColour(235, 235, 235, 90));
240 			grdParams->SetCellBackgroundColour(idx, COL_DEF_VAL, wxColour(235, 235, 235, 90));
241 
242 			idx++;
243 
244 			if (arg->GetMode() != pgParam::PG_PARAM_VARIADIC &&
245 			        arg->IsArray())
246 			{
247 				grdParams->AppendRows(1);
248 
249 				grdParams->SetRowLabelValue(idx, wxT("+"));
250 				grdParams->SetCellValue(idx, COL_NAME, _("Click '+/-' to add/remove value to the array"));
251 
252 				grdParams->SetReadOnly(idx, COL_NAME,    true);
253 				grdParams->SetReadOnly(idx, COL_TYPE,    true);
254 				grdParams->SetReadOnly(idx, COL_NULL,    true);
255 				grdParams->SetReadOnly(idx, COL_EXPR,    true);
256 				grdParams->SetReadOnly(idx, COL_VALUE,   true);
257 				grdParams->SetReadOnly(idx, COL_USE_DEF, true);
258 				grdParams->SetReadOnly(idx, COL_DEF_VAL, true);
259 
260 				grdParams->SetCellBackgroundColour(idx, COL_NAME,    wxColour(235, 235, 235, 90));
261 				grdParams->SetCellBackgroundColour(idx, COL_TYPE,    wxColour(235, 235, 235, 90));
262 				grdParams->SetCellBackgroundColour(idx, COL_NULL,    wxColour(235, 235, 235, 90));
263 				grdParams->SetCellBackgroundColour(idx, COL_EXPR,    wxColour(235, 235, 235, 90));
264 				grdParams->SetCellBackgroundColour(idx, COL_VALUE,   wxColour(235, 235, 235, 90));
265 				grdParams->SetCellBackgroundColour(idx, COL_USE_DEF, wxColour(235, 235, 235, 90));
266 				grdParams->SetCellBackgroundColour(idx, COL_DEF_VAL, wxColour(235, 235, 235, 90));
267 
268 				idx++;
269 			}
270 		}
271 	}
272 
273 	// Move the cursor to the first value (so that the user
274 	// can just start typing)
275 
276 	grdParams->SetGridCursor(0, COL_VALUE);
277 	grdParams->SetFocus();
278 }
279 
LoadLastCellSetting(int row_number,int parameter_index,int index_array_bound,bool isArray)280 void dlgDirectDbg::LoadLastCellSetting(int row_number, int parameter_index,
281                                        int index_array_bound, bool isArray)
282 {
283 	wxString lastValue, valKey;
284 	dbgTargetInfo *target = m_controller->GetTargetInfo();
285 	long num;
286 
287 	settings->Read(wxT("Debugger/Proc/OID"), &num, -1L);
288 	if(num != target->GetOid())
289 	{
290 		return;
291 	}
292 	else
293 	{
294 		if(!isArray)
295 		{
296 			// Non array elements cell index is 0
297 			valKey = wxString::Format(wxT("Debugger/Proc/%d/0/"), parameter_index);
298 			settings->Read(valKey + wxT("VAL"), &lastValue, wxEmptyString);
299 			grdParams->SetCellValue(row_number, COL_VALUE, lastValue);
300 		}
301 		else
302 		{
303 			long arrCnt;
304 
305 			settings->Read(
306 			    wxString::Format(
307 			        wxT("Debugger/Proc/%d/"), parameter_index)
308 			    + wxT("ArrCnt"), &arrCnt, 0L);
309 
310 			// Refresh an entire array element
311 			for( int i = 0; i <= index_array_bound && i <= arrCnt - 1; i++ )
312 			{
313 				valKey = wxString::Format(wxT("Debugger/Proc/%d/%d/"), parameter_index, i);
314 				settings->Read(valKey + wxT("VAL"), &lastValue, wxEmptyString);
315 				grdParams->SetCellValue(row_number - index_array_bound + i, COL_VALUE,
316 				                        lastValue);
317 			}
318 		}
319 	}
320 
321 }
322 
323 ////////////////////////////////////////////////////////////////////////////////
324 // LoadSettings()
325 //
326 //    Loads default values from our .ini file. We save the OID of the most
327 //    recent direct-debugging target when close a session. If we're direct-
328 //    debugging the same target this time around, we load the argument values
329 //    from the .ini file.
LoadSettings()330 void dlgDirectDbg::LoadSettings()
331 {
332 	long     num, arrCnt;
333 	wxString tmp, key;
334 	bool     initPkgCon = false;
335 
336 	settings->Read(wxT("Debugger/Proc/OID"), &num, -1L);
337 
338 	dbgTargetInfo *target = m_controller->GetTargetInfo();
339 	pgDbgArgs *args = m_controller->GetTargetInfo()->GetArgs();
340 
341 	if (num != target->GetOid())
342 	{
343 		chkPkgInit->SetValue(target->GetPkgInitOid() != 0L);
344 		chkPkgInit->Enable(target->GetPkgInitOid() != 0L);
345 
346 		return;
347 	}
348 	else
349 	{
350 		settings->Read(wxT("Debugger/Proc/initialize_package_constructor"), &initPkgCon, true);
351 
352 		chkPkgInit->SetValue(&initPkgCon && target->GetPkgInitOid() != 0L);
353 		chkPkgInit->Enable(target->GetPkgInitOid() != 0L);
354 	}
355 
356 	settings->Read(wxT("Debugger/Proc/args"), &num, 0L);
357 
358 	if (num <= 0L)
359 		return;
360 
361 	if (!args)
362 		return;
363 
364 	for (int cnt = 0, row = 0; cnt < num && cnt < (int)args->Count();)
365 	{
366 		ctlGridCellBoolEditor *editor =
367 		    dynamic_cast<ctlGridCellBoolEditor *>(
368 		        grdParams->GetCellEditor(row, COL_NULL));
369 		dbgArgInfo *arg
370 		    = editor != NULL ? editor->GetArg() : NULL;
371 
372 		if (arg == NULL)
373 		{
374 			row++;
375 
376 			continue;
377 		}
378 
379 		key = wxString::Format(wxT("Debugger/Proc/%d/"), cnt);
380 
381 		// Use NULL?
382 		settings->Read(key + wxT("NULL"), &tmp, wxEmptyString);
383 		grdParams->SetCellValue(row, COL_NULL, tmp);
384 
385 		// Use Default (if available)
386 		settings->Read(key + wxT("USE_DEF"), &tmp, wxEmptyString);
387 		grdParams->SetCellValue(row, COL_USE_DEF, tmp);
388 
389 		// Is Array/VARIADIC?
390 		settings->Read(key + wxT("ArrCnt"), &arrCnt, 0L);
391 
392 		if (arrCnt > 0L)
393 		{
394 			// An individual variable can be an expression or can be the 'NULL' value
395 			wxString valKey, strVal, strExpr, strNull;
396 
397 			for (int idx = 0; idx < arrCnt; idx++, row++)
398 			{
399 				valKey = wxString::Format(wxT("Debugger/Proc/%d/%d/"), cnt, idx);
400 
401 				// Use 'NULL'?
402 				settings->Read(valKey + wxT("NULL"), &strNull, wxEmptyString);
403 
404 				// Use EXPR?
405 				settings->Read(valKey + wxT("EXPR"), &strExpr, wxEmptyString);
406 
407 				// Value
408 				settings->Read(valKey + wxT("VAL"), &strVal, wxEmptyString);
409 
410 				if (arg->IsArray())
411 				{
412 					int arrRow = row + 1;
413 
414 					if (idx == 0)
415 					{
416 						grdParams->SetReadOnly(row, COL_EXPR, true);
417 						grdParams->SetReadOnly(row, COL_VALUE, true);
418 
419 						grdParams->SetCellBackgroundColour(row, COL_EXPR,  wxColour(235, 235, 235, 90));
420 						grdParams->SetCellBackgroundColour(row, COL_VALUE, wxColour(235, 235, 235, 90));
421 
422 						grdParams->SetRowLabelValue(
423 						    row, wxString::Format(wxT("%d"), cnt + 1));
424 					}
425 					grdParams->InsertRows(arrRow, 1, false);
426 					grdParams->SetRowLabelValue(arrRow, wxT("-"));
427 
428 					grdParams->SetCellValue(arrRow, COL_TYPE, arg->GetBaseType());
429 
430 					// Is the value an expression?
431 					grdParams->SetCellEditor(arrRow, COL_EXPR, new ctlGridCellBoolEditor());
432 					grdParams->SetCellRenderer(arrRow, COL_EXPR,
433 					                           new wxGridCellBoolRenderer());
434 					grdParams->SetCellValue(arrRow, COL_EXPR, strExpr);
435 
436 					// Set value to NULL?
437 					grdParams->SetCellEditor(arrRow, COL_NULL,
438 					                         new ctlGridCellBoolEditor(arg));
439 					grdParams->SetCellRenderer(arrRow, COL_NULL,
440 					                           new wxGridCellBoolRenderer());
441 					grdParams->SetCellValue(arrRow, COL_NULL, strNull);
442 
443 					// Set value
444 					grdParams->SetCellValue(arrRow, COL_VALUE, strVal);
445 
446 					grdParams->SetReadOnly(arrRow, COL_NULL, false);
447 					grdParams->SetReadOnly(arrRow, COL_EXPR, false);
448 					grdParams->SetReadOnly(arrRow, COL_VALUE, false);
449 
450 					grdParams->SetCellBackgroundColour(arrRow, COL_NAME,    wxColour(235, 235, 235, 90));
451 					grdParams->SetCellBackgroundColour(arrRow, COL_TYPE,    wxColour(235, 235, 235, 90));
452 					grdParams->SetCellBackgroundColour(arrRow, COL_USE_DEF, wxColour(235, 235, 235, 90));
453 					grdParams->SetCellBackgroundColour(arrRow, COL_DEF_VAL, wxColour(235, 235, 235, 90));
454 
455 					grdParams->SetReadOnly(arrRow + 1, COL_NAME,    true);
456 					grdParams->SetReadOnly(arrRow + 1, COL_TYPE,    true);
457 					grdParams->SetReadOnly(arrRow + 1, COL_NULL,    true);
458 					grdParams->SetReadOnly(arrRow + 1, COL_EXPR,    true);
459 					grdParams->SetReadOnly(arrRow + 1, COL_VALUE,   true);
460 					grdParams->SetReadOnly(arrRow + 1, COL_USE_DEF, true);
461 					grdParams->SetReadOnly(arrRow + 1, COL_DEF_VAL, true);
462 
463 					grdParams->SetCellBackgroundColour(arrRow + 1, COL_NAME, wxColour(235, 235, 235, 90));
464 					grdParams->SetCellBackgroundColour(arrRow + 1, COL_TYPE, wxColour(235, 235, 235, 90));
465 					grdParams->SetCellBackgroundColour(arrRow + 1, COL_NULL, wxColour(235, 235, 235, 90));
466 					grdParams->SetCellBackgroundColour(arrRow + 1, COL_EXPR, wxColour(235, 235, 235, 90));
467 					grdParams->SetCellBackgroundColour(arrRow + 1, COL_VALUE, wxColour(235, 235, 235, 90));
468 					grdParams->SetCellBackgroundColour(arrRow + 1, COL_USE_DEF,
469 					                                   wxColour(235, 235, 235, 90));
470 					grdParams->SetCellBackgroundColour(arrRow + 1, COL_DEF_VAL,
471 					                                   wxColour(235, 235, 235, 90));
472 
473 					grdParams->SetRowLabelValue(arrRow + 1, wxT("+"));
474 				}
475 				else
476 				{
477 					grdParams->SetCellValue(row, COL_VALUE, strVal);
478 					grdParams->SetCellValue(row, COL_EXPR, strExpr);
479 					grdParams->SetCellValue(row, COL_NULL, strNull);
480 
481 					grdParams->SetReadOnly(row, COL_NULL, false);
482 					grdParams->SetReadOnly(row, COL_EXPR, false);
483 					grdParams->SetReadOnly(row, COL_VALUE, false);
484 
485 					grdParams->SetRowLabelValue(
486 					    row, wxString::Format(wxT("%d"), cnt + 1));
487 				}
488 			}
489 			if (arg->IsArray())
490 				row++;
491 		}
492 		cnt++;
493 	}
494 }
495 
496 
OnOk(wxCommandEvent & _ev)497 void dlgDirectDbg::OnOk(wxCommandEvent &_ev)
498 {
499 	if (m_thread)
500 		return;
501 
502 	grdParams->Enable(false);
503 	btnDebug->Enable(false);
504 
505 	m_thread = new dbgArgValueEvaluator(m_conn, this);
506 
507 	if (m_thread->Create() != wxTHREAD_NO_ERROR)
508 	{
509 		delete m_thread;
510 		m_thread = NULL;
511 
512 		wxLogError(_("Failed to create a debugging thread."));
513 		EndModal(wxID_CANCEL);
514 
515 		return;
516 	}
517 
518 	m_thread->Run();
519 }
520 
521 
OnCancel(wxCommandEvent & _ev)522 void dlgDirectDbg::OnCancel(wxCommandEvent &_ev)
523 {
524 	if (m_thread)
525 	{
526 		if (m_thread->IsRunning())
527 		{
528 			m_thread->CancelEval();
529 			m_thread->Wait();
530 		}
531 
532 		delete m_thread;
533 		m_thread = NULL;
534 	}
535 	_ev.Skip();
536 }
537 
OnClickGridLabel(wxGridEvent & _ev)538 void dlgDirectDbg::OnClickGridLabel(wxGridEvent &_ev)
539 {
540 	if (_ev.AltDown() || _ev.ControlDown() || _ev.ShiftDown())
541 	{
542 		_ev.Skip();
543 
544 		return;
545 	}
546 
547 	int row = _ev.GetRow();
548 	int col = _ev.GetCol();
549 
550 	if (row < 0 || col > 0)
551 	{
552 		_ev.Skip();
553 
554 		return;
555 	}
556 
557 	wxString strLabel = grdParams->GetRowLabelValue(row);
558 
559 	if (strLabel == wxT("+"))
560 	{
561 		wxASSERT(row != 0);
562 
563 		ctlGridCellBoolEditor *editor =
564 		    dynamic_cast<ctlGridCellBoolEditor *>(
565 		        grdParams->GetCellEditor(row - 1, COL_NULL));
566 		dbgArgInfo *arg
567 		    = editor != NULL ? editor->GetArg() : NULL;
568 
569 		grdParams->InsertRows(row, 1, false);
570 		grdParams->SetRowLabelValue(row, wxT("-"));
571 
572 		grdParams->SetCellValue(row, COL_TYPE, arg->GetBaseType());
573 		grdParams->SetCellBackgroundColour(row, COL_TYPE, wxColour(235, 235, 235, 90));
574 		grdParams->SetCellBackgroundColour(row, COL_NAME, wxColour(235, 235, 235, 90));
575 		grdParams->SetCellBackgroundColour(row, COL_USE_DEF, wxColour(235, 235, 235, 90));
576 		grdParams->SetCellBackgroundColour(row, COL_DEF_VAL, wxColour(235, 235, 235, 90));
577 
578 		// Is the value an expression?
579 		grdParams->SetCellEditor(row, COL_EXPR, new ctlGridCellBoolEditor());
580 		grdParams->SetCellRenderer(row, COL_EXPR, new wxGridCellBoolRenderer());
581 		grdParams->SetCellValue(row, COL_EXPR, wxT(""));
582 
583 		// Set value to NULL?
584 		grdParams->SetCellEditor(row, COL_NULL, new ctlGridCellBoolEditor(arg));
585 		grdParams->SetCellRenderer(row, COL_NULL, new wxGridCellBoolRenderer());
586 		grdParams->SetCellValue(row, COL_NULL, wxT("1"));
587 
588 		row++;
589 		grdParams->SetRowLabelValue(row, wxT("+"));
590 	}
591 	else if (strLabel == wxT("-"))
592 	{
593 		dbgArgInfo *arg = NULL;
594 		grdParams->DeleteRows(row, 1, false);
595 	}
596 	else
597 		return;
598 
599 	// Update the row labels
600 	ctlGridCellBoolEditor *editor = NULL;
601 	dbgArgInfo *arg = NULL,
602 	            *prev = NULL;
603 	wxString strName;
604 
605 	int totalRows = grdParams->GetNumberRows(),
606 	    idx = 0;
607 	row = 0;
608 
609 	while (row < totalRows)
610 	{
611 		editor =
612 		    dynamic_cast<ctlGridCellBoolEditor *>(
613 		        grdParams->GetCellEditor(row, COL_NULL));
614 		arg	= editor != NULL ? editor->GetArg() : NULL;
615 		strName = grdParams->GetCellValue(row, COL_NAME);
616 
617 		if (strName.IsEmpty() && arg)
618 			grdParams->SetRowLabelValue(row, wxT("-"));
619 		else if (!strName.IsEmpty() && !arg)
620 			grdParams->SetRowLabelValue(row, wxT("+"));
621 		else
622 		{
623 			idx++;
624 			grdParams->SetRowLabelValue(row, wxString::Format(wxT("%d"), idx));
625 		}
626 
627 		row++;
628 	}
629 }
630 
631 ////////////////////////////////////////////////////////////////////////////////
632 // SaveSettings()
633 //
634 //    Save default values to our .ini file. We save the OID of the most
635 //    recent direct-debugging target when close a session. We also save the
636 //    value of each argument - if you debug the same target again next time,
637 //    loadSettings() will initialize the parameter-values window with the
638 //    same parameter values that you entered in this session.
639 //
SaveSettings()640 void dlgDirectDbg::SaveSettings()
641 {
642 	wxString    strName, strExpr, strVal, strNull, strDef, strKey, strValExpr;
643 	dbgArgInfo *prev = NULL,
644 	            *arg = NULL;
645 	int         row = 0,
646 	            idx = -1,
647 	            arrCnt = 1;
648 
649 	// Save the current function/procedure/trigger OID
650 	settings->WriteLong(wxT("Debugger/Proc/OID"),
651 	                    m_controller->GetTargetInfo()->GetOid());
652 
653 	// Save - whether we need to debug the package constructor
654 	settings->WriteBool(wxT("Debugger/Proc/initialize_package_constructor"),
655 	                    (chkPkgInit->IsEnabled() && chkPkgInit->GetValue()));
656 
657 	for (; row < grdParams->GetNumberRows(); row++)
658 	{
659 		ctlGridCellBoolEditor *editor =
660 		    dynamic_cast<ctlGridCellBoolEditor *>(
661 		        grdParams->GetCellEditor(row, COL_NULL));
662 		arg	= editor != NULL ? editor->GetArg() : NULL;
663 
664 		// This should be the information for add/remove values for an array
665 		if (arg == NULL)
666 		{
667 			continue;
668 		}
669 		else if (prev != arg)
670 		{
671 			idx++;
672 			prev = arg;
673 
674 			if (arg->IsArray())
675 			{
676 				arrCnt = 0;
677 			}
678 			else
679 			{
680 				arrCnt = 1;
681 			}
682 		}
683 		else
684 		{
685 			arrCnt++;
686 		}
687 
688 		settings->WriteInt(wxString::Format(wxT("Debugger/Proc/%d/ArrCnt"), idx), arrCnt);
689 
690 		if ((arrCnt == 0 && arg->IsArray()) ||
691 		        (arrCnt == 1 && !arg->IsArray()))
692 		{
693 			// Use Default (if available)
694 			settings->Write(wxString::Format(wxT("Debugger/Proc/%d/USE_DEF"), idx),
695 			                grdParams->GetCellValue(row, COL_USE_DEF));
696 
697 			// Use NULL?
698 			settings->Write(wxString::Format(wxT("Debugger/Proc/%d/NULL"), idx),
699 			                grdParams->GetCellValue(row, COL_NULL));
700 
701 			if (arrCnt == 0 && arg->IsArray())
702 				continue;
703 		}
704 
705 		strKey = wxString::Format(wxT("Debugger/Proc/%d/%d/"), idx, arrCnt - 1);
706 
707 		// Use NULL?
708 		settings->Write(strKey + wxT("NULL"),
709 		                grdParams->GetCellValue(row, COL_NULL));
710 
711 		// Is value EXPR?
712 		settings->Write(strKey + wxT("EXPR"),
713 		                grdParams->GetCellValue(row, COL_EXPR));
714 
715 		// Value
716 		settings->Write(strKey + wxT("VAL"),
717 		                grdParams->GetCellValue(row, COL_VALUE));
718 	}
719 
720 	// Save the number of arguments
721 	settings->WriteLong(wxT("Debugger/Proc/args"), (long)(idx + 1));
722 }
723 
724 
ctlGridCellBoolEditor(dbgArgInfo * _arg)725 ctlGridCellBoolEditor::ctlGridCellBoolEditor(dbgArgInfo *_arg)
726 	: m_arg(_arg)
727 {}
728 
729 
BeginEdit(int _row,int _col,wxGrid * _grid)730 void ctlGridCellBoolEditor::BeginEdit(int _row, int _col, wxGrid *_grid)
731 {
732 	wxGridCellBoolEditor::BeginEdit(_row, _col, _grid);
733 
734 	wxFocusEvent event (wxEVT_KILL_FOCUS);
735 	if (m_control)
736 	{
737 		m_control->GetEventHandler()->AddPendingEvent(event);
738 	}
739 }
740 
741 
GetParamsGrid()742 wxGrid *dlgDirectDbg::GetParamsGrid()
743 {
744 	return grdParams;
745 }
746 
DebugPkgConstructor()747 bool dlgDirectDbg::DebugPkgConstructor()
748 {
749 	return chkPkgInit->IsEnabled() && chkPkgInit->GetValue();
750 }
751 
752 
Clone() const753 wxGridCellEditor *ctlGridCellBoolEditor::Clone() const
754 {
755 	return new ctlGridCellBoolEditor(m_arg);
756 }
757 
758 
ResultArgsUpdated(wxCommandEvent & _ev)759 void dlgDirectDbg::ResultArgsUpdated(wxCommandEvent &_ev)
760 {
761 	SaveSettings();
762 	SavePosition();
763 
764 	if (m_thread)
765 	{
766 		if (m_thread->IsRunning())
767 		{
768 			m_thread->CancelEval();
769 			m_thread->Wait();
770 		}
771 
772 		delete m_thread;
773 		m_thread = NULL;
774 	}
775 
776 	if (IsModal())
777 		EndModal(wxID_OK);
778 	else
779 		Destroy();
780 }
781 
782 
ResultArgsUpdateError(wxCommandEvent & _ev)783 void dlgDirectDbg::ResultArgsUpdateError(wxCommandEvent &_ev)
784 {
785 	if (_ev.GetInt() == pgQueryResultEvent::PGQ_CONN_LOST)
786 	{
787 		if(wxMessageBox(
788 		            _("Connection to the database server lost!\nDo you want to try to reconnect to the server?"),
789 		            _("Connection Lost"), wxICON_ERROR | wxICON_QUESTION | wxYES_NO) == wxID_YES)
790 		{
791 			if (m_thread)
792 			{
793 				if (m_thread->IsRunning())
794 				{
795 					m_thread->CancelEval();
796 					m_thread->Wait();
797 				}
798 
799 				delete m_thread;
800 				m_thread = NULL;
801 			}
802 			m_conn->Reconnect();
803 
804 			return;
805 		}
806 		EndModal(wxID_CANCEL);
807 
808 		return;
809 	}
810 
811 	if (m_thread)
812 	{
813 		if (m_thread->IsRunning())
814 		{
815 			m_thread->CancelEval();
816 			m_thread->Wait();
817 		}
818 
819 		delete m_thread;
820 		m_thread = NULL;
821 	}
822 
823 	wxLogError(_ev.GetString());
824 
825 	grdParams->Enable(true);
826 	btnDebug->Enable(true);
827 }
828 
829 
NoticeHandler(void *,const char *)830 void dbgArgValueEvaluator::NoticeHandler(void *, const char *)
831 {
832 	// Ignore the notices
833 }
834 
835 
Entry()836 void *dbgArgValueEvaluator::Entry()
837 {
838 	bool argNull, nullVal, useDef;
839 	wxString strVal, strValExpr;
840 	dbgArgInfo *prev = NULL,
841 	            *arg = NULL;
842 
843 	wxGrid *grd = m_dlg->GetParamsGrid();
844 
845 	m_dlg->m_controller->GetTargetInfo()->DebugPackageConstructor()
846 	    = m_dlg->DebugPkgConstructor();
847 
848 	m_conn->RegisterNoticeProcessor(dbgArgValueEvaluator::NoticeHandler, NULL);
849 
850 	for (int row = 0, idx = 0, arrCnt = 1, arr_idx_bound = 0;
851 	        row < grd->GetNumberRows() && !m_cancelled; row++)
852 	{
853 		ctlGridCellBoolEditor *editor =
854 		    dynamic_cast<ctlGridCellBoolEditor *>(
855 		        grd->GetCellEditor(row, dlgDirectDbg::COL_NULL));
856 		arg	= editor != NULL ? editor->GetArg() : NULL;
857 
858 		// This should be the information for add/remove values for an array
859 		if (arg == NULL)
860 		{
861 			// prev was an array, can we fetch value for the same
862 			if (prev && !prev->Null())
863 			{
864 				wxString res =
865 				    m_conn->ExecuteScalar(
866 				        wxT("SELECT ARRAY[") + strValExpr + wxT("]::") + prev->GetTypeName(),
867 				        false);
868 
869 				if (m_cancelled)
870 				{
871 					m_conn->RegisterNoticeProcessor(NULL, NULL);
872 
873 					return (void *)NULL;
874 				}
875 				if (m_conn->GetStatus() == PGCONN_BAD)
876 				{
877 					wxCommandEvent ev(wxEVT_COMMAND_MENU_SELECTED, RESULT_ID_ARGS_UPDATE_ERROR);
878 
879 					ev.SetInt(pgQueryResultEvent::PGQ_CONN_LOST);
880 
881 					m_dlg->GetEventHandler()->AddPendingEvent(ev);
882 
883 					m_conn->RegisterNoticeProcessor(NULL, NULL);
884 
885 					return (void *)NULL;
886 				}
887 				if (m_conn->GetLastResultStatus() == PGRES_TUPLES_OK)
888 				{
889 					prev->Value() = res;
890 				}
891 				else
892 				{
893 					wxCommandEvent ev(wxEVT_COMMAND_MENU_SELECTED, RESULT_ID_ARGS_UPDATE_ERROR);
894 					wxString strName = prev->GetName();
895 					wxString strMsg;
896 
897 					if (strName.IsEmpty())
898 					{
899 						ev.SetString(
900 						    wxString::Format(
901 						        _("The specified value for argument #%d is not valid.\nPlease re-enter the value for it."),
902 						        idx));
903 					}
904 					else
905 					{
906 						ev.SetString(
907 						    wxString::Format(
908 						        _("Specified value for argument '%s' is not valid.\nPlease re-enter the value for it."),
909 						        strName.c_str()));
910 					}
911 					ev.SetInt(pgQueryResultEvent::PGQ_RESULT_ERROR);
912 					m_dlg->LoadLastCellSetting(row - 1, idx - 1, arr_idx_bound - 1, true);
913 					m_dlg->GetEventHandler()->AddPendingEvent(ev);
914 
915 					m_conn->RegisterNoticeProcessor(NULL, NULL);
916 
917 					return (void *)NULL;
918 
919 				}
920 				prev = NULL;
921 				arr_idx_bound = 0;
922 			}
923 
924 			continue;
925 		}
926 		else if (prev != arg)
927 		{
928 			idx++;
929 			argNull = false;
930 			nullVal = false;
931 			useDef  = false;
932 
933 			strValExpr = wxEmptyString;
934 		}
935 
936 		if (prev != arg)
937 		{
938 			prev = arg;
939 
940 			// Use Default (if available)
941 			arg->UseDefault() = wxGridCellBoolEditor::IsTrueValue(
942 			                        grd->GetCellValue(row, dlgDirectDbg::COL_USE_DEF));
943 			// NULL?
944 			arg->Null() = wxGridCellBoolEditor::IsTrueValue(
945 			                  grd->GetCellValue(row, dlgDirectDbg::COL_NULL));
946 
947 			if (arg->UseDefault())
948 				strValExpr = arg->Default();
949 
950 			if (arg->IsArray())
951 				continue;
952 		}
953 
954 		// Use NULL?
955 		//
956 		//   Keep this just before 'Use Default' column as - we may have non-empty
957 		//   value in the 'VAL' column
958 
959 		if (!arg->UseDefault() || !arg->Null())
960 		{
961 			if (!strValExpr.IsEmpty())
962 			{
963 				strValExpr += wxT(", ");
964 			}
965 
966 			if (wxGridCellBoolEditor::IsTrueValue(
967 			            grd->GetCellValue(row, dlgDirectDbg::COL_NULL)))
968 			{
969 				strValExpr += wxT("NULL");
970 			}
971 			else if (wxGridCellBoolEditor::IsTrueValue(
972 			             grd->GetCellValue(row, dlgDirectDbg::COL_EXPR)))
973 			{
974 				strValExpr += wxT("(") + grd->GetCellValue(row, dlgDirectDbg::COL_VALUE) + wxT(")");
975 			}
976 			else
977 			{
978 				strValExpr += m_conn->qtDbString(
979 				                  grd->GetCellValue(row, dlgDirectDbg::COL_VALUE));
980 			}
981 			strValExpr.Append(wxT("::"))
982 			.Append(arg->IsArray() ?
983 			        arg->GetBaseType() : arg->GetTypeName());
984 
985 			if(arg->IsArray())
986 			{
987 				arr_idx_bound ++;
988 			}
989 		}
990 
991 		if (!arg->IsArray() && !arg->Null())
992 		{
993 			pgSet *set =
994 			    m_conn->ExecuteSet(wxT("SELECT ") + strValExpr, false);
995 
996 			if (m_cancelled)
997 			{
998 				return (void *)NULL;
999 			}
1000 			if (m_conn->GetStatus() == PGCONN_BAD)
1001 			{
1002 				wxCommandEvent ev(wxEVT_COMMAND_MENU_SELECTED, RESULT_ID_ARGS_UPDATE_ERROR);
1003 
1004 				ev.SetString(_("Connection to the database server lost!"));
1005 				ev.SetInt(pgQueryResultEvent::PGQ_CONN_LOST);
1006 
1007 				m_dlg->GetEventHandler()->AddPendingEvent(ev);
1008 
1009 				m_conn->RegisterNoticeProcessor(NULL, NULL);
1010 
1011 				if (set)
1012 					delete set;
1013 
1014 				return (void *)NULL;
1015 			}
1016 			if (m_conn->GetLastResultStatus() == PGRES_TUPLES_OK &&
1017 			        set && set->NumRows() > 0L)
1018 			{
1019 				if (set->IsNull(0))
1020 				{
1021 					arg->Null() = true;
1022 				}
1023 				else
1024 				{
1025 					arg->Null() = false;
1026 					arg->Value() = set->GetVal(0);
1027 				}
1028 			}
1029 			else
1030 			{
1031 				wxString strName = arg->GetName();
1032 
1033 				if (strName.IsEmpty())
1034 					strName = wxString::Format(_("Param#%d"), idx);
1035 
1036 				wxCommandEvent ev(wxEVT_COMMAND_MENU_SELECTED, RESULT_ID_ARGS_UPDATE_ERROR);
1037 
1038 				ev.SetString(
1039 				    wxString::Format(_("Please re-enter the value for argument '%s'."),
1040 				                     strName.c_str()));
1041 				ev.SetInt(pgQueryResultEvent::PGQ_RESULT_ERROR);
1042 
1043 				m_dlg->GetEventHandler()->AddPendingEvent(ev);
1044 				m_dlg->LoadSettings();
1045 				// For scalar variable, always index of array bound is 0.
1046 				m_dlg->LoadLastCellSetting(row, idx - 1, 0, false);
1047 				m_conn->RegisterNoticeProcessor(NULL, NULL);
1048 
1049 				return (void *)NULL;
1050 			}
1051 		}
1052 	}
1053 
1054 	wxCommandEvent ev(wxEVT_COMMAND_MENU_SELECTED, RESULT_ID_ARGS_UPDATED);
1055 	m_dlg->GetEventHandler()->AddPendingEvent(ev);
1056 
1057 	m_conn->RegisterNoticeProcessor(NULL, NULL);
1058 
1059 	return (void *)NULL;
1060 }
1061 
1062 
CancelEval()1063 void dbgArgValueEvaluator::CancelEval()
1064 {
1065 	m_cancelled = true;
1066 
1067 	if (m_conn->GetTxStatus() == PGCONN_TXSTATUS_ACTIVE)
1068 		m_conn->CancelExecution();
1069 }
1070 
dbgArgValueEvaluator(pgConn * _conn,dlgDirectDbg * _dlg)1071 dbgArgValueEvaluator::dbgArgValueEvaluator(pgConn *_conn, dlgDirectDbg *_dlg)
1072 	: wxThread(wxTHREAD_JOINABLE), m_conn(_conn), m_dlg(_dlg), m_cancelled(false)
1073 {}
1074