pgadmin3/debugger/dlgDirectDbg.cpp
2020-07-07 22:19:12 +05:00

1073 lines
30 KiB
C++

//////////////////////////////////////////////////////////////////////////
//
// 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 <wx/wx.h>
#include <wx/grid.h>
// 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, _("<No default value>"));
// 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<ctlGridCellBoolEditor *>(
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<ctlGridCellBoolEditor *>(
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<ctlGridCellBoolEditor *>(
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<ctlGridCellBoolEditor *>(
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<ctlGridCellBoolEditor *>(
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)
{}