mirror of
https://github.com/levinsv/pgadmin3.git
synced 2026-05-15 14:15:49 -06:00
433 lines
20 KiB
C++
433 lines
20 KiB
C++
#include "pgAdmin3.h"
|
|
#include <utils/FunctionPGHelper.h>
|
|
#include <wx/stdpaths.h>
|
|
#include <wx/textfile.h>
|
|
#include <wx/filename.h>
|
|
#include "db/pgSet.h"
|
|
#include "frm/frmMain.h"
|
|
#include "ctl/ctlSQLBox.h"
|
|
#include <stack>
|
|
|
|
extern sysSettings* settings;
|
|
extern frmMain *winMain;
|
|
|
|
|
|
wxString FunctionPGHelper::getHelpString(wxString fnd, bool isPart)
|
|
{
|
|
if (!isValid()) return wxEmptyString;
|
|
auto search = body.find(fnd);
|
|
wxString txt;
|
|
|
|
if (search != body.end())
|
|
txt = search->second;
|
|
else
|
|
{
|
|
std::vector<wxString> list;
|
|
int l = fnd.Len();
|
|
wxString b;
|
|
for (const auto& e : body) {
|
|
if (e.first.Len() > l && e.first.StartsWith(fnd)) {
|
|
list.push_back(e.first);
|
|
b = e.second;
|
|
}
|
|
}
|
|
if (list.size() == 1) txt = b;
|
|
else {
|
|
for (const auto& s : list) {
|
|
txt += wxString::Format("<a href=\"%s\">%s</a><br>", s, s);
|
|
}
|
|
}
|
|
}
|
|
//if (i == wxNOT_FOUND) return wxEmptyString;
|
|
if (txt.Len()>1 && txt[0]=='@') {
|
|
txt=txt.substr(1);
|
|
txt=getHelpFile(txt);
|
|
}
|
|
return txt;
|
|
}
|
|
void FunctionPGHelper::setDbConn(pgConn *db) {
|
|
dblast=db;
|
|
}
|
|
#include "schema/pgDatabase.h"
|
|
#include "schema/pgSchema.h"
|
|
#include "schema/pgTable.h"
|
|
#include "schema/pgView.h"
|
|
#include "schema/pgFunction.h"
|
|
#include "schema/pgTrigger.h"
|
|
#include "schema/pgForeignTable.h"
|
|
|
|
extern pgDatabaseFactory databaseFactory;
|
|
extern pgSchemaFactory schemaFactory;
|
|
extern pgTableFactory tableFactory;
|
|
//extern pgforeignTableFactory foreignTableFactory;
|
|
extern pgViewFactory viewFactory;
|
|
extern pgFunctionFactory functionFactory;
|
|
extern pgProcedureFactory procedureFactory;
|
|
extern pgTriggerFunctionFactory triggerFunctionFactory;
|
|
wxString FunctionPGHelper::getDBinfoKeyword(wxString objname, bool islower) {
|
|
if (dblast) {
|
|
wxString s,n,a,tmp;
|
|
tmp=objname;
|
|
a=objname.AfterFirst('(');
|
|
if (!a.IsEmpty()) {
|
|
tmp=objname.BeforeFirst('(');
|
|
a=a.substr(0,a.Len()-1);
|
|
} else a="%";
|
|
make_identifier(tmp,s,n,islower);
|
|
if (s.IsEmpty()) {
|
|
s="%";
|
|
}
|
|
wxString querytemplate=R"(
|
|
select p.oid,p.relnamespace::regnamespace nsp,p.relname objname, p.relkind::text,
|
|
case when p.relkind in ('v','m')
|
|
then
|
|
--pg_get_viewdef(p.oid,true)
|
|
''
|
|
else
|
|
''
|
|
end
|
|
define , '' args, obj_description(oid,'pg_class') comment from pg_class p
|
|
where p.relkind in ('r','p','m','v','f') and p.relnamespace::regnamespace::text not in ('pg_catalog','information_schema')
|
|
and p.relnamespace::regnamespace::text like '%s' and p.relname like '%s'
|
|
and (select count(*) from pg_partition_ancestors(oid) ) <=1
|
|
union all
|
|
select p.oid,p.pronamespace::regnamespace nsp,p.proname objname,
|
|
case when pg_get_function_result(p.oid) is null then 'pp'
|
|
when pg_get_function_result(p.oid) = 'trigger' then 'tt' else 'ff' end,
|
|
'' prosrc ,pg_get_function_arguments(oid), obj_description(oid,'pg_proc') from pg_proc p
|
|
where p.pronamespace::regnamespace::text like '%s' and p.proname like '%s' and pg_get_function_arguments(oid) like '%s'
|
|
and p.pronamespace::regnamespace::text not in ('pg_catalog','information_schema')
|
|
order by objname;
|
|
)";
|
|
wxString es=s;
|
|
wxString en=n;
|
|
es.Replace("'","''");
|
|
en.Replace("'","''");
|
|
wxString sql=wxString::Format(querytemplate
|
|
,es,en,es,en,a);
|
|
pgSet *res = dblast->ExecuteSet(sql);
|
|
wxString txt,ns,on,kind,args,def,oid;
|
|
bool isfunc=false;
|
|
int c=0;
|
|
while (!res->Eof())
|
|
{
|
|
ns = res->GetVal("nsp");
|
|
oid = res->GetVal("oid");
|
|
on = res->GetVal("objname");
|
|
kind = res->GetVal("relkind");
|
|
args = res->GetVal("args");
|
|
def = res->GetVal("define");
|
|
wxString link=ns+"."+on;
|
|
if (kind=="ff" || kind=="pp") {
|
|
link+="("+args+")";
|
|
isfunc=true;
|
|
} else isfunc=false;
|
|
txt += wxString::Format("<a href=\"%s\">%s</a><br>", link, link);
|
|
c++;
|
|
res->MoveNext();
|
|
}
|
|
if(res)
|
|
{
|
|
delete res;
|
|
res = NULL;
|
|
}
|
|
if (c>1) return txt;
|
|
if (c==1)
|
|
{
|
|
wxString html;
|
|
if (def.IsEmpty())
|
|
{
|
|
wxTreeItemIdValue foldercookie;
|
|
ctlTree *browser=winMain->GetBrowser();
|
|
wxTreeItemId folderitem = browser->GetFirstChild(browser->GetRootItem(), foldercookie);
|
|
|
|
while (folderitem)
|
|
{
|
|
if (browser->ItemHasChildren(folderitem))
|
|
{
|
|
wxTreeItemIdValue servercookie;
|
|
wxTreeItemId serveritem = browser->GetFirstChild(folderitem, servercookie);
|
|
wxString host=dblast->GetHost();
|
|
wxString db=dblast->GetDbname();
|
|
int port=dblast->GetPort();
|
|
wxString fullid=on;
|
|
if (!args.IsEmpty()) fullid+="("+args+")";
|
|
wxString idf=wxString::Format("%s:%d",host,port);
|
|
while (serveritem)
|
|
{
|
|
pgServer *server = (pgServer *)browser->GetItemData(serveritem);
|
|
if (server != NULL && server->IsCreatedBy(serverFactory)) {
|
|
if (server->GetIdentifier()==idf ) {
|
|
if (server->connection()) {
|
|
// is open connect
|
|
//wxTreeItemId serveritemc1 = browser->GetFirstChild(folderitem, servercookie);
|
|
pgCollection *coll=browser->FindCollection(databaseFactory,serveritem);
|
|
wxTreeItemId dbssId;
|
|
if (coll) dbssId=coll->GetId();
|
|
wxCookieType cookie2;
|
|
wxTreeItemId item = browser->GetFirstChild(dbssId, cookie2);
|
|
|
|
while (item && item.IsOk())
|
|
{
|
|
//wxString tt=browser->GetItemText(item);
|
|
pgObject *obj = browser->GetObject(item);
|
|
if (obj && obj->IsCreatedBy(databaseFactory) && db==obj->GetName() )
|
|
{
|
|
pgCollection *coll=browser->FindCollection(schemaFactory,item);
|
|
wxTreeItemId schemasId;
|
|
if (coll) schemasId=coll->GetId();
|
|
if (schemasId) {
|
|
pgObject *obj2 = browser->GetObject(item);
|
|
//obj2->expandedKids
|
|
wxCookieType cookie4;
|
|
item = browser->GetFirstChild(schemasId, cookie4);
|
|
while (item)
|
|
{
|
|
pgSchema *obj3 = (pgSchema *)browser->GetObject(item);
|
|
if (obj3 && obj3->IsCreatedBy(schemaFactory) && ns==obj3->GetName() ) {
|
|
obj3->ShowTreeDetail(browser);
|
|
wxTreeItemId objcollId;
|
|
pgCollection *coll=NULL;
|
|
if (kind=='r'||kind=='p' ) coll = browser->FindCollection(tableFactory,item);
|
|
if (kind=='v'||kind=='m') coll = browser->FindCollection(viewFactory,item);
|
|
if (kind=='f') coll = browser->FindCollection(foreignTableFactory,item);
|
|
|
|
if (kind=="ff") coll = browser->FindCollection(functionFactory,item);
|
|
if (kind=="pp") coll = browser->FindCollection(procedureFactory,item);
|
|
//if (kind=='p') coll = browser->FindCollection(pg_partitionFactory,item);
|
|
if (kind=="tt") coll = browser->FindCollection(triggerFunctionFactory,item);
|
|
if (coll) {
|
|
objcollId=coll->GetId();
|
|
pgObject *obj4 = browser->GetObject(objcollId);
|
|
obj4->ShowTreeDetail(browser);
|
|
}
|
|
wxCookieType cookie5;
|
|
item = browser->GetFirstChild(objcollId, cookie5);
|
|
while (item)
|
|
{
|
|
pgObject *obj5 = browser->GetObject(item);
|
|
//wxString soid=obj5->GetOidStr();
|
|
if (obj5 && NumToStr(obj5->GetOid())==oid) {
|
|
pgObject *obj5 = browser->GetObject(item);
|
|
obj5->ShowTreeDetail(browser);
|
|
def=obj5->GetSql(browser);
|
|
if (kind=='r'|| kind=='m' || kind=='v' || kind=="ff") {
|
|
//pgTable *o=(pgTable *)(obj5);
|
|
//brower->FindObject(tableFactory,)
|
|
//o->AppendStuff(def,browser,tableFactory);
|
|
}
|
|
goto exitloop;
|
|
}
|
|
item = browser->GetNextChild(objcollId, cookie5);
|
|
|
|
}
|
|
|
|
}
|
|
item = browser->GetNextChild(schemasId, cookie4);
|
|
}
|
|
}
|
|
goto exitloop;
|
|
}
|
|
item = browser->GetNextChild(dbssId, cookie2);
|
|
} // next
|
|
|
|
} else {
|
|
// brower->db close
|
|
//return wxEmptyString;
|
|
def=_("'The database is closed in the object browser.'");
|
|
}
|
|
|
|
goto exitloop;
|
|
}
|
|
}
|
|
serveritem = browser->GetNextChild(folderitem, servercookie);
|
|
}
|
|
}
|
|
folderitem = browser->GetNextChild(browser->GetRootItem(), foldercookie);
|
|
}
|
|
}
|
|
exitloop:
|
|
FSQL::FormatterSQL f(def);
|
|
std::vector<FSQL::complite_element> listobj=f.ParsePLpgsql();
|
|
ctlSQLBox* box = new ctlSQLBox((wxWindow*) winMain, CTL_SQLQUERY, wxDefaultPosition, wxSize(0, 0), wxTE_MULTILINE | wxTE_RICH2);
|
|
box->SetText(def);
|
|
int l = def.Length();
|
|
box->Colourise(0, box->GetLength());
|
|
//bg = box->SetSQLBoxColourBackground(false).GetAsString(wxC2S_CSS_SYNTAX);
|
|
html = box->TextToHtml(0, box->GetLength(),false,listobj);
|
|
wxColour cbg=box->GetBackgroundColour();
|
|
wxString bg=cbg.GetAsString(wxC2S_HTML_SYNTAX);
|
|
delete box;
|
|
html = "<html><body BGCOLOR=\"" + bg + "\">" + html + "</body></html>";
|
|
return html;
|
|
}
|
|
|
|
}
|
|
|
|
return wxEmptyString;
|
|
}
|
|
wxString FunctionPGHelper::getSqlCommandHelp(wxString fnd) {
|
|
wxUniChar sep = wxFileName::GetPathSeparator();
|
|
fnd.Replace(" ", "");
|
|
wxString f = wxFindFirstFile(path + sep + "sql-" + fnd + "*.html");
|
|
if (f.empty()) {
|
|
fnd.Replace("_", "-");
|
|
f = wxFindFirstFile(path + sep + "view-" + fnd + "*.html");
|
|
if (f.empty()) f = wxFindFirstFile(path + sep + "catalog-" + fnd + "*.html");
|
|
}
|
|
wxString last, txt;
|
|
|
|
int c = 0;
|
|
while (!f.empty())
|
|
{
|
|
f = f.AfterLast(sep);
|
|
last = f;
|
|
txt += wxString::Format("<a href=\"%s\">%s</a><br>", f, f);
|
|
f = wxFindNextFile();
|
|
c++;
|
|
}
|
|
if (last.empty()) {
|
|
return wxEmptyString;
|
|
}
|
|
else if (c == 1) {
|
|
return getHelpFile(last);
|
|
}
|
|
else {
|
|
return txt;
|
|
}
|
|
|
|
}
|
|
wxString FunctionPGHelper::getTextForAnchor(wxString name) {
|
|
wxString n;
|
|
if (name.Len()>0 && name[0]=='#') n = name.substr(1); else n=name;
|
|
for (auto s:an) {
|
|
if (s.id==n) {
|
|
n=filecontext.substr(s.start,s.end-s.start+1);
|
|
return n;
|
|
}
|
|
}
|
|
return wxEmptyString;
|
|
}
|
|
wxString FunctionPGHelper::getHelpFile(wxString filename) {
|
|
wxString a;
|
|
int p=filename.Find('#');
|
|
if (p>0) {a=filename.substr(p+1); filename=filename.BeforeFirst('#');}
|
|
|
|
wxString tempDir = path + wxFileName::GetPathSeparator() + filename;
|
|
an.clear();
|
|
filecontext=wxEmptyString;
|
|
if (!wxFileExists(tempDir)) return wxEmptyString;
|
|
wxTextFile tfile;
|
|
tfile.Open(tempDir);
|
|
// read the first line
|
|
wxString str, sbody;
|
|
sbody = tfile.GetFirstLine();
|
|
bool flag = true;
|
|
wxRegEx b("(<body .*?>)");
|
|
wxString ss;
|
|
std::stack<anchor_src> bg;
|
|
while (!tfile.Eof())
|
|
{
|
|
str = tfile.GetNextLine();
|
|
if (flag && b.Matches(str)) {
|
|
size_t start, len;
|
|
b.GetMatch(&start, &len, 0);
|
|
str = str.Mid(start + len);
|
|
sbody = "<body>";
|
|
flag = false;
|
|
}
|
|
str.Replace(filename,"");
|
|
sbody += str;
|
|
}
|
|
// find ancor
|
|
str=sbody;
|
|
filecontext=sbody;
|
|
int l=str.Len();
|
|
int i=0;
|
|
|
|
while (i<l) {
|
|
wxChar c=str[i++];
|
|
if (c=='<' && (i+3)<l
|
|
&& str[i+0]=='d'
|
|
&& str[i+1]=='i'
|
|
&& str[i+2]=='v'
|
|
) {
|
|
anchor_src s {i-1,-1,wxEmptyString};
|
|
i=i+3;
|
|
// find id="anchor name"
|
|
while (i<l && str[i]!='>') {
|
|
if (i>3 && str[i]=='"') {
|
|
if (str[i-1]=='='
|
|
&& str[i-2]=='d'
|
|
&& str[i-3]=='i'
|
|
) {
|
|
wxString n;
|
|
i++;
|
|
while (i<l && str[i]!='"') n=n+str[i++];
|
|
s.id=n;
|
|
}
|
|
}
|
|
i++;
|
|
}
|
|
i++;
|
|
bg.push(s); // end =-1
|
|
|
|
} else if (c=='<' && (i+6)<l
|
|
&& str[i+0]=='/'
|
|
&& str[i+1]=='d'
|
|
&& str[i+2]=='i'
|
|
&& str[i+3]=='v'
|
|
&& str[i+4]=='>'
|
|
) {
|
|
// close
|
|
i=i+4;
|
|
anchor_src s1=bg.top();
|
|
s1.end=i;
|
|
if (s1.id.Len()>0) an.push_back(s1);
|
|
bg.pop();
|
|
if (a.Len()>0 && s1.id==a) sbody=getTextForAnchor(a);
|
|
i++;
|
|
}
|
|
}
|
|
|
|
return sbody;
|
|
|
|
}
|
|
bool FunctionPGHelper::isValid() {
|
|
if (!isload) loadfile();
|
|
return isload;
|
|
}
|
|
void FunctionPGHelper::loadfile() {
|
|
if (isload) return;
|
|
body.clear();
|
|
path = settings->GetPgHelpPath();
|
|
wxString tempDir = path + "_func.txt";
|
|
if (!wxFileExists(tempDir) ) {
|
|
wxMessageBox(_("To use the contextual help for PostgreSQL functions:\n1. You need to place a file '_extract_func_help.pl' to the directory containing the html help files.\n2. Run it and get the file '_func.txt'.\n3. Specify the path in the pgadmin3 settings to the html help."), _("Warning"));
|
|
isload = true;
|
|
return;
|
|
}
|
|
wxTextFile tfile;
|
|
tfile.Open(tempDir);
|
|
|
|
// read the first line
|
|
wxString str, sbody;
|
|
wxString name = tfile.GetFirstLine();
|
|
|
|
//wxSortedArrayString names;
|
|
name = name.AfterFirst('#');
|
|
// read all lines one by one
|
|
// until the end of the file
|
|
while (!tfile.Eof())
|
|
{
|
|
str = tfile.GetNextLine();
|
|
if (str.Left(1) == '#') {
|
|
body.emplace(name, sbody);
|
|
sbody = "";
|
|
name = str.AfterFirst('#');
|
|
}
|
|
else sbody += str;
|
|
}
|
|
body.emplace(name, sbody);
|
|
isload = true;
|
|
};
|