Add sql formatter

Добавлено встроенное форматирование Sql запросов.
Доработано автодополнение колонок.
Добавлено раскрытие <alias>.* в список колонок.
This commit is contained in:
lsv 2024-06-17 20:31:29 +05:00
parent 2d3f87edaa
commit 282cf08716
5 changed files with 1582 additions and 2 deletions

View file

@ -27,6 +27,7 @@
#include <wx/aui/aui.h>
#include "utils/align/AlignWrap.h"
#include "utils/popuphelp.h"
#include "utils/FormatterSQL.h"
wxString ctlSQLBox::sqlKeywords;
static const wxString s_leftBrace(_T("([{"));
@ -1038,6 +1039,20 @@ wxString ctlSQLBox::ExternalFormat(int typecmd)
return _("" + msgword + "ing complete.");
}
if (typecmd == 0) {
FSQL::FormatterSQL f(processInput);
int rez = f.ParseSql(0);
if (rez >= 0) {
wxRect rr(0, 0, 120, 2000);
wxString processOutput = f.Formating(rr);
if (isSelected)
ReplaceSelection(processOutput);
else
SetText(processOutput);
return _("Formatting Ok");
} else
return wxString::Format("Error parse sql %d",rez);
}
return _("You need to setup a "+msgword+"ing command");
}
@ -1479,8 +1494,8 @@ void ctlSQLBox::OnAutoComplete(wxCommandEvent &rev)
return;
if (m_autocompDisabled)
return;
wxString what = GetCurLine().Left(GetCurrentPos() - PositionFromLine(GetCurrentLine()));;
int pos = GetCurrentPos();
wxString what = GetCurLine().Left(pos - PositionFromLine(GetCurrentLine()));;
int spaceidx = what.Find(' ', true);
char *tab_ret;
@ -1490,6 +1505,77 @@ void ctlSQLBox::OnAutoComplete(wxCommandEvent &rev)
tab_ret = tab_complete(what.mb_str(wxConvUTF8), spaceidx + 1, what.Len() + 1, m_database);
wxString wxRet;
if ((tab_ret == NULL || tab_ret[0] == '\0')&&(what.Right(1)>' ')){
long p = SelectQuery(pos);
int s = p >> 16;
wxString sql = GetTextRange(s, p & 65535);
FSQL::FormatterSQL f(sql);
int rez = f.ParseSql(0);
if (rez >= 0) {
// ok query
f.BuildAutoComplite(0, 0);
int sqlPos = CountCharacters(s,pos);
int ItemPos=f.GetIndexItemNextSqlPosition(sqlPos);
FSQL::view_item vi;
f.GetItem(ItemPos, vi);
if (vi.type == FSQL::spaces) {
ItemPos--;
f.GetItem(ItemPos, vi);
}
wxString field;
bool ast = false;
if (vi.type == FSQL::separation) {
ast=vi.txt == ".*";
while (f.GetItem(--ItemPos, vi)) {
if (vi.type == FSQL::identifier || vi.type == FSQL::name) {
field = vi.txt;
break;
}
if (vi.srcpos != -1) break;
};
}
else if (vi.type == FSQL::identifier) {
field = vi.txt;
}
if (!field.IsEmpty()) {
wxString lf;
wxString tabn;
wxString r=f.GetColsList(field, lf, tabn);
int l2 = 0;
wxString flt = "";
wxString prev=tabn;
wxString fld = field.AfterFirst('.');
if (fld.Len()>0) l2 = fld.Len();
if (tabn.Len() > 0 && r.Len()==0) {
if (!field.AfterFirst('.').IsEmpty()) flt = " and a.attname ~* " + qtConnString(fld);
wxString sch;
if (tabn.Find('.') != -1) sch = tabn.BeforeFirst('.');
if (sch.Len() > 0) {
tabn = tabn.AfterFirst('.');
if (sch[0] == '"') sch.Replace("\"", ""); else sch = sch.Lower();
sch = " and relnamespace =" + qtConnString(sch) + "::regnamespace";
}
if (tabn[0] == '"') tabn.Replace("\"", ""); else tabn = tabn.Lower();
wxString sql2 = wxT("select string_agg(a.attname,E'\t' ORDER BY attname) from pg_attribute a where a.attrelid = (select oid from pg_class p where relname=") + qtConnString(tabn) + sch
+ wxT(") and a.attisdropped IS FALSE and a.attnum>=0 ") + flt
+ wxT("");
//pgSet *res = m_database->ExecuteSet(sql);
r = m_database->ExecuteScalar(sql2);
}
if (ast) {
// replace name.*
r.Replace("\t", ", "+ field +".");
int npos = pos - 2 - field.Len();
DeleteRange(npos, field.Len() + 2);
InsertText(npos, field + "."+r);
} else
AutoCompShow(l2, r);
}
return;
}
int l=0;
wxString alias;
wxString fld,tmp;

View file

@ -12,6 +12,7 @@
#ifndef PGADMIN3_H
#define PGADMIN3_H
#include "../utils/diff_match_patch.h"
#include "utils/FormatterSQL.h"
// wxWindows headers
#include <wx/wx.h>

View file

@ -0,0 +1,178 @@
#pragma once
#include <wx/wx.h>
namespace FSQL {
/// <summary>
/// Formatter SQL
/// </summary>
enum type_item {
unknown
, spaces
, keyword
, literal
, separation
, bracket
, identifier
, name
, numeric
, bindarg
, comment
};
struct view_item {
type_item type = unknown;
int srcpos = -1; // start position sql text -1 needed ignore
int width = 0; //
int height = 0;
int endlevel = -1; // end bracet or home
bool newline = false;
int x = 0, y = 0, flags = 0;
wxString txt;
};
enum f_key {
none,
new_line_align_no_pad = 1,
new_line_align_next = 2,
newLineComma = 4,
newLineXPrevValue = 8,
isCASE = 16,
special = 32,
newLineBracet = 64,
HorizontalAlign = 128,
isFUNCTION = 256,
end_from = 256 << 1,
limit = 256 << 2,
fetch = 256 << 3,
for_up = 256 << 4,
fix_position_new_line = 256 << 5,
};
const struct KEYWORDEntity
{
const char* name;
int len; // == strlen(name)
int flags;
} keyEntities[] =
{
{ "as", 2, none },
{ "is", 2, none },
{ "by", 2, none },
{ "in", 2, none },
{ "on", 2, HorizontalAlign },
{ "of", 2, none },
{ "or", 2, newLineXPrevValue },
{ "and", 3, newLineXPrevValue},
{ "not", 3, none},
{ "asc", 3, none},
{ "set", 3, none},
{ "all", 3, none},
{ "end", 3, special},
{ "desc", 4, none },
{ "like", 4, none},
{ "null", 4, none},
{ "from", 4, new_line_align_no_pad },
{ "case", 4, isCASE },
{ "when", 4, new_line_align_no_pad },
{ "else", 4, new_line_align_no_pad },
{ "with", 4, special },
{ "join", 4, new_line_align_next },
{ "left", 4, new_line_align_next },
{ "full", 4, new_line_align_next },
{ "then", 4, none},
{ "into", 4, none},
{ "last", 4, none},
{ "next", 4, none},
{ "only", 4, none},
{ "cube", 4, none},
{ "rows", 4, none},
{ "sets", 4, none},
{ "where", 5, new_line_align_no_pad | end_from},
{ "outer", 5, none},
{ "union", 5, new_line_align_no_pad | end_from},
{ "order", 5, new_line_align_no_pad | end_from},
{ "limit", 5, new_line_align_no_pad | end_from},
{ "group", 5, new_line_align_no_pad | end_from},
{ "count", 5, none},
{ "using", 5, none},
{ "first", 5, none},
{ "inner", 5, none},
{ "nulls", 5, none},
{ "cross", 5, none},
{ "select", 6, newLineComma | new_line_align_no_pad},
{ "update", 6, newLineComma | new_line_align_no_pad},
{ "insert", 6, newLineComma | new_line_align_no_pad},
{ "values", 6, newLineComma | new_line_align_no_pad},
{ "window", 6, new_line_align_no_pad | end_from},
{ "having", 6, new_line_align_no_pad | end_from},
{ "except", 6, new_line_align_no_pad | end_from},
{ "offset", 6, none | end_from},
{ "nothing", 7, none},
{ "lateral", 7, none},
{ "between", 7, none},
{ "nothing", 7, none},
{ "default", 7, none},
{ "current", 7, none},
{ "distinct", 8, none},
{ "conflict", 8, none},
{ "recursive", 9, none},
{ "intersect", 9, new_line_align_no_pad | end_from},
{ "returning", 9, none},
{ "ordinality", 10, none},
{ "materialized", 12, none},
};
struct complite_element {
wxString table;
wxString alias;
wxString columnList;
int startIndex = -1;
int endIndex = -1;
};
struct bits {
unsigned int from : 1;
unsigned int select_list : 1;
unsigned int with : 1;
unsigned int skip : 1;
};
union Byte {
unsigned char byte;
struct bits b;
};
class FormatterSQL {
public:
FormatterSQL(const wxString& sqlsrc) {
sql = sqlsrc;
}
void Formating(wxDC& d, wxRect re, bool isTest = false); // draw
wxString Formating(wxRect re); //
//
wxString BuildAutoComplite(int startIndex, int level);
wxString GetListTable(int cursorPos);
wxString GetColsList(wxString what, wxString& listfieldOut, wxString& nameTableOut);
//
int ParseSql(int flags);
wxString printParseArray();
void SetSql(const wxString& sqlsrc) { sql = sqlsrc; }
int GetIndexItemNextSqlPosition(int sqlPosition);
bool GetItem(int index, FSQL::view_item& item);
private:
wxString get_list_columns(int startindex, union FSQL::Byte zone);
wxPoint align_level(int start_i, int level, int Xpos, int Ypos, int flag);
int check_bracket(int index);
int get_prev_value(int indx, wxString keyword);
int next_item_no_space(int& index, int direction = 1);
wxSize max_width_size(int index); // index bracet
wxSize best_sizeAndDraw(wxDC& dc, wxPoint& pos, FSQL::view_item& vi, int mode);
int maxYheightLine = 0;
int max_exp_bracet_width = 200;
int pad = 16;
int casepad = 8;
wxPoint neededNewLine; // добавляет новую строку перед первым встреченным не пробельным символом
wxRect rect;
wxString sql;
std::vector<FSQL::view_item> items;
std::vector<FSQL::complite_element> listTable; // перечень таблиц синонимов, подзапросов и функций с колонками
//int recurse(int level);
};
}

View file

@ -1035,6 +1035,7 @@
<ClCompile Include="utils\align\AlignWrap.cpp" />
<ClCompile Include="utils\align\Item.cpp" />
<ClCompile Include="utils\csvfiles.cpp" />
<ClCompile Include="utils\FormatterSQL.cpp" />
<ClCompile Include="utils\diff_match_patch.cc">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
@ -1613,8 +1614,11 @@
<ClInclude Include="include\utils\registr.h" />
<ClInclude Include="include\utils\registry.h" />
<ClInclude Include="include\utils\sysLogger.h" />
<ClInclude Include="include\utils\FormatterSQL.h" />
<ClInclude Include="include\utils\sysProcess.h" />
<ClInclude Include="include\utils\sysSettings.h" />
<ClInclude Include="include\utils\popuphelp.h" />
<ClInclude Include="include\utils\FunctionPGHelper.h" />
<ClInclude Include="include\utils\utffile.h" />
<ClInclude Include="include\ctl\calbox.h" />
<ClInclude Include="include\ctl\ctlAuiNotebook.h" />

1311
utils/FormatterSQL.cpp Normal file

File diff suppressed because it is too large Load diff