pgadmin3/frm/frmExport.cpp
lsv 920ad9c0e6 The start time query is added to the history tab. Fix execute to file.
1. Добавлено время начала выполнения каждого запроса во вкладке "История".
2. Исправлено поведения команды "Выполнить в файл". Теперь она работает также как и "Выполнить",
   но результаты идут в файл.
   Следует учитывать что в этом случае Флажок "Формат для XLS" не работает.
2026-06-30 15:41:47 +05:00

610 lines
17 KiB
C++

//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// frmExport.cpp - The export file dialogue
//
//////////////////////////////////////////////////////////////////////////
// App headers
#include "pgAdmin3.h"
#include <wx/file.h>
#include "frm/frmExport.h"
#include "utils/sysSettings.h"
#include "utils/misc.h"
#include "ctl/ctlSQLResult.h"
#include <wx/wx.h>
#include <wx/clipbrd.h>
#ifdef WIN32
#include <wx/msw/ole/automtn.h>
#endif
#define txtFilename CTRL_TEXT("txtFilename")
#define btnOK CTRL_BUTTON("wxID_OK")
#define rbUnicode CTRL_RADIOBUTTON("rbUnicode")
#define rbLocal CTRL_RADIOBUTTON("rbLocal")
#define rbCRLF CTRL_RADIOBUTTON("rbCRLF")
#define rbLF CTRL_RADIOBUTTON("rbLF")
#define rbQuoteStrings CTRL_RADIOBUTTON("rbQuoteStrings")
#define rbQuoteAll CTRL_RADIOBUTTON("rbQuoteAll")
#define rbQuoteNone CTRL_RADIOBUTTON("rbQuoteNone")
#define chkColnames CTRL_CHECKBOX("chkColnames")
#define chkXlsCopy CTRL_CHECKBOX("chkXlsCopy")
#define cbColSeparator CTRL_COMBOBOX("cbColSeparator")
#define cbQuoteChar CTRL_COMBOBOX("cbQuoteChar")
BEGIN_EVENT_TABLE(frmExport, pgDialog)
EVT_TEXT(XRCID("txtFilename"), frmExport::OnChange)
EVT_RADIOBUTTON(XRCID("rbQuoteNone"), frmExport::OnChange)
EVT_RADIOBUTTON(XRCID("rbQuoteStrings"), frmExport::OnChange)
EVT_RADIOBUTTON(XRCID("rbQuoteAll"), frmExport::OnChange)
EVT_BUTTON(XRCID("btnFilename"), frmExport::OnBrowseFile)
EVT_BUTTON(wxID_HELP, frmExport::OnHelp)
EVT_BUTTON(wxID_OK, frmExport::OnOK)
EVT_BUTTON(wxID_CANCEL, frmExport::OnCancel)
END_EVENT_TABLE()
class XMLDataFormat : public wxDataFormat
{
public:
XMLDataFormat() : wxDataFormat(wxT("XML Spreadsheet")) {}
};
class XMLDataObject : public wxDataObjectSimple
{
public:
XMLDataObject(const wxString& xmlstring = wxEmptyString) : wxDataObjectSimple(), m_XMLString(xmlstring)
{
SetFormat(XMLDataFormat());
}
size_t GetLength() const { return m_XMLString.Len()*2 + 1; }
wxString GetXML() const { return m_XMLString; }
void SetXML(const wxString& xml) { m_XMLString = xml; }
size_t GetDataSize() const
{
return GetLength();
}
bool GetDataHere(void *buf) const
{
//char* c = _strdup(m_XMLString.c_str()); not needed as per suggestion
//memcpy(buf, m_XMLString.c_str(), m_XMLString.Len()*2+1);
memcpy(buf,m_XMLString.mb_str(wxConvUTF8),m_XMLString.Len()*2+1);
return true;
}
bool SetData(size_t len, const void* buf)
{
//char* c = new char[len + 1]; not needed as per suggestion
//memcpy(c, buf, len); not needed as per suggestion
m_XMLString = wxString::FromUTF8((const char*)buf); //changed as per suggestion
//delete c;
return true;
}
virtual size_t GetDataSize(const wxDataFormat&) const
{
return GetDataSize();
}
virtual bool GetDataHere(const wxDataFormat&, void *buf) const
{
return GetDataHere(buf);
}
virtual bool SetData(const wxDataFormat&, size_t len, const void *buf)
{
return SetData(len, buf);
}
private:
wxString m_XMLString;
};
frmExport::frmExport(wxWindow *p)
{
parent = p;
SetFont(settings->GetSystemFont());
LoadResource(p, wxT("frmExport"));
RestorePosition();
// Icon
appearanceFactory->SetIcons(this);
cbQuoteChar->Disable();
btnOK->Disable();
bool uc = settings->GetExportUnicode();
rbUnicode->SetValue(uc);
rbLocal->SetValue(!uc);
bool isCrLf = settings->GetExportRowSeparator() == wxT("\r\n");
rbCRLF->SetValue(isCrLf);
rbLF->SetValue(!isCrLf);
int qt = settings->GetExportQuoting();
rbQuoteNone->SetValue(qt == 0);
rbQuoteStrings->SetValue(qt == 1);
rbQuoteAll->SetValue(qt == 2);
cbColSeparator->SetValue(settings->GetExportColSeparator());
cbQuoteChar->SetValue(settings->GetExportQuoteChar());
wxString val;
settings->Read(wxT("Export/LastFile"), &val, wxEmptyString);
txtFilename->SetValue(val);
ctlSQLResult* ctrl= dynamic_cast<ctlSQLResult*>(parent);
if (!ctrl) {
chkXlsCopy->SetValue(false);
chkXlsCopy->Enable(false);
}
wxCommandEvent ev;
OnChange(ev);
}
frmExport::~frmExport()
{
SavePosition();
}
void frmExport::OnHelp(wxCommandEvent &ev)
{
DisplayHelp(wxT("export"), HELP_PGADMIN);
}
void frmExport::OnChange(wxCommandEvent &ev)
{
cbQuoteChar->Enable(rbQuoteStrings->GetValue() || rbQuoteAll->GetValue());
btnOK->Enable(!txtFilename->GetValue().IsEmpty() && !cbColSeparator->GetValue().IsEmpty());
}
void frmExport::OnOK(wxCommandEvent &ev)
{
settings->SetExportUnicode(rbUnicode->GetValue());
settings->SetExportRowSeparator(rbCRLF->GetValue() ? wxT("\r\n") : wxT("\n"));
settings->SetExportColSeparator(cbColSeparator->GetValue());
if (rbQuoteAll->GetValue())
settings->SetExportQuoting(2);
else if (rbQuoteStrings->GetValue())
settings->SetExportQuoting(1);
else
settings->SetExportQuoting(0);
settings->SetExportQuoteChar(cbQuoteChar->GetValue());
settings->Write(wxT("Export/LastFile"), txtFilename->GetValue());
if (IsModal())
EndModal(wxID_OK);
else
Destroy();
}
wxString frmExport::InitXml(int cols, int rows)
{
wxString strc;
wxString strr;
strc.Printf(wxT("%d"),cols);
strr.Printf(wxT("%d"),rows);
wxString s= wxT("<?xml version=\"1.0\"?>\n")
wxT("<?mso-application progid=\"Excel.Sheet\"?>\n")
wxT("<Workbook xmlns=\"urn:schemas-microsoft-com:office:spreadsheet\"\n")
wxT(" xmlns:o=\"urn:schemas-microsoft-com:office:office\"\n")
wxT("xmlns:x=\"urn:schemas-microsoft-com:office:excel\"\n")
wxT("xmlns:ss=\"urn:schemas-microsoft-com:office:spreadsheet\"\n")
wxT("xmlns:html=\"http://www.w3.org/TR/REC-html40\">\n")
wxT("<Styles>\n")
wxT("<Style ss:ID=\"def\">\n")
wxT("<Borders>\n")
wxT("<Border ss:Position=\"Bottom\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"/>\n")
wxT("<Border ss:Position=\"Left\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"/>\n")
wxT("<Border ss:Position=\"Right\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"/>\n")
wxT("<Border ss:Position=\"Top\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"/>\n")
wxT("</Borders></Style>\n")
wxT("<Style ss:ID=\"hdr\">\n")
wxT("<Borders>\n")
wxT("<Border ss:Position=\"Bottom\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"/>\n")
wxT("<Border ss:Position=\"Left\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"/>\n")
wxT("<Border ss:Position=\"Right\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"/>\n")
wxT("<Border ss:Position=\"Top\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"/>\n")
wxT("</Borders><Font ss:FontName=\"Arial Cyr\" x:CharSet=\"204\" ss:Bold=\"1\"/></Style>\n")
wxT("<Style ss:ID=\"dt\">\n")
wxT("<Borders>\n")
wxT("<Border ss:Position=\"Bottom\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"/>\n")
wxT("<Border ss:Position=\"Left\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"/>\n")
wxT("<Border ss:Position=\"Right\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"/>\n")
wxT("<Border ss:Position=\"Top\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"/>\n")
wxT("</Borders><NumberFormat ss:Format=\"General Date\"/></Style>\n")
wxT("<Style ss:ID=\"null\">\n")
wxT("<Borders>\n")
wxT("<Border ss:Position=\"Bottom\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"/>\n")
wxT("<Border ss:Position=\"Left\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"/>\n")
wxT("<Border ss:Position=\"Right\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"/>\n")
wxT("<Border ss:Position=\"Top\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"/>\n")
wxT("</Borders><Interior ss:Color=\"#CCFFCC\" ss:Pattern=\"Solid\"/></Style>\n")
wxT("</Styles>\n")
wxT("<Worksheet ss:Name=\"data\">\n")
wxT("<Table ss:ExpandedColumnCount=\"")+strc+wxT("\" ss:ExpandedRowCount=\"")+strr+wxT("\">\n")
wxT("\n")
;
for (int c = 0 ; c < cols ; c++)
{
s+=wxT("<Column ss:AutoFitWidth=\"1\"/>\n");
}
return s;
}
wxString textEscapeXml(wxString &text)
{
wxString::const_iterator i;
wxString xmltext=wxEmptyString;
for ( i = text.begin(); i != text.end(); ++i )
{
#if wxCHECK_VERSION(3, 0, 0)
char c=*i;
switch (c)
#else
switch (*i)
#endif
{
case '&': xmltext+= wxT("&amp;"); break;
case '\'': xmltext+= wxT("&apos;"); break;
case '"': xmltext+= wxT("&quot;"); break;
case '<': xmltext+= wxT("&lt;"); break;
case '>': xmltext+= wxT("&gt;"); break;
case 13: break;
case 10: xmltext+= wxT("&#10;"); break;
default: xmltext+= *i; break;
}
}
return xmltext;
}
bool frmExport::ExportXls(ctlSQLResult *grid)
{
int colCount, rowCount;
int col;
wxString line;
long skipped = 0;
wxWX2MBbuf buf;
colCount = grid->GetNumberCols();
rowCount = grid->NumRows();
line=InitXml(colCount,rowCount+1);
line += wxT("<Row>\n");
for (col = 0 ; col < colCount ; col++)
{
line += wxT("<Cell ss:StyleID=\"hdr\"><Data ss:Type=\"String\">")+grid->OnGetItemText(-1, col + 1).BeforeFirst('\n')+wxT("</Data></Cell>");
//wxString adr(wxT(""));
//adr.Printf(wxT("%s%d"), name,1);
//rng[0] = wxVariant(adr);
//excelObject.GetObject(range, wxT("Range"), 1,rng);
//range.CallMethod(wxT("Activate"));
//excelObject.PutProperty(wxT("ActiveCell.Font.Bold"), true);
//excelObject.PutProperty(wxT("ActiveCell.Value"), grid->OnGetItemText(-1, col + 1).BeforeFirst('\n'));
//name[0]++;
}
line += wxT("</Row>\n");
int row;
wxString text;
wxString type;
OID typOid;
wxString fn=txtFilename->GetValue().BeforeLast('.');
if (fn.IsEmpty()) fn=txtFilename->GetValue()+wxT(".xml");
else fn+=wxT(".xml");
wxFile file(fn, wxFile::write);
if (!file.IsOpened())
{
wxLogError(__("Failed to open file %s."), fn.c_str());
return false;
}
file.Write(line, wxConvUTF8);
wxString xmltext=wxEmptyString;
for (row = 0 ; row < rowCount ; row++)
{
if (grid->GetRowSize(row)==0) continue;
line = wxT("<Row>\n");
for (col = 0 ; col < colCount ; col++)
{
wxString style(wxT("null"));
//adr.Printf(wxT("%s%d"), name,row+2);
//rng[0] = wxVariant(adr);
//excelObject.GetObject(range, wxT("Range"), 1,rng);
//range.CallMethod(wxT("Activate"));
text=grid->OnGetItemText(row, col + 1);
xmltext=wxEmptyString;
typOid = grid->colTypClasses[col];
if (!text.IsEmpty()) {
style=wxT("def");
xmltext=textEscapeXml(text);
// find out if string
switch (typOid)
{
case PGTYPCLASS_NUMERIC:
//type=wxT("0.00");
if (xmltext.IsNumber())
xmltext=wxT("<Cell ss:StyleID=\"")+style+wxT("\"><Data ss:Type=\"Number\">")+xmltext+wxT("</Data></Cell>");
else
xmltext=wxT("<Cell ss:StyleID=\"")+style+wxT("\"><Data ss:Type=\"String\">")+xmltext+wxT("</Data></Cell>");
break;
case PGTYPCLASS_DATE:
//
xmltext=xmltext.BeforeFirst('+');
if (xmltext.Replace(wxT(" "),wxT("T"))==0) {
xmltext=wxT("<Cell ss:StyleID=\"")+style+wxT("\"><Data ss:Type=\"String\">")+xmltext+wxT("</Data></Cell>");
} else
{
style=wxT("dt");
xmltext=wxT("<Cell ss:StyleID=\"")+style+wxT("\"><Data ss:Type=\"DateTime\">")+xmltext+wxT("</Data></Cell>");
}
break;
case PGTYPCLASS_BOOL:
default:
//type=wxT("@");
xmltext=wxT("<Cell ss:StyleID=\"")+style+wxT("\"><Data ss:Type=\"String\">")+xmltext+wxT("</Data></Cell>");
break;
}
} else xmltext=wxT("<Cell ss:StyleID=\"null\"/>");
// excelObject.PutProperty(wxT("ActiveCell.Borders.LineStyle"), 1);
// excelObject.PutProperty(wxT("ActiveCell.NumberFormat"),type);
// excelObject.PutProperty(wxT("ActiveCell.Value"), text);
// name[0]++;
line += xmltext+wxT("\n");
}
line += wxT("</Row>\n");
file.Write(line, wxConvUTF8);
}
line = wxT("</Table></Worksheet>");
file.Write(line, wxConvUTF8);
text=textEscapeXml(grid->sqlquerytext);
line= wxT("<Worksheet ss:Name=\"SQL text\">\n")
wxT("<Table ss:ExpandedColumnCount=\"1\" ss:ExpandedRowCount=\"1\">\n")
wxT("<Column ss:Width=\"300\"/><Row ss:AutoFitHeight=\"1\">\n")
wxT("<Cell><Data ss:Type=\"String\">")+text+
wxT("\n</Data></Cell></Row></Table></Worksheet>")
wxT("</Workbook>\n")
;
file.Write(line, wxConvUTF8);
file.Close();
#ifdef __WXMSW__
wxAutomationObject excelObject;
if (excelObject.CreateInstance(wxT("Excel.Application"))) {
//excelObject.GetObject(xlbook,wxT("Workbooks.Add"));
excelObject.CallMethod(wxT("Workbooks.Open"), fn);
excelObject.PutProperty(_T("VISIBLE"), true);
return true;
}
#endif
return false;
}
bool frmExport::Export(pgSet *set)
{
ctlSQLResult *grid = 0;
if (!set)
{
wxLogInfo(wxT("Exporting data from the grid"));
grid = (ctlSQLResult *)parent;
if (chkXlsCopy->GetValue()) {
return ExportXls(grid);
}
}
else
wxLogInfo(wxT("Exporting data from a resultset"));
wxFile file(txtFilename->GetValue(), wxFile::write);
if (!file.IsOpened())
{
wxLogError(__("Failed to open file %s."), txtFilename->GetValue().c_str());
return false;
}
wxString line;
long skipped = 0;
wxWX2MBbuf buf;
int colCount, rowCount;
if (set)
{
colCount = set->NumCols();
rowCount = set->NumRows();
}
else
{
colCount = grid->GetNumberCols();
rowCount = grid->NumRows();
}
int col;
if (chkColnames->GetValue())
{
for (col = 0 ; col < colCount ; col++)
{
if (!col)
line = wxEmptyString;
else
line += cbColSeparator->GetValue();
if (rbQuoteStrings->GetValue() || rbQuoteAll->GetValue())
{
wxString qc = cbQuoteChar->GetValue();
wxString hdr;
if (set)
hdr = set->ColName(col);
else
hdr = grid->OnGetItemText(-1, col + 1).BeforeFirst('\n');
hdr.Replace(qc, qc + qc);
line += qc + hdr + qc;
}
else
{
if (set)
line += set->ColName(col);
else
line += grid->OnGetItemText(-1, col + 1).BeforeFirst('\n');
}
}
if (rbCRLF->GetValue())
line += wxT("\r\n");
else
line += wxT("\n");
if (rbUnicode->GetValue())
file.Write(line, wxConvUTF8);
else
{
buf = line.mb_str(wxConvLibc);
if (!buf)
skipped++;
else
file.Write(line, wxConvLibc);
}
}
wxString text;
OID typOid;
int row;
for (row = 0 ; row < rowCount ; row++)
{
for (col = 0 ; col < colCount ; col++)
{
if (!col)
line = wxEmptyString;
else
line += cbColSeparator->GetValue();
bool needQuote = rbQuoteAll->GetValue();
if (set)
{
text = set->GetVal(col);
typOid = set->ColTypClass(col);
}
else
{
text = grid->OnGetItemText(row, col + 1);
typOid = grid->colTypClasses[col];
}
if (!needQuote && rbQuoteStrings->GetValue())
{
// find out if string
switch (typOid)
{
case PGTYPCLASS_NUMERIC:
case PGTYPCLASS_BOOL:
break;
default:
needQuote = true;
break;
}
}
if (needQuote)
{
wxString qc = cbQuoteChar->GetValue();
text.Replace(qc, qc + qc);
line += qc + text + qc;
}
else
line += text;
}
if (rbCRLF->GetValue())
line += wxT("\r\n");
else
line += wxT("\n");
if (rbUnicode->GetValue())
file.Write(line, wxConvUTF8);
else
{
buf = line.mb_str(wxConvLibc);
if (!buf)
skipped++;
else
file.Write(line, wxConvLibc);
}
if (set)
set->MoveNext();
}
file.Close();
if (skipped)
wxLogError(wxPLURAL(
"Data export incomplete.\n\n%d row contained characters that could not be converted to the local charset.\n\nPlease correct the data or try using UTF8 instead.",
"Data export incomplete.\n\n%d rows contained characters that could not be converted to the local charset.\n\nPlease correct the data or try using UTF8 instead.",
skipped), skipped);
else
wxMessageBox(_("Data export completed successfully."), _("Export data"), wxICON_INFORMATION | wxOK);
return true;
}
void frmExport::OnCancel(wxCommandEvent &ev)
{
if (IsModal())
EndModal(wxID_CANCEL);
else
Destroy();
}
void frmExport::OnBrowseFile(wxCommandEvent &ev)
{
wxString directory;
wxString filename;
if (txtFilename->GetValue().IsEmpty())
directory = wxGetHomeDir();
else
{
directory = wxFileName(txtFilename->GetValue()).GetPath();
filename = wxFileName(txtFilename->GetValue()).GetFullName();
}
#ifdef __WXMSW__
wxFileDialog file(this, _("Select export filename"), directory, filename,
_("CSV files (*.csv)|*.csv|Data files (*.dat)|*.dat|All files (*.*)|*.*"), wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
#else
wxFileDialog file(this, _("Select export filename"), directory, filename,
_("CSV files (*.csv)|*.csv|Data files (*.dat)|*.dat|All files (*)|*"), wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
#endif
if (file.ShowModal() == wxID_OK)
{
txtFilename->SetValue(file.GetPath());
OnChange(ev);
}
}