pgadmin3/frm/frmMainConfig.cpp
lsv 2734a746a9 Migration to wxWidgets 3.1.5
Выполнен переход на новые библтотеки. Устранены проблемы совместимости.
Множество мелких правок.
2021-05-21 11:09:20 +05:00

721 lines
17 KiB
C++

//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// frmMainConfig.cpp - Backend configuration tool
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
#ifdef __WXMSW__
#include <io.h>
#include <fcntl.h>
#endif
#include <wx/imaglist.h>
#include "ctl/ctlMenuToolbar.h"
#include "frm/frmMainConfig.h"
#include "frm/frmMain.h"
#include "dlg/dlgMainConfig.h"
#include "utils/utffile.h"
#include "schema/pgServer.h"
#include "frm/menu.h"
#include "utils/pgfeatures.h"
#include <wx/arrimpl.cpp>
WX_DEFINE_OBJARRAY(pgConfigOrgLineArray);
#define CTL_CFGVIEW 345
BEGIN_EVENT_TABLE(frmMainConfig, frmConfig)
EVT_MENU(MNU_UNDO, frmMainConfig::OnUndo)
EVT_MENU(MNU_CONTENTS, frmMainConfig::OnContents)
EVT_LIST_ITEM_ACTIVATED(CTL_CFGVIEW, frmMainConfig::OnEditSetting)
EVT_LIST_ITEM_SELECTED(CTL_CFGVIEW, frmMainConfig::OnSelectSetting)
END_EVENT_TABLE()
#define BCE_TITLE _("Backend Configuration Editor")
frmMainConfig::frmMainConfig(frmMain *parent, pgServer *server)
: frmConfig(parent, BCE_TITLE, 0)
{
wxString applicationname = appearanceFactory->GetLongAppName() + _(" - Configuration Editor");
if (server)
{
conn = server->CreateConn(wxEmptyString, 0, applicationname);
serverVersionNumber = server->GetVersionNumber();
}
else
serverVersionNumber = wxEmptyString;
InitForm();
Init();
if (conn)
{
if (serverFileName.IsEmpty())
serverFileName = wxT("postgresql.conf");
wxString txt;
txt.Printf(_(" - %s on %s (%s:%d)"),
serverFileName.c_str(), server->GetDescription().c_str(),
server->GetName().c_str(), server->GetPort());
SetTitle(BCE_TITLE + txt);
wxString str;
if (conn->BackendMinimumVersion(10, 0)) {
str = conn->ExecuteScalar(wxT("SELECT pg_read_file('") + serverFileName + wxT("') "));
}
else
str = conn->ExecuteScalar(wxT("SELECT pg_file_read('") + serverFileName + wxT("', 0, ")
wxT("pg_file_length('") + serverFileName + wxT("'))"));
DisplayFile(str);
statusBar->SetStatusText(wxString::Format(_(" Configuration read from %s"), conn->GetHost().c_str()));
}
}
frmMainConfig::frmMainConfig(const wxString &title, const wxString &configFile)
: frmConfig(title + wxT(" - ") + _("Backend Configuration Editor"), configFile)
{
InitForm();
Init();
OpenLastFile();
}
frmMainConfig::~frmMainConfig()
{
options.clear();
categories.clear();
lines.Clear();
}
void frmMainConfig::Init(pgSettingReader *reader)
{
pgSettingItem *item;
do
{
item = reader->GetNextItem();
if (item)
{
if (item->context == pgSettingItem::PGC_INTERNAL)
delete item;
else
{
if (options[item->name])
delete item;
else
{
options[item->name] = item;
wxArrayString *category = categories[item->category];
if (!category)
{
category = new wxArrayString;
categories[item->category] = category;
}
category->Add(item->name);
}
}
}
}
while (item);
editMenu->Enable(MNU_DELETE, false);
toolBar->EnableTool(MNU_DELETE, false);
}
void frmMainConfig::Init()
{
// Cleanup first, in case we're re-initing
options.clear();
categories.clear();
lines.Clear();
pgSettingReader *reader;
if (conn)
{
// read settings from server
reader = new pgSettingDbReader(conn);
}
else
{
// read settings from file. First, use localized file...
reader = new pgSettingFileReader(true);
if (reader->IsValid())
Init(reader);
delete reader;
// ... then add default file
reader = new pgSettingFileReader(false);
}
if (reader->IsValid())
Init(reader);
delete reader;
}
void frmMainConfig::InitForm()
{
appearanceFactory->SetIcons(this);
InitFrame(wxT("frmMainConfig"));
RestorePosition(50, 50, 600, 600, 300, 200);
cfgList = new ctlListView(this, CTL_CFGVIEW, wxDefaultPosition, wxDefaultSize, wxSIMPLE_BORDER);
cfgList->SetImageList(configImageList, wxIMAGE_LIST_SMALL);
cfgList->AddColumn(_("Setting name"), 120);
cfgList->AddColumn(_("Value"), 80);
if (conn)
cfgList->AddColumn(_("Current value"), 80);
cfgList->AddColumn(_("Comment"), 400);
}
void frmMainConfig::OnSelectSetting(wxListEvent &event)
{
// Disable undo because we don't want to undo the wrong line.
editMenu->Enable(MNU_UNDO, false);
toolBar->EnableTool(MNU_UNDO, false);
}
void frmMainConfig::OnEditSetting(wxListEvent &event)
{
wxString name = cfgList->GetText(event.GetIndex());
if (!name.IsEmpty())
{
pgSettingItem *item = options[name];
wxASSERT(item);
dlgMainConfig dlg(this, item);
dlg.Go();
if (item->orgLine && !item->newLine->Differs(item->orgLine))
{
delete item->newLine;
item->newLine = 0;
}
else
{
changed = true;
fileMenu->Enable(MNU_SAVE, true);
editMenu->Enable(MNU_UNDO, true);
toolBar->EnableTool(MNU_SAVE, true);
toolBar->EnableTool(MNU_UNDO, true);
}
UpdateLine(event.GetIndex());
}
}
void frmMainConfig::OnContents(wxCommandEvent &event)
{
DisplayHelp(wxT("index"), HELP_PGADMIN);
}
wxString frmMainConfig::GetHelpPage() const
{
wxString page;
if (page.IsEmpty())
page = wxT("runtime-config");
return page;
}
void frmMainConfig::OnUndo(wxCommandEvent &ev)
{
wxString name = cfgList->GetText(cfgList->GetSelection());
if (!name.IsEmpty())
{
pgSettingItem *item = options[name];
if (item->newLine)
{
delete item->newLine;
item->newLine = 0;
UpdateLine(cfgList->GetSelection());
}
}
}
void frmMainConfig::UpdateLine(int pos)
{
wxString name = cfgList->GetText(pos);
if (!name.IsEmpty())
{
pgSettingItem *item = options[name];
pgConfigLine *line = item->newLine;
if (!line)
line = item->orgLine;
wxString value, comment;
int imgIndex = 0;
if (line)
{
value = line->value;
comment = line->comment;
if (!line->isComment)
imgIndex = 1;
}
cfgList->SetItem(pos, 1, value);
if (conn)
cfgList->SetItem(pos, 3, comment);
else
cfgList->SetItem(pos, 2, comment);
cfgList->SetItemImage(pos, imgIndex, imgIndex);
}
}
void frmMainConfig::WriteFile(pgConn *conn)
{
size_t i;
wxString str;
for (i = 0 ; i < lines.GetCount() ; i++)
str.Append(lines.Item(i).GetNewText() + wxT("\n"));
for (i = 0 ; i < (size_t)cfgList->GetItemCount() ; i++)
{
pgSettingItem *item = options[cfgList->GetText(i)];
if (item && item->newLine && item->newLine->item && !item->orgLine)
str.Append(item->newLine->GetNewText() + wxT("\n"));
}
if (DoWriteFile(str, conn))
{
changed = false;
fileMenu->Enable(MNU_SAVE, false);
editMenu->Enable(MNU_UNDO, false);
toolBar->EnableTool(MNU_SAVE, false);
toolBar->EnableTool(MNU_UNDO, false);
size_t i;
for (i = 0 ; i < (size_t)cfgList->GetItemCount() ; i++)
{
pgSettingItem *item = options[cfgList->GetText(i)];
if (item && item->newLine)
{
if (!item->orgLine)
{
item->orgLine = new pgConfigOrgLine(item->newLine);
lines.Add(item->orgLine);
item->orgLine->item = item;
}
else
{
item->orgLine->comment = item->newLine->comment;
item->orgLine->isComment = item->newLine->isComment;
item->orgLine->value = item->newLine->value;
item->orgLine->text = item->orgLine->GetNewText();
}
}
}
for (i = 0 ; i < lines.GetCount() ; i++)
{
pgConfigOrgLine &line = lines.Item(i);
if (line.item && line.item->newLine)
{
line.text = line.GetNewText();
delete line.item->newLine;
line.item->newLine = 0;
}
}
}
}
void frmMainConfig::DisplayFile(const wxString &str)
{
Init();
filetype = wxTextFileType_Unix;
wxStringTokenizer strtok;
wxArrayString *unknownCategory = 0;
if (str.Find('\r') >= 0)
{
if (str.Find(wxT("\n\r")) >= 0 || str.Find(wxT("\r\n")))
filetype = wxTextFileType_Dos;
else
filetype = wxTextFileType_Mac;
strtok.SetString(wxTextBuffer::Translate(str, wxTextFileType_Unix), wxT("\n"), wxTOKEN_RET_EMPTY);
}
else
strtok.SetString(str, wxT("\n"), wxTOKEN_RET_EMPTY);
while (strtok.HasMoreTokens())
{
pgConfigOrgLine *line = new pgConfigOrgLine(strtok.GetNextToken());
lines.Add(line);
// identify option
bool isComment = false;
const wxChar *p = line->text.c_str();
// identify keywords
while (*p && wxStrchr(wxT("# \n"), *p))
{
if (*p == '#')
isComment = true;
p++;
}
if (!*p)
isComment = true;
line->isComment = isComment;
const wxChar *p2 = p;
while (*p2 && *p2 != '#' && *p2 != ' ' && *p2 != '\t' && *p2 != '=')
p2++;
if (p2 != p)
{
wxString keyword = line->text.Mid(p - line->text.c_str(), p2 - p);
pgSettingItemHashmap::iterator it = options.find(keyword);
pgSettingItem *item;
if (it == options.end())
{
if (isComment)
continue;
item = new pgSettingItem;
item->name = keyword;
item->category = _("Unknown");
item->short_desc = _("Unknown option");
item->extra_desc = _("This option is present in the configuration file, but not known to the configuration tool.");
item->SetType(wxT("string"));
options[item->name] = item;
if (!unknownCategory)
{
unknownCategory = new wxArrayString;
categories[item->category] = unknownCategory;
}
unknownCategory->Add(item->name);
}
else
item = it->second;
if (!isComment || !item->orgLine || item->orgLine->isComment)
{
line->item = item;
if (item->orgLine)
item->orgLine->item = 0;
item->orgLine = line;
}
while (*p2 && *p2 != '=')
p2++;
p2++; // skip =
while (*p2 && wxStrchr(wxT(" \t"), *p2))
p2++;
wxChar quoteChar = 0;
if (wxStrchr(wxT("'\""), *p2))
quoteChar = *p2++;
const wxChar *p3 = p2;
while (*p3)
{
if (*p3 == quoteChar || (!quoteChar && wxStrchr(wxT(" \t#"), *p3)))
break;
p3++;
}
if (p2 != p3)
{
line->value = line->text.Mid(p2 - line->text.c_str(), p3 - p2);
if (quoteChar)
p3++;
const wxChar *p4 = p3;
while (*p4 && wxStrchr(wxT(" \t#"), *p4))
p4++;
line->commentIndent = line->text.Mid(p3 - line->text.c_str(), p4 - p3);
line->comment = p4;
}
}
}
cfgList->DeleteAllItems();
double versionNum;
bool versionRetVal = serverVersionNumber.ToDouble(&versionNum);
// we want to show options ordered by category/name
// category might be localized, and we want a distinct category ordering
FillList(wxT("listen_addresses")); // Connections and Authentication / Connection Settings
FillList(wxT("authentication_timeout")); // Connections and Authentication / Security and Authentication
FillList(wxT("check_function_bodies")); // Client Connection Defaults / Statement Behaviour
FillList(wxT("lc_messages")); // Client Connection Defaults / Locale and Formatting
if (versionRetVal && versionNum < 8.4)
FillList(wxT("explain_pretty_print")); // Client Connection Defaults / Other Defaults
FillList(wxT("enable_hashjoin")); // Query Tuning / Planner Method Configuration
FillList(wxT("cpu_operator_cost")); // Query Tuning / Planner Cost Constants
if (!conn || !conn->GetIsGreenplum()) // Greenplum doesn't have the Genetic Query Optimizer
FillList(wxT("geqo")); // Query Tuning / Genetic Query Optimizer
FillList(wxT("default_statistics_target")); // Query Tuning / Other Planner Options
FillList(wxT("deadlock_timeout")); // Lock Management
FillList(wxT("shared_buffers")); // Resource Usage / Memory
if (versionRetVal && versionNum < 8.4)
FillList(wxT("max_fsm_pages")); // Resource Usage / Free Space Map
FillList(wxT("bgwriter_delay")); // Resource Usage
FillList(wxT("max_files_per_process")); // Resource Usage / Kernel Resources
FillList(wxT("log_connections")); // Reporting and Logging / What to Log
FillList(wxT("client_min_messages")); // Reporting and Logging / When to Log
FillList(wxT("log_destination")); // Reporting and Logging / Where to Log
FillList(wxT("stats_command_string"), wxT("track_activities")); // Statistics / Query and Index Statistics Collector
FillList(wxT("log_executor_stats")); // Statistics / Monitoring
FillList(wxT("fsync")); // Write-Ahead Log / Settings
//FillList(wxT("checkpoint_segments")); // Write-Ahead Log / Checkpoints
if (versionRetVal && versionNum <= 8.4)
FillList(wxT("add_missing_from")); // Version and Platform Compatibility / Previous PostgreSQL Version
FillList(wxT("transform_null_equals")); // Version and Platform Compatibility / Other Platforms and Clients
if (!conn || !conn->GetIsGreenplum()) // Greenplum doesn't have trace_notify visible
FillList(wxT("trace_notify")); // Developer Options
FillList(wxT("hba_file")); // Ungrouped
// for all we didn't get
while (!categories.empty())
{
pgCategoryHashmap::iterator it = categories.begin();
wxString missed = it->first;
FillList(it->second);
categories.erase(it);
}
}
void frmMainConfig::FillList(const wxString &categoryMember, const wxString &altCategoryMember)
{
pgSettingItem *categoryItem = options[categoryMember];
if (!categoryItem && !altCategoryMember.IsEmpty())
categoryItem = options[altCategoryMember];
wxASSERT_MSG(categoryItem, wxString::Format(wxT("%s/%s"), categoryMember.c_str(), altCategoryMember.c_str()));
if (categoryItem)
{
FillList(categories[categoryItem->category]);
categories.erase(categoryItem->category);
}
}
void frmMainConfig::FillList(wxArrayString *category)
{
if (category)
{
size_t i;
for (i = 0 ; i < category->GetCount() ; i++)
{
pgSettingItem *item = options[category->Item(i)];
wxASSERT(item);
wxString value;
wxString comment;
int imgIndex = 0;
if (item->orgLine)
{
if (!item->orgLine->isComment)
imgIndex = 1;
value = item->orgLine->value;
comment = item->orgLine->comment;
comment.Replace(wxT("\t"), wxT(" "));
}
long pos = cfgList->AppendItem(imgIndex, item->name, value);
if (conn)
{
cfgList->SetItem(pos, 2, item->value);
cfgList->SetItem(pos, 3, comment);
}
else
cfgList->SetItem(pos, 2, comment);
}
delete category;
}
}
enum
{
HINT_LISTEN_ADDRESSES,
HINT_AUTOVACUUM_CFG
};
const wxChar *hintString[] =
{
_("The PostgreSQL server engine is currently configured to listen for local connections only.\nYou might want to check \"listen_addresses\" to enable accessing the server over the network too."),
_("The autovacuum backend process is not running.\nIt is recommended to enable it by setting 'track_counts' and 'autovacuum' to 'on' in PostgreSQL 8.3 and above or 'stats_start_collector', 'stats_row_level' and 'autovacuum' to 'on' in earlier versions.")
};
wxString frmMainConfig::GetHintString()
{
wxArrayInt hints;
size_t i;
int autovacuum = 0;
bool autovacuumPresent = false;
for (i = 0 ; i < (size_t)cfgList->GetItemCount() ; i++)
{
pgSettingItem *item = options[cfgList->GetText(i)];
if (item)
{
wxString name = item->name;
wxString value = item->GetActiveValue();
if (name == wxT("listen_addresses"))
{
if (value.IsEmpty() || value == wxT("localhost"))
hints.Add(HINT_LISTEN_ADDRESSES);
}
else if (name == wxT("autovacuum"))
{
autovacuumPresent = true;
if (StrToBool(value))
autovacuum++;
}
else if (name == wxT("stats_start_collector") || name == wxT("stats_row_level"))
{
if (StrToBool(value))
autovacuum++;
}
else if (name == wxT("track_counts"))
{
// Double increment, because track_counts in 8.3 is worth both previous options.
if (StrToBool(value))
autovacuum = autovacuum + 2;
}
}
}
if (autovacuumPresent && autovacuum < 3)
hints.Add(HINT_AUTOVACUUM_CFG);
wxString str;
for (i = 0 ; i < hints.GetCount() ; i++)
{
if (i)
str.Append(wxT("\n\n"));
str.Append(hintString[hints.Item(i)]);
}
return str;
}
void frmMainConfig::OnOpen(wxCommandEvent &event)
{
if (CheckChanged(true))
return;
#ifdef __WXMSW__
wxFileDialog dlg(this, _("Open configuration file"), lastDir, wxT(""),
_("Configuration files (*.conf)|*.conf|All files (*.*)|*.*"), wxFD_OPEN);
#else
wxFileDialog dlg(this, _("Open configuration file"), lastDir, wxT(""),
_("Configuration files (*.conf)|*.conf|All files (*)|*"), wxFD_OPEN);
#endif
if (dlg.ShowModal() == wxID_OK)
{
Init();
lastFilename = dlg.GetFilename();
lastDir = dlg.GetDirectory();
lastPath = dlg.GetPath();
OpenLastFile();
UpdateRecentFiles();
}
}
mainConfigFactory::mainConfigFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar) : actionFactory(list)
{
mnu->Append(id, wxT("postgresql.conf"), _("Edit general server configuration file."));
}
wxWindow *mainConfigFactory::StartDialog(frmMain *form, pgObject *obj)
{
pgServer *server = obj->GetServer();
if (server)
{
frmConfig *frm = new frmMainConfig(form, server);
frm->Go();
return frm;
}
return 0;
}
bool mainConfigFactory::CheckEnable(pgObject *obj)
{
if (obj)
{
pgServer *server = obj->GetServer();
if (server)
{
pgConn *conn = server->GetConnection();
return conn && server->GetSuperUser() && conn->HasFeature(FEATURE_FILEREAD);
}
}
return false;
}
mainConfigFileFactory::mainConfigFileFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar) : actionFactory(list)
{
mnu->Append(id, _("Open postgresql.conf..."), _("Open configuration editor with postgresql.conf."));
}
wxWindow *mainConfigFileFactory::StartDialog(frmMain *form, pgObject *obj)
{
frmConfig *dlg = new frmMainConfig(form);
dlg->Go();
dlg->DoOpen();
return dlg;
}