From 7205b3d93f93eef6706b49e48764a9d1489b55e5 Mon Sep 17 00:00:00 2001
From: Jasmin Dizdarevic <jasmin.dizdarevic@gmail.com>
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
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 @@
+<html>
+
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
+<link rel="STYLESHEET" type="text/css" href="pgadmin3.css">
+<title>Search</title>
+</head>
+
+<body>
+
+<h3>Object search</h3>
+<center><img src="images/search.png"></center><br/>
+<p>
+With this GUI you can search for almost any kind of objects in a database and the corresponding cluster.
+</p>
+<p>
+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.
+</p>
+<p>
+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.
+</p>
+
+</body>
+
+</html>
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 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<resource>
+  <object class="wxDialog" name="dlgSearchObject">
+    <title>Search Objects</title>
+    <size>353,40d</size>
+    <style>wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER</style>
+    <object class="wxFlexGridSizer">
+      <cols>2</cols>
+      <rows>4</rows>
+      <vgap>3</vgap>
+      <hgap>3</hgap>
+      <growablecols>1</growablecols>
+      <growablerows>2</growablerows>
+      <object class="sizeritem">
+        <object class="wxStaticText" name="stFilename">
+          <label>Pattern</label>
+        </object>
+        <flag>wxALIGN_CENTRE_VERTICAL|wxTOP|wxLEFT|wxRIGHT</flag>
+        <border>4</border>
+      </object>
+      <object class="sizeritem">
+        <object class="wxFlexGridSizer">
+          <cols>2</cols>
+          <rows>1</rows>
+          <vgap>5</vgap>
+          <hgap>5</hgap>
+          <growablecols>0</growablecols>
+          <growablerows>2</growablerows>
+          <object class="sizeritem">
+            <object class="wxTextCtrl" name="txtPattern">              
+              <size>200,-1d</size>
+              <tooltip>Enter part of the object's name you're looking for</tooltip>
+            </object>
+            <flag>wxEXPAND|wxALIGN_CENTER_VERTICAL|wxTOP|wxLEFT|wxRIGHT</flag>
+            <border>4</border>
+          </object>
+          <object class="sizeritem">
+            <object class="wxButton" name="btnSearch">
+              <label>Find</label>
+              <default>1</default>
+              <pos>300,14d</pos>
+              <style></style>
+            </object>
+            <flag>wxALIGN_CENTRE_VERTICAL|wxTOP|wxLEFT|wxRIGHT</flag>
+            <border>4</border>
+          </object>
+          <flag>wxEXPAND|wxALIGN_CENTER_VERTICAL|wxTOP|wxLEFT|wxRIGHT</flag>
+          <border>4</border>
+        </object>
+        <flag>wxEXPAND|wxALIGN_CENTER_VERTICAL|wxTOP|wxLEFT|wxRIGHT</flag>
+        <border>4</border>
+      </object>
+      <object class="sizeritem">
+        <object class="wxStaticText" name="stFormat">
+          <label>Type</label>
+        </object>
+        <flag>wxALIGN_CENTRE_VERTICAL|wxTOP|wxLEFT|wxRIGHT</flag>
+        <border>4</border>
+      </object>
+      <object class="sizeritem">
+        <object class="wxComboBox" name="cbType">          
+          <size>196,-1d</size>
+          <content/>
+          <style>wxCB_READONLY|wxCB_DROPDOWN</style>
+        </object>
+        <flag>wxEXPAND|wxALIGN_CENTER_VERTICAL|wxTOP|wxLEFT|wxRIGHT</flag>
+        <border>4</border>
+      </object>
+      <object class="sizeritem">
+        <object class="wxStaticText" name="stCompressRatio">
+          <label>Result</label>
+        </object>
+        <flag>wxALIGN_CENTRE_VERTICAL|wxTOP|wxLEFT|wxRIGHT</flag>
+        <border>4</border>
+      </object>
+      <object class="sizeritem">
+        <object class="wxListCtrl" name="lcResults">
+          <pos>10,80</pos>
+          <size>340,150d</size>
+          <style>wxLC_REPORT</style>
+        </object>
+        <flag>wxEXPAND|wxALIGN_CENTER_VERTICAL|wxTOP|wxLEFT|wxRIGHT|wxBOTTOM</flag>
+        <border>4</border>
+      </object>    
+      <object class="sizeritem">
+        <object class="wxButton" name="wxID_HELP">
+          <label>&amp;Help</label>
+          <flag>wxALL|wxALIGN_RIGHT</flag>
+        </object>
+      </object>
+      <object class="sizeritem">
+        <object class="wxButton" name="wxID_CANCEL">
+          <label>&amp;Close</label>
+          <default>0</default>          
+        </object>
+        <flag>wxALL|wxALIGN_RIGHT</flag>
+      </object>
+    </object>
+  </object>
+</resource>
\ 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
