////////////////////////////////////////////////////////////////////////// // // pgAdmin III - PostgreSQL Tools // // Copyright (C) 2002 - 2016, The pgAdmin Development Team // This software is released under the PostgreSQL Licence // // dlgDirectDbg.cpp - debugger // ////////////////////////////////////////////////////////////////////////// #include "pgAdmin3.h" // wxWindows headers #include #include // App headers #include "ctl/ctlAuiNotebook.h" #include "db/pgConn.h" #include "db/pgQueryThread.h" #include "db/pgQueryResultEvent.h" #include "schema/pgObject.h" #include "debugger/dbgConst.h" #include "debugger/dbgBreakPoint.h" #include "debugger/dbgController.h" #include "debugger/dbgModel.h" #include "debugger/ctlStackWindow.h" #include "debugger/ctlMessageWindow.h" #include "debugger/ctlTabWindow.h" #include "debugger/frmDebugger.h" #include "debugger/dlgDirectDbg.h" #include "images/debugger.pngc" #define lblMessage CTRL_STATIC("lblMessage") #define grdParams CTRL("grdParams", wxGrid) #define chkPkgInit CTRL_CHECKBOX("chkPkgInit") #define btnDebug CTRL_BUTTON("wxID_OK") IMPLEMENT_CLASS(dlgDirectDbg, pgDialog) BEGIN_EVENT_TABLE(dlgDirectDbg, pgDialog) EVT_BUTTON(wxID_OK, dlgDirectDbg::OnOk) EVT_BUTTON(wxID_CANCEL, dlgDirectDbg::OnCancel) EVT_GRID_CMD_LABEL_LEFT_CLICK(XRCID("grdParams"), dlgDirectDbg::OnClickGridLabel) EVT_MENU(RESULT_ID_ARGS_UPDATED, dlgDirectDbg::ResultArgsUpdated) EVT_MENU(RESULT_ID_ARGS_UPDATE_ERROR, dlgDirectDbg::ResultArgsUpdateError) END_EVENT_TABLE() //////////////////////////////////////////////////////////////////////////////// // dlgDirectDbg constructor // // This class implements 'direct-debugging'. In direct-debugging, the user // provides a function signature, procedure signature, or OID on the command // line (this identifies the debug target). We query the server for the // names, types, and in/out modes for each target parameter and then prompt // the user to enter a value for each of the IN (and IN/OUT) parameters. // // When the user fills in the parameter values and clicks OK, we set a // breakpoint at the target and then execute a SELECT statement or an // EXEC statement that invokes the target (with the parameter values // provided by the user). dlgDirectDbg::dlgDirectDbg(frmDebugger *_parent, dbgController *_controller, pgConn *_conn) : m_controller(_controller), m_thread(NULL), m_conn(_conn) { int width, height, totalWidth = 0; SetFont(settings->GetSystemFont()); LoadResource(_parent, wxT("dlgDirectDbg")); // Icon SetIcon(*debugger_png_ico); RestorePosition(); grdParams->SetRowLabelSize(wxGRID_AUTOSIZE); grdParams->SetColLabelSize(20); grdParams->AutoSizeColumns(true); chkPkgInit->SetValue(false); PopulateParamGrid(); LoadSettings(); for(int i = 0; i < grdParams->GetNumberCols(); i++) grdParams->AutoSizeColumn(i, true); if (grdParams->GetNumberCols() > 1) { // Extend grid to it's parent width grdParams->GetClientSize(&width, &height); for (int i = 0; i < grdParams->GetNumberCols(); i++) { totalWidth += grdParams->GetColSize(i); } // Total client width - total six column widths - the first (an empty) column // width grdParams->SetColSize(COL_DEF_VAL, width - totalWidth - 100); } } //////////////////////////////////////////////////////////////////////////////// // PopulateParamGrid() // // This function reads parameter descriptions from m_targetInfo and adds a // new row to the grid control for each IN (or IN/OUT, VARAIADIC) parameter. // Each row displays the parameter name, the data type, and an entry box // where the user can type in a value for that parameter // void dlgDirectDbg::PopulateParamGrid() { pgDbgArgs *args = m_controller->GetTargetInfo()->GetArgs(); // If the target is defined within package, offer the user // a chance to debug the initializer (there may or may not // be an initializer, we don't really know at this point) if(m_controller->GetTargetInfo()->GetPkgOid() == 0) chkPkgInit->Disable(); else chkPkgInit->Enable(); if (!args) { grdParams->CreateGrid(0, 1); grdParams->SetColLabelValue(COL_NAME, _("No arguments required")); grdParams->SetColSize(0, 350); return; } // Add seven columns to the grid control: // - Name, Type, NULL?, EXPR?, Value, Default?, And Default Value grdParams->CreateGrid(0, 7); grdParams->SetColLabelValue(COL_NAME, _("Name")); grdParams->SetColLabelValue(COL_TYPE, _("Type")); grdParams->SetColLabelValue(COL_NULL, _("Null?")); grdParams->SetColLabelValue(COL_EXPR, _("Expression?")); grdParams->SetColLabelValue(COL_VALUE, _("Value")); grdParams->SetColLabelValue(COL_USE_DEF, _("Use default?")); grdParams->SetColLabelValue(COL_DEF_VAL, _("Default Value")); size_t idx = 0; for(size_t pIdx = 0; pIdx < args->Count(); pIdx++) { dbgArgInfo *arg = (*args)[pIdx]; // If this is an IN parameter (or an IN/OUT parameter), add // a new row to the grid if(arg->GetMode() != pgParam::PG_PARAM_OUT) { grdParams->AppendRows(1); grdParams->SetCellValue(idx, COL_NAME, arg->GetName()); // Make it obvious which are variadics if (arg->GetMode() != pgParam::PG_PARAM_VARIADIC) { grdParams->SetRowLabelValue(idx, wxEmptyString); grdParams->SetCellValue(idx, COL_TYPE, arg->GetTypeName()); if (!arg->IsArray()) { // Is the value an expression? grdParams->SetCellEditor(idx, COL_EXPR, new ctlGridCellBoolEditor()); grdParams->SetCellRenderer(idx, COL_EXPR, new wxGridCellBoolRenderer()); grdParams->SetCellValue(idx, COL_EXPR, wxT("")); } else { grdParams->SetCellBackgroundColour(idx, COL_EXPR, wxColour(235, 235, 235, 90)); grdParams->SetCellBackgroundColour(idx, COL_VALUE, wxColour(235, 235, 235, 90)); } // Use the default value? grdParams->SetCellRenderer(idx, COL_USE_DEF, new wxGridCellBoolRenderer()); grdParams->SetCellEditor(idx, COL_USE_DEF, new ctlGridCellBoolEditor()); } else { grdParams->SetCellValue(idx, COL_TYPE, wxT("VARIADIC ") + arg->GetTypeName()); grdParams->SetRowLabelValue(idx, wxEmptyString); grdParams->AppendRows(1); grdParams->SetRowLabelValue(idx + 1, wxT("+")); grdParams->SetCellValue(idx + 1, COL_NAME, _("Click '+/-' to add/remove value to variadic")); grdParams->SetCellBackgroundColour(idx + 1, COL_EXPR, wxColour(235, 235, 235, 90)); grdParams->SetCellBackgroundColour(idx + 1, COL_VALUE, wxColour(235, 235, 235, 90)); grdParams->SetCellBackgroundColour(idx + 1, COL_USE_DEF, wxColour(235, 235, 235, 90)); } // Set value to NULL? grdParams->SetCellEditor(idx, COL_NULL, new ctlGridCellBoolEditor(arg)); grdParams->SetCellRenderer(idx, COL_NULL, new wxGridCellBoolRenderer()); grdParams->SetCellValue(idx, COL_NULL, wxT("")); if (arg->HasDefault()) { // Whenever the default value is available, use that by default grdParams->SetCellValue(idx, COL_USE_DEF, wxT("1")); grdParams->SetCellValue(idx, COL_DEF_VAL, arg->Default()); // When "Use Defalut?" is enabled, disable VALUE, NULL? and // EXPR? columns grdParams->SetReadOnly(idx, COL_USE_DEF, false); grdParams->SetReadOnly(idx, COL_VALUE, false); grdParams->SetReadOnly(idx, COL_EXPR, false); } else { grdParams->SetCellValue(idx, COL_USE_DEF, wxT("")); grdParams->SetCellValue(idx, COL_DEF_VAL, _("")); // When default value is not available, we should ask the user // to enter them grdParams->SetReadOnly(idx, COL_USE_DEF, true); grdParams->SetReadOnly(idx, COL_VALUE, arg->IsArray()); grdParams->SetReadOnly(idx, COL_EXPR, arg->IsArray()); grdParams->SetCellBackgroundColour(idx, COL_USE_DEF, wxColour(235, 235, 235, 90)); if (arg->IsArray()) { grdParams->SetCellBackgroundColour(idx, COL_VALUE, wxColour(235, 235, 235, 90)); grdParams->SetCellBackgroundColour(idx, COL_EXPR, wxColour(235, 235, 235, 90)); } } grdParams->SetReadOnly(idx, COL_NULL, false); grdParams->SetReadOnly(idx, COL_NAME, true); grdParams->SetReadOnly(idx, COL_TYPE, true); grdParams->SetReadOnly(idx, COL_DEF_VAL, true); grdParams->SetCellBackgroundColour(idx, COL_NAME, wxColour(235, 235, 235, 90)); grdParams->SetCellBackgroundColour(idx, COL_TYPE, wxColour(235, 235, 235, 90)); grdParams->SetCellBackgroundColour(idx, COL_DEF_VAL, wxColour(235, 235, 235, 90)); idx++; if (arg->GetMode() != pgParam::PG_PARAM_VARIADIC && arg->IsArray()) { grdParams->AppendRows(1); grdParams->SetRowLabelValue(idx, wxT("+")); grdParams->SetCellValue(idx, COL_NAME, _("Click '+/-' to add/remove value to the array")); grdParams->SetReadOnly(idx, COL_NAME, true); grdParams->SetReadOnly(idx, COL_TYPE, true); grdParams->SetReadOnly(idx, COL_NULL, true); grdParams->SetReadOnly(idx, COL_EXPR, true); grdParams->SetReadOnly(idx, COL_VALUE, true); grdParams->SetReadOnly(idx, COL_USE_DEF, true); grdParams->SetReadOnly(idx, COL_DEF_VAL, true); grdParams->SetCellBackgroundColour(idx, COL_NAME, wxColour(235, 235, 235, 90)); grdParams->SetCellBackgroundColour(idx, COL_TYPE, wxColour(235, 235, 235, 90)); grdParams->SetCellBackgroundColour(idx, COL_NULL, wxColour(235, 235, 235, 90)); grdParams->SetCellBackgroundColour(idx, COL_EXPR, wxColour(235, 235, 235, 90)); grdParams->SetCellBackgroundColour(idx, COL_VALUE, wxColour(235, 235, 235, 90)); grdParams->SetCellBackgroundColour(idx, COL_USE_DEF, wxColour(235, 235, 235, 90)); grdParams->SetCellBackgroundColour(idx, COL_DEF_VAL, wxColour(235, 235, 235, 90)); idx++; } } } // Move the cursor to the first value (so that the user // can just start typing) grdParams->SetGridCursor(0, COL_VALUE); grdParams->SetFocus(); } void dlgDirectDbg::LoadLastCellSetting(int row_number, int parameter_index, int index_array_bound, bool isArray) { wxString lastValue, valKey; dbgTargetInfo *target = m_controller->GetTargetInfo(); long num; settings->Read(wxT("Debugger/Proc/OID"), &num, -1L); if(num != target->GetOid()) { return; } else { if(!isArray) { // Non array elements cell index is 0 valKey = wxString::Format(wxT("Debugger/Proc/%d/0/"), parameter_index); settings->Read(valKey + wxT("VAL"), &lastValue, wxEmptyString); grdParams->SetCellValue(row_number, COL_VALUE, lastValue); } else { long arrCnt; settings->Read( wxString::Format( wxT("Debugger/Proc/%d/"), parameter_index) + wxT("ArrCnt"), &arrCnt, 0L); // Refresh an entire array element for( int i = 0; i <= index_array_bound && i <= arrCnt - 1; i++ ) { valKey = wxString::Format(wxT("Debugger/Proc/%d/%d/"), parameter_index, i); settings->Read(valKey + wxT("VAL"), &lastValue, wxEmptyString); grdParams->SetCellValue(row_number - index_array_bound + i, COL_VALUE, lastValue); } } } } //////////////////////////////////////////////////////////////////////////////// // LoadSettings() // // Loads default values from our .ini file. We save the OID of the most // recent direct-debugging target when close a session. If we're direct- // debugging the same target this time around, we load the argument values // from the .ini file. void dlgDirectDbg::LoadSettings() { long num, arrCnt; wxString tmp, key; bool initPkgCon = false; settings->Read(wxT("Debugger/Proc/OID"), &num, -1L); dbgTargetInfo *target = m_controller->GetTargetInfo(); pgDbgArgs *args = m_controller->GetTargetInfo()->GetArgs(); if (num != target->GetOid()) { chkPkgInit->SetValue(target->GetPkgInitOid() != 0L); chkPkgInit->Enable(target->GetPkgInitOid() != 0L); return; } else { settings->Read(wxT("Debugger/Proc/initialize_package_constructor"), &initPkgCon, true); chkPkgInit->SetValue(&initPkgCon && target->GetPkgInitOid() != 0L); chkPkgInit->Enable(target->GetPkgInitOid() != 0L); } settings->Read(wxT("Debugger/Proc/args"), &num, 0L); if (num <= 0L) return; if (!args) return; for (int cnt = 0, row = 0; cnt < num && cnt < (int)args->Count();) { ctlGridCellBoolEditor *editor = dynamic_cast( grdParams->GetCellEditor(row, COL_NULL)); dbgArgInfo *arg = editor != NULL ? editor->GetArg() : NULL; if (arg == NULL) { row++; continue; } key = wxString::Format(wxT("Debugger/Proc/%d/"), cnt); // Use NULL? settings->Read(key + wxT("NULL"), &tmp, wxEmptyString); grdParams->SetCellValue(row, COL_NULL, tmp); // Use Default (if available) settings->Read(key + wxT("USE_DEF"), &tmp, wxEmptyString); grdParams->SetCellValue(row, COL_USE_DEF, tmp); // Is Array/VARIADIC? settings->Read(key + wxT("ArrCnt"), &arrCnt, 0L); if (arrCnt > 0L) { // An individual variable can be an expression or can be the 'NULL' value wxString valKey, strVal, strExpr, strNull; for (int idx = 0; idx < arrCnt; idx++, row++) { valKey = wxString::Format(wxT("Debugger/Proc/%d/%d/"), cnt, idx); // Use 'NULL'? settings->Read(valKey + wxT("NULL"), &strNull, wxEmptyString); // Use EXPR? settings->Read(valKey + wxT("EXPR"), &strExpr, wxEmptyString); // Value settings->Read(valKey + wxT("VAL"), &strVal, wxEmptyString); if (arg->IsArray()) { int arrRow = row + 1; if (idx == 0) { grdParams->SetReadOnly(row, COL_EXPR, true); grdParams->SetReadOnly(row, COL_VALUE, true); grdParams->SetCellBackgroundColour(row, COL_EXPR, wxColour(235, 235, 235, 90)); grdParams->SetCellBackgroundColour(row, COL_VALUE, wxColour(235, 235, 235, 90)); grdParams->SetRowLabelValue( row, wxString::Format(wxT("%d"), cnt + 1)); } grdParams->InsertRows(arrRow, 1, false); grdParams->SetRowLabelValue(arrRow, wxT("-")); grdParams->SetCellValue(arrRow, COL_TYPE, arg->GetBaseType()); // Is the value an expression? grdParams->SetCellEditor(arrRow, COL_EXPR, new ctlGridCellBoolEditor()); grdParams->SetCellRenderer(arrRow, COL_EXPR, new wxGridCellBoolRenderer()); grdParams->SetCellValue(arrRow, COL_EXPR, strExpr); // Set value to NULL? grdParams->SetCellEditor(arrRow, COL_NULL, new ctlGridCellBoolEditor(arg)); grdParams->SetCellRenderer(arrRow, COL_NULL, new wxGridCellBoolRenderer()); grdParams->SetCellValue(arrRow, COL_NULL, strNull); // Set value grdParams->SetCellValue(arrRow, COL_VALUE, strVal); grdParams->SetReadOnly(arrRow, COL_NULL, false); grdParams->SetReadOnly(arrRow, COL_EXPR, false); grdParams->SetReadOnly(arrRow, COL_VALUE, false); grdParams->SetCellBackgroundColour(arrRow, COL_NAME, wxColour(235, 235, 235, 90)); grdParams->SetCellBackgroundColour(arrRow, COL_TYPE, wxColour(235, 235, 235, 90)); grdParams->SetCellBackgroundColour(arrRow, COL_USE_DEF, wxColour(235, 235, 235, 90)); grdParams->SetCellBackgroundColour(arrRow, COL_DEF_VAL, wxColour(235, 235, 235, 90)); grdParams->SetReadOnly(arrRow + 1, COL_NAME, true); grdParams->SetReadOnly(arrRow + 1, COL_TYPE, true); grdParams->SetReadOnly(arrRow + 1, COL_NULL, true); grdParams->SetReadOnly(arrRow + 1, COL_EXPR, true); grdParams->SetReadOnly(arrRow + 1, COL_VALUE, true); grdParams->SetReadOnly(arrRow + 1, COL_USE_DEF, true); grdParams->SetReadOnly(arrRow + 1, COL_DEF_VAL, true); grdParams->SetCellBackgroundColour(arrRow + 1, COL_NAME, wxColour(235, 235, 235, 90)); grdParams->SetCellBackgroundColour(arrRow + 1, COL_TYPE, wxColour(235, 235, 235, 90)); grdParams->SetCellBackgroundColour(arrRow + 1, COL_NULL, wxColour(235, 235, 235, 90)); grdParams->SetCellBackgroundColour(arrRow + 1, COL_EXPR, wxColour(235, 235, 235, 90)); grdParams->SetCellBackgroundColour(arrRow + 1, COL_VALUE, wxColour(235, 235, 235, 90)); grdParams->SetCellBackgroundColour(arrRow + 1, COL_USE_DEF, wxColour(235, 235, 235, 90)); grdParams->SetCellBackgroundColour(arrRow + 1, COL_DEF_VAL, wxColour(235, 235, 235, 90)); grdParams->SetRowLabelValue(arrRow + 1, wxT("+")); } else { grdParams->SetCellValue(row, COL_VALUE, strVal); grdParams->SetCellValue(row, COL_EXPR, strExpr); grdParams->SetCellValue(row, COL_NULL, strNull); grdParams->SetReadOnly(row, COL_NULL, false); grdParams->SetReadOnly(row, COL_EXPR, false); grdParams->SetReadOnly(row, COL_VALUE, false); grdParams->SetRowLabelValue( row, wxString::Format(wxT("%d"), cnt + 1)); } } if (arg->IsArray()) row++; } cnt++; } } void dlgDirectDbg::OnOk(wxCommandEvent &_ev) { if (m_thread) return; grdParams->Enable(false); btnDebug->Enable(false); m_thread = new dbgArgValueEvaluator(m_conn, this); if (m_thread->Create() != wxTHREAD_NO_ERROR) { delete m_thread; m_thread = NULL; wxLogError(_("Failed to create a debugging thread.")); EndModal(wxID_CANCEL); return; } m_thread->Run(); } void dlgDirectDbg::OnCancel(wxCommandEvent &_ev) { if (m_thread) { if (m_thread->IsRunning()) { m_thread->CancelEval(); m_thread->Wait(); } delete m_thread; m_thread = NULL; } _ev.Skip(); } void dlgDirectDbg::OnClickGridLabel(wxGridEvent &_ev) { if (_ev.AltDown() || _ev.ControlDown() || _ev.ShiftDown()) { _ev.Skip(); return; } int row = _ev.GetRow(); int col = _ev.GetCol(); if (row < 0 || col > 0) { _ev.Skip(); return; } wxString strLabel = grdParams->GetRowLabelValue(row); if (strLabel == wxT("+")) { wxASSERT(row != 0); ctlGridCellBoolEditor *editor = dynamic_cast( grdParams->GetCellEditor(row - 1, COL_NULL)); dbgArgInfo *arg = editor != NULL ? editor->GetArg() : NULL; grdParams->InsertRows(row, 1, false); grdParams->SetRowLabelValue(row, wxT("-")); grdParams->SetCellValue(row, COL_TYPE, arg->GetBaseType()); grdParams->SetCellBackgroundColour(row, COL_TYPE, wxColour(235, 235, 235, 90)); grdParams->SetCellBackgroundColour(row, COL_NAME, wxColour(235, 235, 235, 90)); grdParams->SetCellBackgroundColour(row, COL_USE_DEF, wxColour(235, 235, 235, 90)); grdParams->SetCellBackgroundColour(row, COL_DEF_VAL, wxColour(235, 235, 235, 90)); // Is the value an expression? grdParams->SetCellEditor(row, COL_EXPR, new ctlGridCellBoolEditor()); grdParams->SetCellRenderer(row, COL_EXPR, new wxGridCellBoolRenderer()); grdParams->SetCellValue(row, COL_EXPR, wxT("")); // Set value to NULL? grdParams->SetCellEditor(row, COL_NULL, new ctlGridCellBoolEditor(arg)); grdParams->SetCellRenderer(row, COL_NULL, new wxGridCellBoolRenderer()); grdParams->SetCellValue(row, COL_NULL, wxT("1")); row++; grdParams->SetRowLabelValue(row, wxT("+")); } else if (strLabel == wxT("-")) { dbgArgInfo *arg = NULL; grdParams->DeleteRows(row, 1, false); } else return; // Update the row labels ctlGridCellBoolEditor *editor = NULL; dbgArgInfo *arg = NULL, *prev = NULL; wxString strName; int totalRows = grdParams->GetNumberRows(), idx = 0; row = 0; while (row < totalRows) { editor = dynamic_cast( grdParams->GetCellEditor(row, COL_NULL)); arg = editor != NULL ? editor->GetArg() : NULL; strName = grdParams->GetCellValue(row, COL_NAME); if (strName.IsEmpty() && arg) grdParams->SetRowLabelValue(row, wxT("-")); else if (!strName.IsEmpty() && !arg) grdParams->SetRowLabelValue(row, wxT("+")); else { idx++; grdParams->SetRowLabelValue(row, wxString::Format(wxT("%d"), idx)); } row++; } } //////////////////////////////////////////////////////////////////////////////// // SaveSettings() // // Save default values to our .ini file. We save the OID of the most // recent direct-debugging target when close a session. We also save the // value of each argument - if you debug the same target again next time, // loadSettings() will initialize the parameter-values window with the // same parameter values that you entered in this session. // void dlgDirectDbg::SaveSettings() { wxString strName, strExpr, strVal, strNull, strDef, strKey, strValExpr; dbgArgInfo *prev = NULL, *arg = NULL; int row = 0, idx = -1, arrCnt = 1; // Save the current function/procedure/trigger OID settings->WriteLong(wxT("Debugger/Proc/OID"), m_controller->GetTargetInfo()->GetOid()); // Save - whether we need to debug the package constructor settings->WriteBool(wxT("Debugger/Proc/initialize_package_constructor"), (chkPkgInit->IsEnabled() && chkPkgInit->GetValue())); for (; row < grdParams->GetNumberRows(); row++) { ctlGridCellBoolEditor *editor = dynamic_cast( grdParams->GetCellEditor(row, COL_NULL)); arg = editor != NULL ? editor->GetArg() : NULL; // This should be the information for add/remove values for an array if (arg == NULL) { continue; } else if (prev != arg) { idx++; prev = arg; if (arg->IsArray()) { arrCnt = 0; } else { arrCnt = 1; } } else { arrCnt++; } settings->WriteInt(wxString::Format(wxT("Debugger/Proc/%d/ArrCnt"), idx), arrCnt); if ((arrCnt == 0 && arg->IsArray()) || (arrCnt == 1 && !arg->IsArray())) { // Use Default (if available) settings->Write(wxString::Format(wxT("Debugger/Proc/%d/USE_DEF"), idx), grdParams->GetCellValue(row, COL_USE_DEF)); // Use NULL? settings->Write(wxString::Format(wxT("Debugger/Proc/%d/NULL"), idx), grdParams->GetCellValue(row, COL_NULL)); if (arrCnt == 0 && arg->IsArray()) continue; } strKey = wxString::Format(wxT("Debugger/Proc/%d/%d/"), idx, arrCnt - 1); // Use NULL? settings->Write(strKey + wxT("NULL"), grdParams->GetCellValue(row, COL_NULL)); // Is value EXPR? settings->Write(strKey + wxT("EXPR"), grdParams->GetCellValue(row, COL_EXPR)); // Value settings->Write(strKey + wxT("VAL"), grdParams->GetCellValue(row, COL_VALUE)); } // Save the number of arguments settings->WriteLong(wxT("Debugger/Proc/args"), (long)(idx + 1)); } ctlGridCellBoolEditor::ctlGridCellBoolEditor(dbgArgInfo *_arg) : m_arg(_arg) {} void ctlGridCellBoolEditor::BeginEdit(int _row, int _col, wxGrid *_grid) { wxGridCellBoolEditor::BeginEdit(_row, _col, _grid); wxFocusEvent event (wxEVT_KILL_FOCUS); if (m_control) { m_control->GetEventHandler()->AddPendingEvent(event); } } wxGrid *dlgDirectDbg::GetParamsGrid() { return grdParams; } bool dlgDirectDbg::DebugPkgConstructor() { return chkPkgInit->IsEnabled() && chkPkgInit->GetValue(); } wxGridCellEditor *ctlGridCellBoolEditor::Clone() const { return new ctlGridCellBoolEditor(m_arg); } void dlgDirectDbg::ResultArgsUpdated(wxCommandEvent &_ev) { SaveSettings(); SavePosition(); if (m_thread) { if (m_thread->IsRunning()) { m_thread->CancelEval(); m_thread->Wait(); } delete m_thread; m_thread = NULL; } if (IsModal()) EndModal(wxID_OK); else Destroy(); } void dlgDirectDbg::ResultArgsUpdateError(wxCommandEvent &_ev) { if (_ev.GetInt() == pgQueryResultEvent::PGQ_CONN_LOST) { if(wxMessageBox( _("Connection to the database server lost!\nDo you want to try to reconnect to the server?"), _("Connection Lost"), wxICON_ERROR | wxICON_QUESTION | wxYES_NO) == wxID_YES) { if (m_thread) { if (m_thread->IsRunning()) { m_thread->CancelEval(); m_thread->Wait(); } delete m_thread; m_thread = NULL; } m_conn->Reconnect(); return; } EndModal(wxID_CANCEL); return; } if (m_thread) { if (m_thread->IsRunning()) { m_thread->CancelEval(); m_thread->Wait(); } delete m_thread; m_thread = NULL; } wxLogError(_ev.GetString()); grdParams->Enable(true); btnDebug->Enable(true); } void dbgArgValueEvaluator::NoticeHandler(void *, const char *) { // Ignore the notices } void *dbgArgValueEvaluator::Entry() { bool argNull, nullVal, useDef; wxString strVal, strValExpr; dbgArgInfo *prev = NULL, *arg = NULL; wxGrid *grd = m_dlg->GetParamsGrid(); m_dlg->m_controller->GetTargetInfo()->DebugPackageConstructor() = m_dlg->DebugPkgConstructor(); m_conn->RegisterNoticeProcessor(dbgArgValueEvaluator::NoticeHandler, NULL); for (int row = 0, idx = 0, arrCnt = 1, arr_idx_bound = 0; row < grd->GetNumberRows() && !m_cancelled; row++) { ctlGridCellBoolEditor *editor = dynamic_cast( grd->GetCellEditor(row, dlgDirectDbg::COL_NULL)); arg = editor != NULL ? editor->GetArg() : NULL; // This should be the information for add/remove values for an array if (arg == NULL) { // prev was an array, can we fetch value for the same if (prev && !prev->Null()) { wxString res = m_conn->ExecuteScalar( wxT("SELECT ARRAY[") + strValExpr + wxT("]::") + prev->GetTypeName(), false); if (m_cancelled) { m_conn->RegisterNoticeProcessor(NULL, NULL); return (void *)NULL; } if (m_conn->GetStatus() == PGCONN_BAD) { wxCommandEvent ev(wxEVT_COMMAND_MENU_SELECTED, RESULT_ID_ARGS_UPDATE_ERROR); ev.SetInt(pgQueryResultEvent::PGQ_CONN_LOST); m_dlg->GetEventHandler()->AddPendingEvent(ev); m_conn->RegisterNoticeProcessor(NULL, NULL); return (void *)NULL; } if (m_conn->GetLastResultStatus() == PGRES_TUPLES_OK) { prev->Value() = res; } else { wxCommandEvent ev(wxEVT_COMMAND_MENU_SELECTED, RESULT_ID_ARGS_UPDATE_ERROR); wxString strName = prev->GetName(); wxString strMsg; if (strName.IsEmpty()) { ev.SetString( wxString::Format( _("The specified value for argument #%d is not valid.\nPlease re-enter the value for it."), idx)); } else { ev.SetString( wxString::Format( _("Specified value for argument '%s' is not valid.\nPlease re-enter the value for it."), strName.c_str())); } ev.SetInt(pgQueryResultEvent::PGQ_RESULT_ERROR); m_dlg->LoadLastCellSetting(row - 1, idx - 1, arr_idx_bound - 1, true); m_dlg->GetEventHandler()->AddPendingEvent(ev); m_conn->RegisterNoticeProcessor(NULL, NULL); return (void *)NULL; } prev = NULL; arr_idx_bound = 0; } continue; } else if (prev != arg) { idx++; argNull = false; nullVal = false; useDef = false; strValExpr = wxEmptyString; } if (prev != arg) { prev = arg; // Use Default (if available) arg->UseDefault() = wxGridCellBoolEditor::IsTrueValue( grd->GetCellValue(row, dlgDirectDbg::COL_USE_DEF)); // NULL? arg->Null() = wxGridCellBoolEditor::IsTrueValue( grd->GetCellValue(row, dlgDirectDbg::COL_NULL)); if (arg->UseDefault()) strValExpr = arg->Default(); if (arg->IsArray()) continue; } // Use NULL? // // Keep this just before 'Use Default' column as - we may have non-empty // value in the 'VAL' column if (!arg->UseDefault() || !arg->Null()) { if (!strValExpr.IsEmpty()) { strValExpr += wxT(", "); } if (wxGridCellBoolEditor::IsTrueValue( grd->GetCellValue(row, dlgDirectDbg::COL_NULL))) { strValExpr += wxT("NULL"); } else if (wxGridCellBoolEditor::IsTrueValue( grd->GetCellValue(row, dlgDirectDbg::COL_EXPR))) { strValExpr += wxT("(") + grd->GetCellValue(row, dlgDirectDbg::COL_VALUE) + wxT(")"); } else { strValExpr += m_conn->qtDbString( grd->GetCellValue(row, dlgDirectDbg::COL_VALUE)); } strValExpr.Append(wxT("::")) .Append(arg->IsArray() ? arg->GetBaseType() : arg->GetTypeName()); if(arg->IsArray()) { arr_idx_bound ++; } } if (!arg->IsArray() && !arg->Null()) { pgSet *set = m_conn->ExecuteSet(wxT("SELECT ") + strValExpr, false); if (m_cancelled) { return (void *)NULL; } if (m_conn->GetStatus() == PGCONN_BAD) { wxCommandEvent ev(wxEVT_COMMAND_MENU_SELECTED, RESULT_ID_ARGS_UPDATE_ERROR); ev.SetString(_("Connection to the database server lost!")); ev.SetInt(pgQueryResultEvent::PGQ_CONN_LOST); m_dlg->GetEventHandler()->AddPendingEvent(ev); m_conn->RegisterNoticeProcessor(NULL, NULL); if (set) delete set; return (void *)NULL; } if (m_conn->GetLastResultStatus() == PGRES_TUPLES_OK && set && set->NumRows() > 0L) { if (set->IsNull(0)) { arg->Null() = true; } else { arg->Null() = false; arg->Value() = set->GetVal(0); } } else { wxString strName = arg->GetName(); if (strName.IsEmpty()) strName = wxString::Format(_("Param#%d"), idx); wxCommandEvent ev(wxEVT_COMMAND_MENU_SELECTED, RESULT_ID_ARGS_UPDATE_ERROR); ev.SetString( wxString::Format(_("Please re-enter the value for argument '%s'."), strName.c_str())); ev.SetInt(pgQueryResultEvent::PGQ_RESULT_ERROR); m_dlg->GetEventHandler()->AddPendingEvent(ev); m_dlg->LoadSettings(); // For scalar variable, always index of array bound is 0. m_dlg->LoadLastCellSetting(row, idx - 1, 0, false); m_conn->RegisterNoticeProcessor(NULL, NULL); return (void *)NULL; } } } wxCommandEvent ev(wxEVT_COMMAND_MENU_SELECTED, RESULT_ID_ARGS_UPDATED); m_dlg->GetEventHandler()->AddPendingEvent(ev); m_conn->RegisterNoticeProcessor(NULL, NULL); return (void *)NULL; } void dbgArgValueEvaluator::CancelEval() { m_cancelled = true; if (m_conn->GetTxStatus() == PGCONN_TXSTATUS_ACTIVE) m_conn->CancelExecution(); } dbgArgValueEvaluator::dbgArgValueEvaluator(pgConn *_conn, dlgDirectDbg *_dlg) : wxThread(wxTHREAD_JOINABLE), m_conn(_conn), m_dlg(_dlg), m_cancelled(false) {}