diff --git a/README.md b/README.md index ce4470c..29bb2cc 100644 --- a/README.md +++ b/README.md @@ -43,3 +43,6 @@ This text Russian language. * В экспорте результатов запроса в Excel исправлена ошибка при сохранении интервалов * При обновлении схемы не блокируется интерфейс если на таблице идет долгая операция cluster Но при F5 на самой таблице блокировка сохраняется (это связано с блокированием функций pg_def* при получинии информации от таблицах) +09.12.2018 + - autocomplite: добавлены имена функций, и возможность подставлять имена колонок таблиц из поля FROM + - при наборе имени функции появляется перечень параметров этой функции diff --git a/Release/pgAdmin3.exe b/Release/pgAdmin3.exe index 777f817..33bee13 100644 Binary files a/Release/pgAdmin3.exe and b/Release/pgAdmin3.exe differ diff --git a/ctl/ctlSQLBox.cpp b/ctl/ctlSQLBox.cpp index 6f036cc..19846a5 100644 --- a/ctl/ctlSQLBox.cpp +++ b/ctl/ctlSQLBox.cpp @@ -455,13 +455,106 @@ bool ctlSQLBox::DoFind(const wxString &find, const wxString &replace, bool doRep void ctlSQLBox::SetAutoReplaceList(queryMacroList *autorep) { autoreplace=autorep; } +void ctlSQLBox::SetDefFunction(wxArrayString &name, wxArrayString &def) { + m_name=&name; + m_def=&def; +} + void ctlSQLBox::OnKeyDown(wxKeyEvent &event) { //wxString autoreplace[]={wxT("se"),wxT("select * from"),wxT("sc"),wxT("select count(*) from"),wxT("si"),wxT("select * from info_oper where"),wxT("sh"),wxT("select * from info_history where")}; #ifdef __WXGTK__ event.m_metaDown = false; #endif + if (m_name) + { + if (((event.GetKeyCode() == '0')&&(event.GetModifiers() == (wxMOD_SHIFT))) + ||event.GetKeyCode() == WXK_UP + ||event.GetKeyCode() == WXK_DOWN) + { + if (CallTipActive()) CallTipCancel(); + event.Skip(); + return; + + } + + if ((event.GetKeyCode() == '9')&&(event.GetModifiers() == (wxMOD_SHIFT))) + { + int pos = GetCurrentPos() ; + //CallTipSetBackground(*wxYELLOW); + wxString line = GetLine(GetCurrentLine()); + //int max = line.Length() - (GetLineEndPosition(GetCurrentLine()) - GetCurrentPos()) - offset; + int max= PositionFromLine(LineFromPosition(pos)); + int l=0; + wxCharBuffer myStringChars = line.mb_str(); + for (int kk=pos-max-1;kk>=0;kk--) { + //char c=myStringChars.data[kk]; + if ( line[kk]<'0') break; + l++; + } + wxString f_name=GetTextRange(pos-l, pos); + int idx=m_name->Index(f_name,false,false); + if (idx!=wxNOT_FOUND) { + calltip=f_name +wxT("(")+m_def->Item(idx)+wxT(")"); + ct_hl=calltip.Find(wxT(",")); + if (ct_hl==wxNOT_FOUND) ct_hl=l; + for (int jj=idx+1;jjCount();jj++) { + if (m_name->Item(jj)== f_name) { + calltip+=wxT("\n")+f_name +wxT("(")+m_def->Item(jj)+wxT(")"); + } else break; + } + + CallTipShow(pos-l,calltip); + CallTipSetHighlight(0,ct_hl); + + + } + //t = GetTextRange(pos-rpl.Length(), pos); + + event.Skip(); + return; + } + } + if (CallTipActive()&&(event.GetKeyCode() == ',' + ||event.GetKeyCode() == WXK_RIGHT + ||event.GetKeyCode() == WXK_LEFT + )) { + //int ptip=CallTipPosAtStart(); + int direction=1; + if (event.GetKeyCode() == WXK_LEFT) + direction=-1; + char c=GetCharAt(GetCurrentPos()); + if (event.GetKeyCode() != ','&&c!=',' ) { + //direction=ct_hl; + } else + { + int pos=ct_hl+direction; + int a=0; + while ( (pos0)) { + c=calltip[pos]; + if ((c==',')||(c=='\n')) { + if (direction==1) break; + if (a==1) break; + a++; + ct_hl=pos; + } + pos=pos+direction; + } + //int l=calltip.find(wxT(","),ct_hl+1); + int prev=ct_hl; + if (direction==-1) { + prev=pos; + pos=ct_hl; + } else + { + if (pos==wxNOT_FOUND||(pos==calltip.length())) pos=ct_hl; + } + CallTipSetHighlight(prev,pos); + + ct_hl=pos; + } + } // Get the line ending type wxString lineEnd; switch (GetEOLMode()) @@ -485,7 +578,7 @@ void ctlSQLBox::OnKeyDown(wxKeyEvent &event) //wxString line = GetLine(GetCurrentLine()); wxString t; wxString wc; - char c; + //char c; bool f; wxString rpl = wxT(""); wxString rpl2 = wxT(""); @@ -887,7 +980,7 @@ void ctlSQLBox::OnPositionStc(wxStyledTextEvent &event) if ((ch == '{' || ch == '}' || ch == '[' || ch == ']' || ch == '(' || ch == ')') && - st != 2 && st != 6 && st != 7) + st != 1 && st != 2 && st != 6 && st != 7) { match = BraceMatch(pos - 1); if (match != wxSTC_INVALID_POSITION) @@ -896,32 +989,134 @@ void ctlSQLBox::OnPositionStc(wxStyledTextEvent &event) else if ((nextch == '{' || nextch == '}' || nextch == '[' || nextch == ']' || nextch == '(' || nextch == ')') && - st != 2 && st != 6 && st != 7) + st != 1 &&st != 2 && st != 6 && st != 7) { match = BraceMatch(pos); if (match != wxSTC_INVALID_POSITION) BraceHighlight(pos, match); } - + int startsql=0; // Roll back through the doc and highlight any unmatched braces + int tmp=pos; while ((pos--) >= 0) { ch = GetCharAt(pos); st = GetStyleAt(pos); - + if (st != 1 &&st != 2 && st != 6 && st != 7 &&(ch==';')&&startsql==0) startsql=pos+1; if ((ch == '{' || ch == '}' || ch == '[' || ch == ']' || ch == '(' || ch == ')') && - st != 2 && st != 6 && st != 7) + st != 1 &&st != 2 && st != 6 && st != 7) { match = BraceMatch(pos); if (match == wxSTC_INVALID_POSITION) { BraceBadLight(pos); break; + } else + { + } } } + // select + pos=tmp; + wxString keyword; + wxString ident; + while ((pos--) >= 0) + { + ch = GetCharAt(pos); + st = GetStyleAt(pos); + if (st==5) {ident.append(ch);} else + { + if ((!ident.IsEmpty())) { + + if (ident.CmpNoCase(wxT("tceles"))==0) { + startsql=pos+1; + break; + } + ident.Clear(); + } + + } + if (st != 1 &&st != 2 && st != 6 && st != 7 &&(ch==';')&&startsql==0) startsql=pos+1; + if (( ch == '}' || + ch == ']' || + ch == ')') && + st != 1 &&st != 2 && st != 6 && st != 7) + { + match = BraceMatch(pos); + if (match == wxSTC_INVALID_POSITION) + { + break; + } else + { + pos=match-1; + } + } + } + + int endtext= GetLength(); + bool isfrom=false; + pos=startsql; + list_table.Clear(); + ident.Clear(); + while (pos=0;kk--) { + //char c=myStringChars.data[kk]; + if ( what[kk]<'0') break; + l++; + } + wxString f_name; + if (l>0) { + wxString alias=what.substr(what.Len()-1-l,l); + wxString lst=list_table; + wxString table; + //alias.Replace(wxT("."), wxT("")); + lst.Replace(wxT("\""), wxT("")); + wxStringTokenizer tokenizer(lst, wxT(" ,")); + wxString prev; + bool found=false; + int p=0; + while ( tokenizer.HasMoreTokens() ) + { + wxString token = tokenizer.GetNextToken(); + if (token.IsEmpty()) {p=0; continue;} + found=token.CmpNoCase(alias)==0; + if (tokenizer.GetLastDelimiter()==' '&&found) { + table=token; + break; + } + if ((tokenizer.GetLastDelimiter()==',')&&found) { + if (p==1) table=prev; else table=token; + break; + } + if (found) {if (p==1) table=prev; else table=token; break;} + prev=token; + p++; + } + if (found) { + wxString sql=wxT("select string_agg(a.attname,E'\t') from pg_attribute a where a.attrelid = (select oid from pg_class p where relname=") +qtConnString(table) + +wxT(") and a.attisdropped IS FALSE and a.attnum>=0 order by 1"); + //pgSet *res = m_database->ExecuteSet(sql); + prev= m_database->ExecuteScalar(sql); + AutoCompShow(0, prev); + return; + } + //list_table.Find(alias + } + l=0; + for (int kk=what.Len()-1;kk>=0;kk--) { + //char c=myStringChars.data[kk]; + if ( what[kk]<'0') break; + l++; + } + if (l==0) return; + f_name=what.Right(l); + what=f_name; + size_t i, + lo = 0, + hi = m_name->Count(); + int res; + while ( lo < hi ) { + i = (lo + hi)/2; + if (m_name->Item(i).StartsWith(f_name)) break; + res= f_name.CmpNoCase(m_name->Item(i)); + //res = wxStrcmp(sz, m_pItems[i]); + if ( res < 0 ) + hi = i; + else if ( res > 0 ) + lo = i + 1; + else + { break; }// i ok + } + if (lo >= hi) return; + int k=i; + while ( k>=0&&m_name->Item(k).StartsWith(f_name) ) k--; + k++; + wxString prev; + while ( (kGetCount())&&m_name->Item(k).StartsWith(f_name) ) + { + if (prev!=m_name->Item(k)) wxRet+=m_name->Item(k)+wxT("\t"); + prev=m_name->Item(k); + k++; + }; + spaceidx = what.Len()-1; + spaceidx = -1; + //return; /* No autocomplete available for this string */ + } else { + wxRet = wxString(tab_ret, wxConvUTF8); + free(tab_ret); + } // Switch to the generic list control. Native doesn't play well with // autocomplete on Mac. #ifdef __WXMAC__ @@ -1043,11 +1320,11 @@ CharacterRange ctlSQLBox::RegexFindText(int minPos, int maxPos, const wxString & ft.lpstrText = (char *)(const char *)buf; // wx 2.8.12 - //if (SendMsg(2150, wxSTC_FIND_REGEXP, (long)&ft) == -1) - //{ - // ft.chrgText.cpMin = -1; - // ft.chrgText.cpMax = -1; - //} + if (SendMsg(2150, wxSTC_FIND_REGEXP, (long)&ft) == -1) + { + ft.chrgText.cpMin = -1; + ft.chrgText.cpMax = -1; + } return ft.chrgText; } diff --git a/frm/frmQuery.cpp b/frm/frmQuery.cpp index 98182eb..87e42ee 100644 --- a/frm/frmQuery.cpp +++ b/frm/frmQuery.cpp @@ -743,7 +743,25 @@ frmQuery::frmQuery(frmMain *form, const wxString &_title, pgConn *_conn, const w queryMenu->Enable(MNU_CLEARHISTORY, false); setTools(false); lastFileFormat = settings->GetUnicodeFile(); - + // Load function name + pgSet *functions; + wxString def_f; + wxString def_name; + functions = conn->ExecuteSet(wxT("select pg_get_function_arguments(oid) def,proname,pronargs nargs from pg_proc where pronargs<>0 order by proname,pronargs")); + if (functions) + { + //name_func.Init(true); + while (!functions->Eof()) + { + def_f = functions->GetVal(wxT("def")); + def_name = functions->GetVal(wxT("proname")); + def_func.Add(def_f); + name_func.Add(def_name); + functions->MoveNext(); + } + name_func.Sort(); + delete functions; + } // Note that under GTK+, SetMaxLength() function may only be used with single line text controls. // (see http://docs.wxwidgets.org/2.8/wx_wxtextctrl.html#wxtextctrlsetmaxlength) #ifndef __WXGTK__ @@ -756,7 +774,8 @@ frmQuery::frmQuery(frmMain *form, const wxString &_title, pgConn *_conn, const w frmQuery::~frmQuery() { closing = true; - + if (!name_func.IsEmpty()) name_func.Clear(); + if (!def_func.IsEmpty()) def_func.Clear(); // Save frmQuery Perspective settings->Write(wxT("frmQuery/Perspective-") + wxString(FRMQUERY_PERSPECTIVE_VER), manager.SavePerspective()); @@ -1946,6 +1965,8 @@ void frmQuery::OnPositionStc(wxStyledTextEvent &event) editMenu->Enable(MNU_AUTOEDITOBJECT, selCount > 0); wxString pos; pos.Printf(_("Ln %d, Col %d, Ch %d"), sqlQuery->LineFromPosition(sqlQuery->GetCurrentPos()) + 1, sqlQuery->GetColumn(sqlQuery->GetCurrentPos()) + 1, sqlQuery->GetCurrentPos() + 1); + //pos.Printf(_("Ln %d, Col %d, Ch %d, st %d"), sqlQuery->LineFromPosition(sqlQuery->GetCurrentPos()) + 1, sqlQuery->GetColumn(sqlQuery->GetCurrentPos()) + 1, sqlQuery->GetCurrentPos() + 1,sqlQuery->GetStyleAt(sqlQuery->GetCurrentPos() - 1)); + SetStatusText(pos, STATUSPOS_POS); if (selCount < 1) pos = wxEmptyString; @@ -4011,6 +4032,7 @@ void frmQuery::SqlBookAddPage() // Probably not needed, as the line above should trigger the PageChange event sqlQuery = box; sqlQuery->SetAutoReplaceList(autoreplace); + sqlQuery->SetDefFunction(name_func, def_func); } void frmQuery::SqlBookDisconnectPage(ctlSQLBox *box) diff --git a/include/ctl/ctlSQLBox.h b/include/ctl/ctlSQLBox.h index b592890..533ba56 100644 --- a/include/ctl/ctlSQLBox.h +++ b/include/ctl/ctlSQLBox.h @@ -73,7 +73,7 @@ public: void UpdateLineNumber(); wxString ExternalFormat(); void AbortProcess(); - + void SetDefFunction(wxArrayString &name, wxArrayString &def); CharacterRange RegexFindText(int minPos, int maxPos, const wxString &text); // Having multiple SQL tabs warrants the following properties to be tracked per tab @@ -104,6 +104,11 @@ private: void OnPositionStc(wxStyledTextEvent &event); void OnMarginClick(wxStyledTextEvent &event); queryMacroList *autoreplace; + wxArrayString *m_name; // field proname + wxArrayString *m_def; // finction arguments + wxString list_table; // list table from section + wxString calltip; + int ct_hl; dlgFindReplace *m_dlgFindReplace; pgConn *m_database; bool m_autoIndent, m_autocompDisabled; diff --git a/include/frm/frmQuery.h b/include/frm/frmQuery.h index f529d7d..5d5fb8e 100644 --- a/include/frm/frmQuery.h +++ b/include/frm/frmQuery.h @@ -135,7 +135,8 @@ private: wxButton *btnDeleteCurrent; wxButton *btnDeleteAll; wxArrayString histoQueries; - + wxArrayString def_func; + wxArrayString name_func; ctlAuiNotebook *sqlQueryBook; //container for all SQL tabs size_t sqlQueryCounter; //for initial tab names ctlSQLBox *sqlQueryExec; //currently executing SQL tab