From 7205b3d93f93eef6706b49e48764a9d1489b55e5 Mon Sep 17 00:00:00 2001 From: Jasmin Dizdarevic Date: Thu, 16 Jun 2011 23:37:36 +0200 Subject: [PATCH] From todo list: Ability to search a database for objects with a specific name Search: Added missing objects, outside database objects, changed UI, bugfixes,... Updated docs; Added 9.1 objects and some fixes in searchSQL. Fix validate constraint with PostgreSQL < 9.1 when creating covering index. Reported by Timon. Fix infinite loop when foreign table has no options defined. Moved open dialog code to frmSearchObject Fix backwards compatibility Changed frmSearchObject to dlgSearchObject --- docs/en_US/images/search.png | Bin 0 -> 42201 bytes docs/en_US/search.html | 30 ++ pgadmin/dlg/dlgSearchObject.cpp | 532 +++++++++++++++++++++++++++++++++ pgadmin/frm/frmMain.cpp | 43 ++- pgadmin/frm/module.mk | 2 +- pgadmin/include/dlg/dlgSearchObject.h | 78 +++++ pgadmin/include/frm/frmMain.h | 3 +- pgadmin/include/frm/module.mk | 2 +- pgadmin/include/schema/pgDatabase.h | 2 - pgadmin/schema/pgDatabase.cpp | 1 + pgadmin/schema/pgForeignTable.cpp | 4 + pgadmin/ui/dlgSearchObject.xrc | 100 ++++++ pgadmin/ui/module.mk | 3 +- 13 files changed, 781 insertions(+), 19 deletions(-) create mode 100644 docs/en_US/images/search.png create mode 100644 docs/en_US/search.html create mode 100644 pgadmin/dlg/dlgSearchObject.cpp create mode 100644 pgadmin/include/dlg/dlgSearchObject.h create mode 100644 pgadmin/ui/dlgSearchObject.xrc diff --git a/docs/en_US/images/search.png b/docs/en_US/images/search.png new file mode 100644 index 0000000000000000000000000000000000000000..e01eac2061db199772133af5f8e0dac71eda5581 GIT binary patch literal 42201 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 H!8GuHQaue& literal 0 HcmV?d00001 diff --git a/docs/en_US/search.html b/docs/en_US/search.html new file mode 100644 index 0000000..c4c1aef --- /dev/null +++ b/docs/en_US/search.html @@ -0,0 +1,30 @@ + + + + + +Search + + + + +

Object search

+

+

+With this GUI you can search for almost any kind of objects in a database and the corresponding cluster. +

+

+You can access it by right clicking a database and select Search Object or by hitting CTRL-G. +The minimum pattern length are 3 characters except for operators. The search is performed unexactly and non-case sensitive. +You can only search for object names. +

+

+The result is presented in the grid with object type, object name and the object tree path. +You can click on a result row to select the object in the object tree. +If the object is grey, this means that you don't have enabled those object types in settings (Options -> Browsers -> Display the following database objects) +and you can't click on it. +

+ + + + diff --git a/pgadmin/dlg/dlgSearchObject.cpp b/pgadmin/dlg/dlgSearchObject.cpp new file mode 100644 index 0000000..b315e0c --- /dev/null +++ b/pgadmin/dlg/dlgSearchObject.cpp @@ -0,0 +1,532 @@ +////////////////////////////////////////////////////////////////////////// +// +// pgAdmin III - PostgreSQL Tools +// +// Copyright (C) 2002 - 2011, The pgAdmin Development Team +// This software is released under the PostgreSQL Licence +// +// dlgSearchObject.h - Search dialogue +// +////////////////////////////////////////////////////////////////////////// + + + +// App headers +#include "pgAdmin3.h" + +#include "frm/frmMain.h" +#include "dlg/dlgSearchObject.h" +#include "utils/sysSettings.h" +#include "utils/misc.h" +#include "ctl/ctlListView.h" + +#define txtPattern CTRL_TEXT("txtPattern") +#define cbType CTRL_COMBOBOX("cbType") +#define lcResults CTRL_LISTCTRL("lcResults") +#define btnSearch CTRL_BUTTON("btnSearch") + + + +BEGIN_EVENT_TABLE(dlgSearchObject, pgDialog) + EVT_BUTTON(wxID_HELP, dlgSearchObject::OnHelp) + EVT_BUTTON(XRCID("btnSearch"), dlgSearchObject::OnSearch) + EVT_BUTTON(wxID_CANCEL, dlgSearchObject::OnCancel) + EVT_TEXT(XRCID("txtPattern"), dlgSearchObject::OnChange) + EVT_LIST_ITEM_SELECTED(XRCID("lcResults"), dlgSearchObject::OnSelSearchResult) + +END_EVENT_TABLE() + +dlgSearchObject::dlgSearchObject(frmMain *p, pgDatabase *db) +{ + parent = p; + header = wxT(""); + currentdb = db; + + wxWindowBase::SetFont(settings->GetSystemFont()); + LoadResource(p, wxT("dlgSearchObject")); + + // Icon + appearanceFactory->SetIcons(this); + RestorePosition(); + + btnSearch->Disable(); + + lcResults->InsertColumn(0, _("Type")); + lcResults->InsertColumn(1, _("Name")); + lcResults->InsertColumn(2, _("Path")); + + + /* Mapping table between local language and english, because in SQL we're using only english and translate it later + /* to the local language.*/ + + aMap[_("All types")] = wxT("All types"); + aMap[_("Schemas")] = wxT("Schemas"); + aMap[_("Tables")] = wxT("Tables"); + aMap[_("Columns")] = wxT("Columns"); + aMap[_("Triggers")] = wxT("Triggers"); + aMap[_("Views")] = wxT("Views"); + aMap[_("Rules")] = wxT("Rules"); + aMap[_("Indexes")] = wxT("Indexes"); + aMap[_("Functions")] = wxT("Functions"); + aMap[_("Aggregates")] = wxT("Aggregates"); + aMap[_("Trigger Functions")] = wxT("Trigger Functions"); + aMap[_("Constraints")] = wxT("Constraints"); + aMap[_("Sequences")] = wxT("Sequences"); + aMap[_("Types")] = wxT("Types"); + aMap[_("Domains")] = wxT("Domains"); + aMap[_("Languages")] = wxT("Languages"); + aMap[_("Conversions")] = wxT("Conversions"); + aMap[_("Casts")] = wxT("Casts"); + aMap[_("Login Roles")] = wxT("Login Roles"); + aMap[_("Group Roles")] = wxT("Group Roles"); + aMap[_("FTS Configurations")] = wxT("FTS Configurations"); + aMap[_("FTS Dictionarie")] = wxT("FTS Dictionaries"); + aMap[_("FTS Parsers")] = wxT("FTS Parsers"); + aMap[_("FTS Templates")] = wxT("FTS Templates"); + aMap[_("Foreign Data Wrappers")] = wxT("Foreign Data Wrappers"); + aMap[_("Foreign Servers")] = wxT("Foreign Servers"); + aMap[_("Foreign Tables")] = wxT("Foreign Tables"); + aMap[_("User Mappings")] = wxT("User Mappings"); + aMap[_("Operators")] = wxT("Operators"); + aMap[_("Operator Classes")] = wxT("Operator Classes"); + aMap[_("Operator Families")] = wxT("Operator Families"); + aMap[_("Extensions")] = wxT("Extensions"); + aMap[_("Collations")] = wxT("Collations"); + + /* Duplicate definition, if somebody knows how to use wxHashTable to fill the dropdown, please make it better and use the iteration to fill the combobox: + The problem is, that the hash table is automatically sorted and that's unuseable. + + LngMapping::iterator it; + for(it = aMap.begin(); it != aMap.end(); ++it) + { + cbType->Append(it->first); + } + */ + + cbType->Clear(); + cbType->Append(_("All types")); + cbType->Append(_("Schemas")); + cbType->Append(_("Tables")); + cbType->Append(_("Columns")); + cbType->Append(_("Triggers")); + cbType->Append(_("Views")); + cbType->Append(_("Rules")); + cbType->Append(_("Indexes")); + cbType->Append(_("Functions")); + cbType->Append(_("Aggregates")); + cbType->Append(_("Trigger Functions")); + cbType->Append(_("Constraints")); + cbType->Append(_("Sequences")); + cbType->Append(_("Types")); + cbType->Append(_("Languages")); + cbType->Append(_("Domains")); + cbType->Append(_("Conversions")); + cbType->Append(_("Casts")); + cbType->Append(_("Login Roles")); + cbType->Append(_("Group Roles")); + cbType->Append(_("FTS Configurations")); + cbType->Append(_("FTS Dictionaries")); + cbType->Append(_("FTS Parsers")); + cbType->Append(_("FTS Templates")); + if(currentdb->BackendMinimumVersion(8,4)) + { + cbType->Append(_("Foreign Data Wrappers")); + cbType->Append(_("Foreign Servers")); + cbType->Append(_("User Mappings")); + } + if(currentdb->BackendMinimumVersion(9,1)) + { + cbType->Append(_("Foreign Tables")); + } + cbType->Append(_("Operators")); + cbType->Append(_("Operator Classes")); + cbType->Append(_("Operator Families")); + + if(currentdb->BackendMinimumVersion(9,1)) + { + cbType->Append(_("Extensions")); + cbType->Append(_("Collations")); + } + cbType->SetSelection(0); + + +} + + +dlgSearchObject::~dlgSearchObject() +{ + //Deconstructor + +} + +void dlgSearchObject::OnHelp(wxCommandEvent &ev) { + DisplayHelp(wxT("searchobject"), HELP_PGADMIN); +} + +void dlgSearchObject::OnSelSearchResult(wxListEvent &ev) +{ + + long row_number = ev.GetIndex(); + + if(lcResults->GetItemTextColour(row_number) == wxColour(128,128,128)) + { + /* Result type is not enabled in settings, so we don't search for it in the tree */ + return; + } + + //Taken from: http://wiki.wxwidgets.org/WxListCtrl#Get_the_String_Contents_of_a_.22cell.22_in_a_LC_REPORT_wxListCtrl + wxListItem row_info; + wxString path; + + // Set what row it is (m_itemId is a member of the regular wxListCtrl class) + row_info.m_itemId = row_number; + // Set what column of that row we want to query for information. + row_info.m_col = 2; + // Set text mask + row_info.m_mask = wxLIST_MASK_TEXT; + + // Get the info and store it in row_info variable. + lcResults->GetItem(row_info); + + // Extract the text out that cell + path = row_info.m_text; + + + if(!parent->SetCurrentNode(parent->GetBrowser()->GetRootItem(), path)) + { + wxMessageBox(_("The specified object couldn't be found in the tree")); + } + +} + +void dlgSearchObject::OnChange(wxCommandEvent &ev) { + + /* When someone searches for operators the limit of 3 characters is ignored */ + if(aMap[cbType->GetValue()] != wxT("Operators") && txtPattern->GetValue().Length() < 3) + { + btnSearch->Disable(); + } + else + { + btnSearch->Enable(); + } +} + +void dlgSearchObject::OnSearch(wxCommandEvent &ev) +{ + + /* + Adding objects: + + Create a sql statement which lists all objects of the specified type and add it to the inner statement with an union. + We need three columns: Type, objectname and path. + Parts of the path which has to be translated to the local langauge (because of tree path) must begin with a colon. + Append the type to the combobox and the mapping table in the constructor. */ + + wxString databasePath = parent->GetNodePath(currentdb->GetDatabase()->GetId()); + wxString searchSQL = wxT("select * from ( ") + wxT(" SELECT ") + wxT(" case ") + wxT(" when c.relkind = 'r' then 'Tables' ") + wxT(" when c.relkind = 'S' then 'Sequences' ") + wxT(" when c.relkind = 'v' then 'Views' ") + wxT(" else 'should not happen' ") + wxT(" end as type, c.relname AS objectname, ") + wxT(" ':Schemas/' || n.nspname || '/' || ") + wxT(" case ") + wxT(" when c.relkind = 'r' then ':Tables' ") + wxT(" when c.relkind = 'S' then ':Sequences' ") + wxT(" when c.relkind = 'v' then ':Views' ") + wxT(" else 'should not happen' ") + wxT(" end || '/' || c.relname AS path ") + wxT(" FROM pg_class c ") + wxT(" LEFT JOIN pg_namespace n ON n.oid = c.relnamespace ") + wxT(" WHERE c.relkind in ('r','S','v') ") + wxT(" union ") + wxT(" SELECT 'Indexes', cls.relname, ':Schemas/' || n.nspname || '/:Tables/' || tab.relname || '/:Indexes/' || cls.relname ") + wxT(" FROM pg_index idx ") + wxT(" JOIN pg_class cls ON cls.oid=indexrelid ") + wxT(" JOIN pg_class tab ON tab.oid=indrelid ") + wxT(" JOIN pg_namespace n ON n.oid=tab.relnamespace ") + wxT(" LEFT JOIN pg_depend dep ON (dep.classid = cls.tableoid AND dep.objid = cls.oid AND dep.refobjsubid = '0' AND dep.refclassid=(SELECT oid FROM pg_class WHERE relname='pg_constraint') AND dep.deptype='i') ") + wxT(" LEFT OUTER JOIN pg_constraint con ON (con.tableoid = dep.refclassid AND con.oid = dep.refobjid) ") + wxT(" LEFT OUTER JOIN pg_description des ON des.objoid=cls.oid ") + wxT(" LEFT OUTER JOIN pg_description desp ON (desp.objoid=con.oid AND desp.objsubid = 0) ") + wxT(" where contype is null ") + wxT(" union ") + wxT(" select case when t.typname = 'trigger' then 'Trigger Functions' else 'Functions' end as type, p.proname, ") + wxT(" ':Schemas/' || n.nspname || '/' || case when t.typname = 'trigger' then ':Trigger Functions' else ':Functions' end || '/' || p.proname ") + wxT(" from pg_proc p ") + wxT(" left join pg_namespace n on p.pronamespace = n.oid ") + wxT(" left join pg_type t on p.prorettype = t.oid ") + wxT(" union ") + wxT(" select 'Schemas', nspname, ':Schemas/' || nspname from pg_namespace ") + wxT(" union ") + wxT(" select 'Columns', a.attname, ") + wxT(" ':Schemas/' || n.nspname || '/' || ") + wxT(" case ") + wxT(" when t.relkind = 'r' then ':Tables' ") + wxT(" when t.relkind = 'S' then ':Sequences' ") + wxT(" when t.relkind = 'v' then ':Views' ") + wxT(" else 'should not happen' ") + wxT(" end || '/' || t.relname || '/:Columns/' || a.attname AS path ") + wxT(" from pg_attribute a ") + wxT(" inner join pg_class t on a.attrelid = t.oid and t.relkind in ('r','v') ") + wxT(" left join pg_namespace n on t.relnamespace = n.oid where a.attnum > 0 ") + wxT(" union ") + wxT(" select 'Constraints', case when tf.relname is null then c.conname else c.conname || ' -> ' || tf.relname end, ':Schemas/' || n.nspname||'/:Tables/'||t.relname||'/:Constraints/'||case when tf.relname is null then c.conname else c.conname || ' -> ' || tf.relname end from pg_constraint c ") + wxT(" left join pg_class t on c.conrelid = t.oid ") + wxT(" left join pg_class tf on c.confrelid = tf.oid ") + wxT(" left join pg_namespace n on t.relnamespace = n.oid ") + wxT(" union ") + wxT(" select 'Rules', r.rulename, ':Schemas/' || n.nspname||case when t.relkind = 'v' then '/:Views/' else '/:Tables/' end||t.relname||'/:Rules/'|| r.rulename from pg_rewrite r ") + wxT(" left join pg_class t on r.ev_class = t.oid ") + wxT(" left join pg_namespace n on t.relnamespace = n.oid ") + wxT(" union ") + wxT(" select 'Triggers', tr.tgname, ':Schemas/' || n.nspname||case when t.relkind = 'v' then '/:Views/' else '/:Tables/' end||t.relname || '/:Triggers/' || tr.tgname from pg_trigger tr ") + wxT(" left join pg_class t on tr.tgrelid = t.oid ") + wxT(" left join pg_namespace n on t.relnamespace = n.oid ") + wxT(" where "); + if(currentdb->BackendMinimumVersion(9,0)) + { + searchSQL += wxT(" tr.tgisinternal = false "); + } + else { + searchSQL += wxT(" tr.tgisconstraint = false "); + } + searchSQL += wxT(" union ") + wxT(" SELECT 'Types', t.typname, ':Schemas/' || n.nspname || '/:Types' || t.typname ") + wxT(" FROM pg_type t ") + wxT(" LEFT OUTER JOIN pg_type e ON e.oid=t.typelem ") + wxT(" LEFT OUTER JOIN pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c' ") + wxT(" LEFT OUTER JOIN pg_namespace n on t.typnamespace = n.oid ") + wxT(" WHERE t.typtype != 'd' AND t.typname NOT LIKE E'\\_%' ") + wxT(" union ") + wxT(" SELECT 'Conversions', co.conname, ':Schemas/' || n.nspname || '/:Conversions/' || co.conname ") + wxT(" FROM pg_conversion co ") + wxT(" JOIN pg_namespace n ON n.oid=co.connamespace ") + wxT(" LEFT OUTER JOIN pg_description des ON des.objoid=co.oid AND des.objsubid=0 ") + wxT(" union ") + wxT(" SELECT 'Casts', format_type(st.oid,NULL) ||'->'|| format_type(tt.oid,tt.typtypmod), ':Casts/' || format_type(st.oid,NULL) ||'->'|| format_type(tt.oid,tt.typtypmod) ") + wxT(" FROM pg_cast ca ") + wxT(" JOIN pg_type st ON st.oid=castsource ") + wxT(" JOIN pg_type tt ON tt.oid=casttarget ") + wxT(" union ") + wxT(" SELECT 'Languages', lanname, ':Languages/' || lanname ") + wxT(" FROM pg_language lan ") + wxT(" WHERE lanispl IS TRUE ") + wxT(" union ") + wxT(" SELECT 'FTS Configurations', cfg.cfgname, ':Schemas/' || n.nspname || '/:FTS Configurations/' || cfg.cfgname ") + wxT(" FROM pg_ts_config cfg ") + wxT(" left join pg_namespace n on cfg.cfgnamespace = n.oid ") + wxT(" union ") + wxT(" SELECT 'FTS Dictionaries', dict.dictname, ':Schemas/' || ns.nspname || '/:FTS Dictionaries/' || dict.dictname ") + wxT(" FROM pg_ts_dict dict ") + wxT(" left join pg_namespace ns on dict.dictnamespace = ns.oid ") + wxT(" union ") + wxT(" SELECT 'FTS Parsers', prs.prsname, ':Schemas/' || ns.nspname || '/:FTS Parsers/' || prs.prsname ") + wxT(" FROM pg_ts_parser prs ") + wxT(" left join pg_namespace ns on prs.prsnamespace = ns.oid ") + wxT(" union ") + wxT(" SELECT 'FTS Templates', tmpl.tmplname, ':Schemas/' || ns.nspname || '/:FTS Templates/' || tmpl.tmplname ") + wxT(" FROM pg_ts_template tmpl ") + wxT(" left join pg_namespace ns on tmpl.tmplnamespace = ns.oid ") + wxT(" union ") + wxT(" select 'Domains', t.typname, ':Schemas/' || n.nspname || '/:Domains/' || t.typname from pg_type t ") + wxT(" inner join pg_namespace n on t.typnamespace = n.oid ") + wxT(" where t.typtype = 'd' ") + wxT(" union ") + wxT(" select 'Aggregates', pr.proname, ':Schemas/' || ns.nspname || '/:Aggregates/' || pr.proname from pg_catalog.pg_aggregate ag ") + wxT(" inner join pg_proc pr on ag.aggfnoid = pr.oid ") + wxT(" left join pg_namespace ns on pr.pronamespace = ns.oid ") + wxT(" union ") + wxT(" select case when rolcanlogin = true then 'Login Roles' else 'Group Roles' end, rolname, case when rolcanlogin = true then ':Login Roles' else ':Group Roles' end || '/' || rolname ") + wxT(" from pg_roles ") + wxT(" union ") + wxT(" select 'Tablespaces', spcname, ':Tablespaces/'||spcname from pg_tablespace ") + wxT(" union ") + wxT(" SELECT 'Operators', op.oprname, ':Schemas/' || ns.nspname || '/:Operators/' || op.oprname ") + wxT(" FROM pg_operator op ") + wxT(" left join pg_namespace ns on op.oprnamespace = ns.oid ") + wxT(" union ") + wxT(" SELECT 'Operator Classes', op.opcname, ':Schemas/' || ns.nspname || '/:Operator Classes/' || op.opcname ") + wxT(" FROM pg_opclass op ") + wxT(" left join pg_namespace ns on op.opcnamespace = ns.oid ") + wxT(" union ") + wxT(" SELECT 'Operator Families', opf.opfname, ':Schemas/' || ns.nspname || '/:Operator Families/' || opf.opfname ") + wxT(" FROM pg_opfamily opf ") + wxT(" left join pg_namespace ns on opf.opfnamespace = ns.oid "); + + + if(currentdb->BackendMinimumVersion(8,4)) + { + searchSQL += wxT(" union ") + wxT(" select 'Foreign Data Wrappers', fdwname, ':Foreign Data Wrappers/' || fdwname from pg_foreign_data_wrapper ") + wxT(" union ") + wxT(" select 'Foreign Server', sr.srvname, ':Foreign Data Wrappers/' || fdw.fdwname || '/:Foreign Servers/' || sr.srvname from pg_foreign_server sr ") + wxT(" inner join pg_foreign_data_wrapper fdw on sr.srvfdw = fdw.oid ") + wxT(" union ") + wxT(" select 'User Mappings', ro.rolname, ':Foreign Data Wrappers/' || fdw.fdwname || '/:Foreign Servers/' || sr.srvname || '/:User Mappings/' || ro.rolname from pg_user_mapping um ") + wxT(" inner join pg_roles ro on um.umuser = ro.oid ") + wxT(" inner join pg_foreign_server sr on um.umserver = sr.oid ") + wxT(" inner join pg_foreign_data_wrapper fdw on sr.srvfdw = fdw.oid "); + } + + if(currentdb->BackendMinimumVersion(9,1)) + { + searchSQL += wxT(" union ") + wxT(" select 'Foreign Tables', c.relname, ':Schemas/' || ns.nspname || '/:Foreign Tables/' || c.relname from pg_foreign_table ft ") + wxT(" inner join pg_class c on ft.ftrelid = c.oid ") + wxT(" inner join pg_namespace ns on c.relnamespace = ns.oid ") + wxT(" union ") + wxT(" select 'Extensions', x.extname, ':Extensions/' || x.extname ") + wxT(" FROM pg_extension x ") + wxT(" JOIN pg_namespace n on x.extnamespace=n.oid ") + wxT(" join pg_available_extensions() e(name, default_version, comment) ON x.extname=e.name ") + wxT(" union ") + wxT(" SELECT 'Collations', c.collname, ':Schemas/' || n.nspname || '/:Collations/' || c.collname ") + wxT(" FROM pg_collation c ") + wxT(" JOIN pg_namespace n ON n.oid=c.collnamespace "); + } + + searchSQL += wxT(") i ") + wxT("where lower(i.objectname) like '%") + txtPattern->GetValue().Lower() + wxT("%' "); + if(cbType->GetValue() != _("All types")) + { + searchSQL += wxT("AND i.type = '") + aMap[cbType->GetValue()] + wxT("' "); + } + searchSQL += wxT("order by 1,2;"); + + + + + pgSet *set = currentdb->GetConnection()->ExecuteSet(searchSQL); + int i = 0; + if(set) + { + lcResults->DeleteAllItems(); + + while(!set->Eof()) + { + wxString objectType = set->GetVal(wxT("type")); + wxString objectName = set->GetVal(wxT("objectname")); + wxString ItemPath; + + + /* Login Roles, Group Roles and Tablespaces are "outside" the database, so we have to adjust the path */ + if(objectType == wxT("Login Roles") || objectType == wxT("Group Roles") || objectType == wxT("Tablespaces")) + { + wxStringTokenizer tkz(databasePath, wxT("/")); + wxString newPath; + while(tkz.HasMoreTokens()) + { + wxString token = tkz.GetNextToken(); + if(token == _("Databases")) + break; + ItemPath += token + wxT("/"); + } + ItemPath += set->GetVal(wxT("path")); + } + else + { + ItemPath = databasePath + wxT("/") + set->GetVal(wxT("path")); + } + + + if(ItemPath.Contains(wxT("Schemas/information_schema"))) + { + /* In information Schema only views and columns are displayed, nothing else */ + + if(objectType == wxT("Views") || objectType == wxT("Columns")) + { + ItemPath.Replace(wxT(":Schemas/information_schema"), wxT(":Catalogs/ANSI/:Catalog Objects")); + ItemPath.Replace(wxT(":Views/"), wxT("")); + } + else { + set->MoveNext(); + continue; + } + + } + if(ItemPath.Contains(wxT("Schemas/pg_catalog"))) + { + ItemPath.Replace(wxT(":Schemas/pg_catalog"), wxT(":Catalogs/PostgreSQL")); + } + + wxListItem item; + item.SetId(i); + lcResults->InsertItem(item); + + wxString locTypeStr = wxGetTranslation(set->GetVal(wxT("type"))); + + /* Check if viewing of the sepcified object is enabled in settings */ + if(!settings->GetDisplayOption(locTypeStr)) + { + lcResults->SetItemTextColour(i, wxColour(128,128,128)); + } + + lcResults->SetItem(i, 0, locTypeStr); + lcResults->SetItem(i, 1, objectName); + lcResults->SetItem(i, 2, TranslatePath(ItemPath)); + set->MoveNext(); + i++; + } + delete set; + } + lcResults->SetColumnWidth(0, wxLIST_AUTOSIZE); + lcResults->SetColumnWidth(1, wxLIST_AUTOSIZE); + lcResults->SetColumnWidth(2, wxLIST_AUTOSIZE); + + + +} + +wxString dlgSearchObject::TranslatePath(wxString &path) +{ + /* Translate a path, but only word that start's with a colon (:) */ + wxStringTokenizer tkz(path, wxT("/")); + wxString newPath; + while(tkz.HasMoreTokens()) + { + wxString token = tkz.GetNextToken(); + if(token.StartsWith(wxT(":"))) + { + token = wxGetTranslation(token.AfterFirst(':')); + } + newPath = newPath + token.Trim() + wxT("/"); + } + return newPath.BeforeLast('/'); + +} + +void dlgSearchObject::OnCancel(wxCommandEvent &ev) +{ + if (IsModal()) + EndModal(wxID_CANCEL); + else + Destroy(); +} + + +searchObjectFactory::searchObjectFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar) : contextActionFactory(list) +{ + mnu->Append(id, _("&Search Object\tCtrl-G"), _("Search database for specific object")); +} + + +wxWindow *searchObjectFactory::StartDialog(frmMain *form, pgObject *obj) +{ + + dlgSearchObject *so = new dlgSearchObject(form, (pgDatabase *) obj); + so->Show(); + return 0; +} +bool searchObjectFactory::CheckEnable(pgObject *obj) +{ + if (obj && obj->IsCreatedBy(databaseFactory)) + return ((pgDatabase *)obj)->GetConnected() && (((pgDatabase *)obj)->GetName() != ((pgDatabase *)obj)->GetServer()->GetDatabaseName()); + + return false; +} + + diff --git a/pgadmin/frm/frmMain.cpp b/pgadmin/frm/frmMain.cpp index 0bd7dd7..190857f 100644 --- a/pgadmin/frm/frmMain.cpp +++ b/pgadmin/frm/frmMain.cpp @@ -67,6 +67,7 @@ #include "frm/frmEditGrid.h" #include "dlg/dlgServer.h" #include "dlg/dlgDatabase.h" +#include "dlg/dlgSearchObject.h" #include "schema/pgTable.h" #include "schema/pgFunction.h" #include "schema/pgIndex.h" @@ -147,6 +148,8 @@ frmMain::frmMain(const wxString &title) dependencies = new ctlListView(listViews, CTL_DEPVIEW, wxDefaultPosition, wxDefaultSize, wxSIMPLE_BORDER); dependents = new ctlListView(listViews, CTL_REFVIEW, wxDefaultPosition, wxDefaultSize, wxSIMPLE_BORDER); + + // Switch back to the native list control. #ifdef __WXMAC__ wxSystemOptions::SetOption(wxT("mac.listctrl.always_use_generic"), false); @@ -353,6 +356,7 @@ void frmMain::CreateMenus() new resetTableStatsFactory(menuFactories, editMenu, 0); new resetFunctionStatsFactory(menuFactories, editMenu, 0); new reassignDropOwnedFactory(menuFactories, editMenu, 0); + new searchObjectFactory(menuFactories, editMenu, 0); editMenu->AppendSeparator(); new separatorFactory(menuFactories); @@ -649,12 +653,13 @@ void frmMain::ExpandChildNodes(wxTreeItemId node, wxArrayString &expandedNodes) wxString frmMain::GetNodePath(wxTreeItemId node) { wxString path; - path = browser->GetItemText(node).BeforeFirst('('); - + path = browser->GetItemText(node).BeforeFirst('(').Trim().Trim(false); + wxTreeItemId parent = browser->GetItemParent(node); while (parent.IsOk()) { - path = browser->GetItemText(parent).BeforeFirst('(') + wxT("/") + path; + browser->GetItemData(parent); + path = browser->GetItemText(parent).BeforeFirst('(').Trim().Trim(false) + wxT("/") + path; parent = browser->GetItemParent(parent); } @@ -668,22 +673,34 @@ wxString frmMain::GetCurrentNodePath() } // Attempt to reselect the node with the given path -bool frmMain::SetCurrentNode(wxTreeItemId node, const wxString &path) +bool frmMain::SetCurrentNode(wxTreeItemId node, const wxString &origPath) { + wxString path = origPath.Lower(); wxTreeItemIdValue cookie; wxTreeItemId child = browser->GetFirstChild(node, cookie); - + + while (child.IsOk()) { - if (GetNodePath(child) == path) - { - browser->SelectItem(child, true); - return true; - } - else if (SetCurrentNode(child, path)) - return true; + wxString actNodePath = GetNodePath(child).Lower(); - child = browser->GetNextChild(node, cookie); + if(path.StartsWith(actNodePath)) + { + if(!browser->IsExpanded(child)) { + browser->SelectItem(child, true); + browser->Expand(child); + } + + if (actNodePath == path) + { + browser->SelectItem(child, true); + return true; + } + else if (SetCurrentNode(child, path)) + return true; + } + child = browser->GetNextChild(node, cookie); + } return false; diff --git a/pgadmin/frm/module.mk b/pgadmin/frm/module.mk index 5ea77e4..1f7c54f 100644 --- a/pgadmin/frm/module.mk +++ b/pgadmin/frm/module.mk @@ -29,7 +29,7 @@ pgadmin3_SOURCES += \ $(srcdir)/frm/frmPgpassConfig.cpp \ $(srcdir)/frm/frmQuery.cpp \ $(srcdir)/frm/frmReport.cpp \ - $(srcdir)/frm/frmRestore.cpp \ + $(srcdir)/frm/frmRestore.cpp \ $(srcdir)/frm/frmSplash.cpp \ $(srcdir)/frm/frmStatus.cpp \ $(srcdir)/frm/plugins.cpp diff --git a/pgadmin/include/dlg/dlgSearchObject.h b/pgadmin/include/dlg/dlgSearchObject.h new file mode 100644 index 0000000..08e37ba --- /dev/null +++ b/pgadmin/include/dlg/dlgSearchObject.h @@ -0,0 +1,78 @@ +////////////////////////////////////////////////////////////////////////// +// +// pgAdmin III - PostgreSQL Tools +// +// Copyright (C) 2002 - 2011, The pgAdmin Development Team +// This software is released under the PostgreSQL Licence +// +// dlgSearchObject.h - Search dialogue +// +////////////////////////////////////////////////////////////////////////// + +#ifndef DLGSEARCHOBJECT_H +#define DLGSEARCHOBJECT_H + +#include "dlg/dlgClasses.h" +#include "ctl/ctlListView.h" +#include "schema/pgDatabase.h" +#include "utils/sysSettings.h" + +// Class declarations +class dlgSearchObject : public pgDialog +{ +public: + dlgSearchObject(frmMain *p, pgDatabase *db); + ~dlgSearchObject(); + +private: + void OnHelp(wxCommandEvent &ev); + void OnSearch(wxCommandEvent &ev); + void OnCancel(wxCommandEvent &ev); + void OnChange(wxCommandEvent &ev); + void OnSelSearchResult(wxListEvent &ev); + wxString TranslatePath(wxString &path); + WX_DECLARE_STRING_HASH_MAP(wxString, LngMapping); + LngMapping aMap; + + pgDatabase *currentdb; + frmMain *parent; + wxString header; + wxArrayString sectionName, sectionData, sectionTableHeader, sectionTableRows, sectionTableInfo, sectionSql; + + DECLARE_EVENT_TABLE() +}; + +/////////////////////////////////////////////////////// +// Search Object Factory base class +/////////////////////////////////////////////////////// +class searchObjectBaseFactory : public actionFactory +{ +private: + searchObjectBaseFactory(menuFactoryList *list) : actionFactory(list) {} + frmMain *GetFrmMain() + { + return parent; + }; + + frmMain *parent; +public: + bool CheckEnable(pgObject *obj) + { + return false; + }; +}; + + +class searchObjectFactory : public contextActionFactory +{ +public: + searchObjectFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar); + wxWindow *StartDialog(frmMain *form, pgObject *obj); + bool CheckEnable(pgObject *obj); + +}; + + + + +#endif diff --git a/pgadmin/include/frm/frmMain.h b/pgadmin/include/frm/frmMain.h index 455f253..3783b9c 100644 --- a/pgadmin/include/frm/frmMain.h +++ b/pgadmin/include/frm/frmMain.h @@ -154,6 +154,7 @@ public: void UpdateAllMacrosList(); void SetItemBackgroundColour(wxTreeItemId item, wxColour colour); + wxString GetNodePath(wxTreeItemId node); private: wxAuiManager manager; @@ -233,7 +234,7 @@ private: void GetExpandedChildNodes(wxTreeItemId node, wxArrayString &expandedNodes); void ExpandChildNodes(wxTreeItemId node, wxArrayString &expandedNodes); - wxString GetNodePath(wxTreeItemId node); + void PopulatePluginButtonMenu(wxCommandEvent &event); diff --git a/pgadmin/include/frm/module.mk b/pgadmin/include/frm/module.mk index 8a03e5c..f7b8d79 100644 --- a/pgadmin/include/frm/module.mk +++ b/pgadmin/include/frm/module.mk @@ -28,7 +28,7 @@ pgadmin3_SOURCES += \ $(srcdir)/include/frm/frmPgpassConfig.h \ $(srcdir)/include/frm/frmQuery.h \ $(srcdir)/include/frm/frmReport.h \ - $(srcdir)/include/frm/frmRestore.h \ + $(srcdir)/include/frm/frmRestore.h \ $(srcdir)/include/frm/frmSplash.h \ $(srcdir)/include/frm/frmStatus.h \ $(srcdir)/include/frm/menu.h diff --git a/pgadmin/include/schema/pgDatabase.h b/pgadmin/include/schema/pgDatabase.h index bef2e3b..22628ee 100644 --- a/pgadmin/include/schema/pgDatabase.h +++ b/pgadmin/include/schema/pgDatabase.h @@ -351,6 +351,4 @@ public: bool CanCreate(); }; - - #endif diff --git a/pgadmin/schema/pgDatabase.cpp b/pgadmin/schema/pgDatabase.cpp index 3657274..e4a3bd3 100644 --- a/pgadmin/schema/pgDatabase.cpp +++ b/pgadmin/schema/pgDatabase.cpp @@ -1242,3 +1242,4 @@ bool disconnectDatabaseFactory::CheckEnable(pgObject *obj) return false; } + diff --git a/pgadmin/schema/pgForeignTable.cpp b/pgadmin/schema/pgForeignTable.cpp index 141d2b1..764a6d0 100644 --- a/pgadmin/schema/pgForeignTable.cpp +++ b/pgadmin/schema/pgForeignTable.cpp @@ -219,6 +219,10 @@ pgObject *pgForeignTable::Refresh(ctlTree *browser, const wxTreeItemId item) void pgForeignTable::iSetOptions(const wxString &tmpoptions) { + + if(tmpoptions == wxT("")) + return; + wxString tmp; wxString option; wxString value; diff --git a/pgadmin/ui/dlgSearchObject.xrc b/pgadmin/ui/dlgSearchObject.xrc new file mode 100644 index 0000000..fa03c59 --- /dev/null +++ b/pgadmin/ui/dlgSearchObject.xrc @@ -0,0 +1,100 @@ + + + + Search Objects + 353,40d + + + 2 + 4 + 3 + 3 + 1 + 2 + + + + + wxALIGN_CENTRE_VERTICAL|wxTOP|wxLEFT|wxRIGHT + 4 + + + + 2 + 1 + 5 + 5 + 0 + 2 + + + 200,-1d + Enter part of the object's name you're looking for + + wxEXPAND|wxALIGN_CENTER_VERTICAL|wxTOP|wxLEFT|wxRIGHT + 4 + + + + + 1 + 300,14d + + + wxALIGN_CENTRE_VERTICAL|wxTOP|wxLEFT|wxRIGHT + 4 + + wxEXPAND|wxALIGN_CENTER_VERTICAL|wxTOP|wxLEFT|wxRIGHT + 4 + + wxEXPAND|wxALIGN_CENTER_VERTICAL|wxTOP|wxLEFT|wxRIGHT + 4 + + + + + + wxALIGN_CENTRE_VERTICAL|wxTOP|wxLEFT|wxRIGHT + 4 + + + + 196,-1d + + + + wxEXPAND|wxALIGN_CENTER_VERTICAL|wxTOP|wxLEFT|wxRIGHT + 4 + + + + + + wxALIGN_CENTRE_VERTICAL|wxTOP|wxLEFT|wxRIGHT + 4 + + + + 10,80 + 340,150d + + + wxEXPAND|wxALIGN_CENTER_VERTICAL|wxTOP|wxLEFT|wxRIGHT|wxBOTTOM + 4 + + + + + wxALL|wxALIGN_RIGHT + + + + + + 0 + + wxALL|wxALIGN_RIGHT + + + + \ No newline at end of file diff --git a/pgadmin/ui/module.mk b/pgadmin/ui/module.mk index 21a62bb..7a0841c 100644 --- a/pgadmin/ui/module.mk +++ b/pgadmin/ui/module.mk @@ -61,6 +61,7 @@ TMP_ui += \ $(srcdir)/ui/dlgRule.xrc \ $(srcdir)/ui/dlgSchedule.xrc \ $(srcdir)/ui/dlgSchema.xrc \ + $(srcdir)/ui/dlgSearchObject.xrc \ $(srcdir)/ui/dlgSelectConnection.xrc \ $(srcdir)/ui/dlgSequence.xrc \ $(srcdir)/ui/dlgServer.xrc \ @@ -87,7 +88,7 @@ TMP_ui += \ $(srcdir)/ui/frmOptions.xrc \ $(srcdir)/ui/frmPassword.xrc \ $(srcdir)/ui/frmReport.xrc \ - $(srcdir)/ui/frmRestore.xrc + $(srcdir)/ui/frmRestore.xrc EXTRA_DIST += \ $(srcdir)/ui/module.mk \ -- 1.7.3.1.msysgit.0