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