From a8ddbc4999e1f153fa9dc5d45f9bd7e423d0a5ff Mon Sep 17 00:00:00 2001 From: lsv Date: Wed, 14 Jan 2026 17:17:08 +0500 Subject: [PATCH] Fix generate Insert SQL instruction. Copying result lines based on a template. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. При некоторых настройках при генерации Insert инструкций, пустые строки заменялись на null. Этот коммит исправлет проблему. 2. Результаты запросов можно оформить произвольным образом используя шаблоны указанные в однострочных коментариях. Формат шаблона: --@gen:Имя шаблона в контестном меню результата:Тут текст шаблона - cols = @colname@,@colname2,a@\n Для добавлнения перевода строк в вывод можно использовать \n. Коментарии указывается в теле запроса (можно указать перед запросом). Можно указать строку из которой нужно сделать выбрать содержимое: @colname[-1]@ - содержимое колонки colname предыдущей строки(относительная адресация). @colname[0]@ - содержимое колонки colname 1 строки (или выделенного диапазона строк). Адресация с начинается с 0. Это абсолютная адресация строк. @colname2,a@ - Указание что результат нужно будет выровнять. Флаг "а" глобальный его можно указать у любой колонки. Флаги указываются в самом конце определения колонки после запятой. Пример: @col1[-1],a@ Если перечень имен колонок запроса не совпадает со списком полей шаблона то шаблон не будет добавлен в контекстное меню Generate. Полученный текст копируется в буфер обмена. При генерации текста используются настройки "Вид кавычек" и "Что брать в кавычки". Шаблоны сохраняются перед выполнением SQL команды и после редактирования шаблона запрос нужно выполнить повторно. --- .vscode/settings.json | 3 +- ctl/ctlSQLGrid.cpp | 16 +++- ctl/ctlSQLResult.cpp | 174 ++++++++++++++++++++++++++++++++++++- frm/frmQuery.cpp | 39 +++++++++ include/ctl/ctlSQLGrid.h | 3 +- include/ctl/ctlSQLResult.h | 3 + include/frm/frmQuery.h | 3 + include/frm/menu.h | 4 +- 8 files changed, 235 insertions(+), 10 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 8e13da8..40580af 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -9,7 +9,8 @@ "new": "cpp", "ostream": "cpp", "sstream": "cpp", - "ranges": "cpp" + "ranges": "cpp", + "optional": "cpp" }, "cmake.configureArgs": [ diff --git a/ctl/ctlSQLGrid.cpp b/ctl/ctlSQLGrid.cpp index 8b248cf..b260c68 100644 --- a/ctl/ctlSQLGrid.cpp +++ b/ctl/ctlSQLGrid.cpp @@ -284,6 +284,7 @@ wxString ctlSQLGrid::GetExportLine(int row, wxArrayInt cols) if (GetNumberCols() == 0 || GetRowSize(row) == 0) return str; + sqlResultTable *t=(sqlResultTable *)GetTable(); wxString colsep = settings->GetCopyColSeparator(); if (generatesql == 2 || generatesql == 1) colsep = wxT(","); if (generatesql == 3) colsep = wxT(" and "); @@ -297,7 +298,14 @@ wxString ctlSQLGrid::GetExportLine(int row, wxArrayInt cols) str.Append(colsep); if (col > 0) head.Append(colsep); head = head + GetColumnName(cols[col]); - wxString text = GetCellValue(row, cols[col]); + wxString text; + bool isnull=false; + if ( t && generatesql > 0) + { + // only insert , in_list and where list + text = t->GetValueWithNull( row, cols[col] , &isnull ); + } else text = GetCellValue(row, cols[col]); + wxString cname = GetColumnName(cols[col]); bool needQuote = false; if (settings->GetCopyQuoting() == 1) @@ -307,12 +315,12 @@ wxString ctlSQLGrid::GetExportLine(int row, wxArrayInt cols) else if (settings->GetCopyQuoting() == 2) /* Quote everything */ needQuote = true; - if (text.Length() == 0 && generatesql > 0) { needQuote = false; } + if (isnull && generatesql > 0) { needQuote = false; } else if (generatesql > 0) needQuote = IsColText(cols[col]); if (generatesql > 0) { - if (text.Length() != 0) { + if (!isnull) { text.Replace(wxT("'"), wxT("''")); } else @@ -320,7 +328,7 @@ wxString ctlSQLGrid::GetExportLine(int row, wxArrayInt cols) } if (generatesql == 3) { - if (text == "null") + if (isnull) str.Append(cname).Append(" is "); else str.Append(cname).Append("="); diff --git a/ctl/ctlSQLResult.cpp b/ctl/ctlSQLResult.cpp index 8416713..99ea7c6 100644 --- a/ctl/ctlSQLResult.cpp +++ b/ctl/ctlSQLResult.cpp @@ -23,7 +23,7 @@ #include "frm/mathplot.h" #include "frm/frmPlot.h" #include "ctl/SourceViewDialog.h" - +#include "utils/align/AlignWrap.h" ctlSQLResult::ctlSQLResult(wxWindow *parent, pgConn *_conn, wxWindowID id, const wxPoint &pos, const wxSize &size) : ctlSQLGrid(parent, id, pos, size) @@ -339,6 +339,160 @@ wxString ctlSQLResult::OnGetItemText(long item, long col) const } return wxEmptyString; } +struct type_temp_flag { +uint8_t colvalue:1; +uint8_t colname:1; +uint8_t indexrow:1; +}; +struct ElementTempl { + wxString txt; + type_temp_flag flags = {0}; + int column=-1; + int row=-1; +}; + +wxString ctlSQLResult::GenerateTemplate(wxString &templ,int action) +{ + std::vector tmplvector; + wxString rez; + int qt=settings->GetCopyQuoting(); + wxString qtsimbol = settings->GetCopyQuoteChar(); + wxString qtsimbol2=qtsimbol+qtsimbol; +// Parse and prepare template string + wxChar c; + int isalign=false; + int len=templ.Len(); + int pos=0; + ElementTempl e; + bool isvar=false; + wxString col; + while (pos0) { + long rr=StrToLong(strrow); + e.row=rr; + e.flags.indexrow=true; + } + continue; + } + if (c=='@') { + isvar=false; + int idx=colNames.Index(col); + if (idx==wxNOT_FOUND) { + if (action == 0) wxMessageBox(wxString::Format("Not found col name %s in result query.",col)); + return wxEmptyString; + } + e.column=idx; + tmplvector.push_back(e); + e.flags={0}; + } else + col.Append(c); + continue; + } + if (c!='@') { + e.txt.Append(c); + } else { + isvar=true; + tmplvector.push_back(e); + e.txt.Clear(); + col.Clear(); + e.flags.colvalue=true; + } + } + if (isvar ) + { + if (action == 0) wxMessageBox(wxString::Format("No close col name %s",col)); + return wxEmptyString; + } + else + { + if (e.txt.Len()>0) tmplvector.push_back(e); + } +// + if (action==1) return "OK"; // only correct parse + + wxArrayInt rows = GetSelectedRows(); + size_t numRows = GetNumberRows(); + size_t numRows2 = GetNumberRows(); + if (rows.Count()>0) numRows=rows.Count(); + size_t row=0; + sqlResultTable *t=(sqlResultTable *)GetTable(); + while (row < numRows) + { + int r=0; + if (rows.Count()>0) r=rows[row++]; else r=row++; + wxString strrow; + for (auto e:tmplvector) + { + if (e.flags.colvalue==false) { + strrow.Append(e.txt); + continue; + } + if (e.flags.indexrow) { + int dr=e.row; + if (dr<0) dr=row+dr-1; + if (dr>=0 && dr0) r=rows[r]; else r=r; + } + bool isnull=false; + wxString text = t->GetValueWithNull( r, e.column , &isnull ); + bool needQuote = false; + if (qt == 1) + { + needQuote = IsColText(e.column); + } + else if (qt == 2) + /* Quote everything */ + needQuote = true; + if (needQuote) text.Replace(qtsimbol, qtsimbol2); + if (isnull) strrow.Append("null"); + else + { + if (needQuote) + strrow.Append(qtsimbol); + strrow.Append(text); + if (needQuote) + strrow.Append(qtsimbol); + } + + } + rez.Append(strrow); + } + if (isalign) { + int cfg = AlignWrap::ALL_LINES| AlignWrap::FIRST_LINE; + AlignWrap a; + wxString lineEnd = wxT("\n"); + rez=a.build(rez,cfg,lineEnd); + + } + if (wxTheClipboard->Open()) + { + wxTheClipboard->SetData(new wxTextDataObject(rez)); + wxTheClipboard->Close(); + } + +return rez; +} + wxString ctlSQLResult::CopySelColumnNameType(bool onlyname) { wxString ss = wxEmptyString; @@ -1151,6 +1305,24 @@ wxString sqlResultTable::GetValueFast(int row, int col) } return ""; } +wxString sqlResultTable::GetValueWithNull(int row, int col, bool *isnull) { + wxString s; + if (thread && thread->DataValid()) + { + if (col >= 0) + { + if (use_map) row=maplines[row]; + thread->DataSet()->Locate(row + 1); + wxString s = thread->DataSet()->GetVal(col); + *isnull = thread->DataSet()->IsNull(col); + return s; + } + + } + return ""; + +} + wxString sqlResultTable::GetValue(int row, int col) { if (thread && thread->DataValid()) diff --git a/frm/frmQuery.cpp b/frm/frmQuery.cpp index 60d12c6..52fb04b 100644 --- a/frm/frmQuery.cpp +++ b/frm/frmQuery.cpp @@ -182,6 +182,7 @@ BEGIN_EVENT_TABLE(frmQuery, pgFrame) EVT_MENU(MNU_AUTOEDITOBJECT, frmQuery::OnAutoEditObject) EVT_MENU_RANGE(MNU_FAVOURITES_MANAGE + 1, MNU_FAVOURITES_MANAGE + 999, frmQuery::OnSelectFavourite) EVT_MENU_RANGE(MNU_MACROS_MANAGE + 1, MNU_MACROS_MANAGE + 99, frmQuery::OnMacroInvoke) + EVT_MENU_RANGE(MNU_GENERATESQL + 1, MNU_GENERATESQL + 199, frmQuery::OnGenerateInvoke) EVT_ACTIVATE( frmQuery::OnActivate) EVT_STC_MODIFIED(CTL_SQLQUERY, frmQuery::OnChangeStc) EVT_STC_UPDATEUI(CTL_SQLQUERY, frmQuery::OnPositionStc) @@ -2228,6 +2229,21 @@ void frmQuery::OnLabelRightClick(wxGridEvent &event) xmenu->Append(MNU_COPY_INLIST, _("IN list format copy"), _("Copy In list format.")); xmenu->Append(MNU_COPY_WHERELIST, _("WHERE list format copy"), _("Copy where list format.")); xmenu->Append(MNU_COPY_TABLEHTML, _("Copy table html format"), _("Copy table html format.")); + if (body_template.Count()>0) { +//MNU_GENERATE_TEMPLATE + wxMenu *submenu = new wxMenu(); + //wxString t="begin @obj_id@ end"; + int cnt=0; + for(int i=0;iGenerateTemplate(body_template[i],1); + if (s=="OK") + { + cnt++; + submenu->Append(MNU_GENERATESQL+cnt,title_template[i],body_template[i]); + } + } + if (cnt>0) xmenu->Append(MNU_GENERATESQL,"Generate",submenu); + } xmenu->AppendSeparator(); xmenu->Append(MNU_AUTOCOLSPLOT, _("Draw plot LY(bar) or LXY or XYYY..."), _("Draw plot LY(bar) LXY or XYYY...")); xmenu->Append(MNU_SUMMARY_COL, _("Summary"), _("Summary selected cells.")); @@ -2288,6 +2304,19 @@ void frmQuery::OnCopy_WhereList(wxCommandEvent& ev) SetStatusText(s, STATUSPOS_MSGS); } } + +void frmQuery::OnGenerateInvoke(wxCommandEvent& ev) +{ + // if (currentControl() == sqlResult) + int id=ev.GetId(); + wxMenu* mi = static_cast(ev.GetEventObject()); + wxString templ = mi->GetHelpString(id); + { + wxString s = wxT("Where list format copy buffer."); + s=sqlResult->GenerateTemplate(templ,0); + SetStatusText(s, STATUSPOS_MSGS); + } +} void frmQuery::OnCopy_TableToHtml(wxCommandEvent& ev) { // if (currentControl() == sqlResult) @@ -3063,6 +3092,8 @@ void frmQuery::OnExecute(wxCommandEvent &event) FSQL::view_item v; bool isddm = false; std::vector v_list; + body_template.Clear(); + title_template.Clear(); while (f.GetNextPositionSqlParse() < query.length() && f.ParseSql(0) >= 0) { // many querys int i = 0; @@ -3075,6 +3106,14 @@ void frmQuery::OnExecute(wxCommandEvent &event) } n_check++; } + if (v.type == FSQL::type_item::comment) { + wxString kw = v.txt; + if (kw.Find("@gen:")>0) { + wxString t=kw.AfterFirst(':'); + body_template.Add(t.AfterFirst(':')); + title_template.Add(t.BeforeFirst(':')); + } + } // check bindarg if (n_check == 1 && v.type == FSQL::type_item::bindarg) { if (!isddm) break; diff --git a/include/ctl/ctlSQLGrid.h b/include/ctl/ctlSQLGrid.h index 1a3fe7c..b754c41 100644 --- a/include/ctl/ctlSQLGrid.h +++ b/include/ctl/ctlSQLGrid.h @@ -34,7 +34,6 @@ public: } int CopyTableToHtml(wxString htmlquery); int Copy(int gensql); - virtual bool CheckRowPresent(int row) { return true; @@ -62,7 +61,7 @@ public: wxString GetRowLabelValue(int row); void SetRowGroup(int row); GroupRows* grp; - int generatesql; // 0 -���, 1 - insert , 2 - in_list + int generatesql; // 0 -copy, 1 - insert , 2 - in_list, 3 - where list wxString sqlquerytext; // Fast searh wxString searchStr; diff --git a/include/ctl/ctlSQLResult.h b/include/ctl/ctlSQLResult.h index 428bfe1..5f47436 100644 --- a/include/ctl/ctlSQLResult.h +++ b/include/ctl/ctlSQLResult.h @@ -52,6 +52,8 @@ public: wxString AutoColsPlot(int flags,frmQuery *parent); wxString CheckSelColumnDate(); wxString CopySelColumnNameType(bool onlyname); + wxString GenerateTemplate(wxString &templ,int action); + void ClearFilter(); bool IsColText(int col); bool hasRowNumber() @@ -95,6 +97,7 @@ public: sqlResultTable(); wxString GetValue(int row, int col); wxString GetValueFast(int row, int col); + wxString GetValueWithNull(int row, int col, bool *isnull); wxString GetRowLabelValue( int row ) ; int GetNumberRows(); int GetNumberCols(); diff --git a/include/frm/frmQuery.h b/include/frm/frmQuery.h index 1aee9d3..47df9a1 100644 --- a/include/frm/frmQuery.h +++ b/include/frm/frmQuery.h @@ -127,6 +127,7 @@ private: ctlAuiNotebook *outputPane; wxString outputPaneInfo; ctlSQLResult *sqlResult; + wxArrayString body_template,title_template; // @gen:title menu:body generator @column_name@ end #define MAX_RESULT_COUNT 10 ctlSQLResult *ctlSQL[MAX_RESULT_COUNT]; ctlSQLBox *ctlSBox[MAX_RESULT_COUNT]; @@ -220,6 +221,7 @@ private: void OnCopy_InList(wxCommandEvent& event); void OnCopy_WhereList(wxCommandEvent& event); void OnCopy_TableToHtml(wxCommandEvent& ev); + void OnGenerateTemplate(wxCommandEvent& ev); void OnClear_Filter(wxCommandEvent &event); void OnCopy_NameTypeCols(wxCommandEvent& ev); void OnCheck_Column_Date(wxCommandEvent& ev); @@ -288,6 +290,7 @@ private: void OnAutoEditObject(wxCommandEvent &event); void SetEOLModeDisplay(int mode); void OnMacroInvoke(wxCommandEvent &event); + void OnGenerateInvoke(wxCommandEvent &event); void OnMacroManage(wxCommandEvent &event); void OnAutoReplaceManage(wxCommandEvent &event); void LoadQueries(); diff --git a/include/frm/menu.h b/include/frm/menu.h index 4206f4a..66b561e 100644 --- a/include/frm/menu.h +++ b/include/frm/menu.h @@ -166,8 +166,8 @@ enum // This is a dummy menu item MNU_DUMMY = QUERY_COMPLETE + 1000, - //Menu Test - MNU_GENERATESQL + //Menu Generate + MNU_GENERATESQL = MNU_DUMMY + 200 }; #endif