mirror of
https://github.com/levinsv/pgadmin3.git
synced 2026-05-15 14:15:49 -06:00
1. Сортировка колонок на вкладках Статистика сохряняется по возможности. 2. Узлы плана которые помечены как (never executed) не подсвечиваются. 3. При построении плана всегда добавляется опция "SUMMARY on" 4. Исправлено не корректное отображение зависимостей для таблиц из публикаций. 5. В отчетах о статистике добавлена итоговая информация по таблицам отчета.
2404 lines
73 KiB
C++
2404 lines
73 KiB
C++
//////////////////////////////////////////////////////////////////////////
|
||
//
|
||
// pgAdmin III - PostgreSQL Tools
|
||
//
|
||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||
// This software is released under the PostgreSQL Licence
|
||
//
|
||
// pgObject.cpp - PostgreSQL object base class
|
||
//
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
// wxWindows headers
|
||
#include <wx/wx.h>
|
||
|
||
// App headers
|
||
#include "pgAdmin3.h"
|
||
#include "utils/misc.h"
|
||
#include "schema/pgObject.h"
|
||
#include "schema/pgServer.h"
|
||
#include "frm/frmMain.h"
|
||
#include "frm/frmReport.h"
|
||
#include "schema/pgDomain.h"
|
||
#include "schema/pgAggregate.h"
|
||
#include "schema/pgSequence.h"
|
||
#include "schema/pgFunction.h"
|
||
#include "schema/pgType.h"
|
||
#include "schema/pgDatabase.h"
|
||
#include "schema/pgTable.h"
|
||
#include "schema/pgColumn.h"
|
||
#include "schema/pgView.h"
|
||
#include "schema/pgType.h"
|
||
#include "schema/pgOperator.h"
|
||
#include "schema/pgLanguage.h"
|
||
#include "schema/pgPublication.h"
|
||
#include "schema/pgConversion.h"
|
||
#include "schema/pgTablespace.h"
|
||
#include "schema/pgGroup.h"
|
||
#include "schema/pgUser.h"
|
||
#include "schema/pgUserMapping.h"
|
||
#include "schema/pgIndex.h"
|
||
#include "schema/pgTrigger.h"
|
||
#include "schema/pgCheck.h"
|
||
#include "schema/pgIndexConstraint.h"
|
||
#include "schema/pgForeignKey.h"
|
||
#include "schema/pgForeignDataWrapper.h"
|
||
#include "schema/pgForeignServer.h"
|
||
#include "schema/pgForeignTable.h"
|
||
#include "schema/pgRule.h"
|
||
#include "schema/pgRole.h"
|
||
#include "schema/pgCast.h"
|
||
#include "schema/pgCatalogObject.h"
|
||
#include "schema/pgTextSearchConfiguration.h"
|
||
#include "schema/pgTextSearchDictionary.h"
|
||
#include "schema/pgTextSearchParser.h"
|
||
#include "schema/pgTextSearchTemplate.h"
|
||
#include "schema/pgOperatorClass.h"
|
||
#include "schema/pgOperatorFamily.h"
|
||
#include "schema/pgSchema.h"
|
||
#include "schema/pgIndexConstraint.h"
|
||
#include "schema/pgExtension.h"
|
||
#include "schema/edbPackage.h"
|
||
#include "schema/edbSynonym.h"
|
||
#include "schema/pgCollation.h"
|
||
#include "utils/pgDefs.h"
|
||
#include "schema/gpExtTable.h"
|
||
#include "schema/gpResQueue.h"
|
||
#include "agent/pgaJob.h"
|
||
#include "agent/pgaSchedule.h"
|
||
#include "agent/pgaStep.h"
|
||
#include "schema/pgPartition.h"
|
||
#include "utils/pgfeatures.h"
|
||
|
||
|
||
int pgObject::GetType() const
|
||
{
|
||
if (factory)
|
||
return factory->GetId();
|
||
return type;
|
||
}
|
||
|
||
|
||
int pgObject::GetMetaType() const
|
||
{
|
||
if (factory)
|
||
return factory->GetMetaType();
|
||
return PGM_UNKNOWN;
|
||
}
|
||
|
||
|
||
wxString pgObject::GetTypeName() const
|
||
{
|
||
return factory->GetTypeName();
|
||
}
|
||
|
||
|
||
wxString pgObject::GetTranslatedTypeName() const
|
||
{
|
||
return wxString(wxGetTranslation(GetTypeName()));
|
||
}
|
||
|
||
|
||
wxString pgObject::GetTranslatedMessage(int kindOfMessage) const
|
||
{
|
||
wxString message = wxEmptyString;
|
||
|
||
switch (kindOfMessage)
|
||
{
|
||
case RETRIEVINGDETAILS:
|
||
message = _("Retrieving details on unknown object of type");
|
||
break;
|
||
case REFRESHINGDETAILS:
|
||
message = _("Refreshing unknown object of type");
|
||
break;
|
||
case BACKUPGLOBALS:
|
||
message = _("Backup globals of unknown object of type");
|
||
break;
|
||
case BACKUPSERVERTITLE:
|
||
message = _("Backup unknown object of type");
|
||
break;
|
||
case DROPEXCLUDINGDEPS:
|
||
message = wxString::Format(_("Are you sure you wish to drop object \"%s\"?"),
|
||
GetFullIdentifier().c_str());
|
||
break;
|
||
case DROPTITLE:
|
||
message = _("Drop object?");
|
||
break;
|
||
case BACKUPTITLE:
|
||
message = wxString::Format(_("Backup \"%s\""),
|
||
GetFullIdentifier().c_str());
|
||
break;
|
||
case RESTORETITLE:
|
||
message = wxString::Format(_("Restore \"%s\""),
|
||
GetFullIdentifier().c_str());
|
||
break;
|
||
}
|
||
//message += wxT(" ") + factory->GetTypeName();
|
||
|
||
return message;
|
||
}
|
||
|
||
|
||
int pgObject::GetIconId()
|
||
{
|
||
int id = -1;
|
||
if (factory)
|
||
id = factory->GetIconId();
|
||
|
||
wxASSERT(id != -1);
|
||
return id;
|
||
}
|
||
|
||
|
||
int pgObject::GetTypeId(const wxString &typname)
|
||
{
|
||
pgaFactory *factory = pgaFactory::GetFactory(typname);
|
||
if (factory)
|
||
return factory->GetId();
|
||
|
||
return -1;
|
||
}
|
||
|
||
|
||
pgObject::pgObject(pgaFactory &_factory, const wxString &newName)
|
||
: wxTreeItemData(), oid(0)
|
||
{
|
||
factory = &_factory;
|
||
|
||
if (factory->IsCollection() && newName.IsEmpty())
|
||
name = factory->GetTypeName();
|
||
else
|
||
name = newName;
|
||
|
||
type = factory->GetId();
|
||
expandedKids = false;
|
||
needReread = false;
|
||
hintShown = false;
|
||
dlg = NULL;
|
||
}
|
||
|
||
|
||
pgObject::pgObject(int newType, const wxString &newName)
|
||
: wxTreeItemData(), oid(0)
|
||
{
|
||
factory = pgaFactory::GetFactory(newType);
|
||
|
||
// Set the typename and type
|
||
type = newType;
|
||
|
||
if (newName.IsEmpty())
|
||
name = factory->GetTypeName();
|
||
else
|
||
name = newName;
|
||
expandedKids = false;
|
||
needReread = false;
|
||
hintShown = false;
|
||
dlg = NULL;
|
||
}
|
||
|
||
|
||
void pgObject::AppendMenu(wxMenu *menu, int type)
|
||
{
|
||
if (menu)
|
||
factory->AppendMenu(menu);
|
||
}
|
||
|
||
|
||
wxString pgObject::GetHelpPage(bool forCreate) const
|
||
{
|
||
wxString page;
|
||
|
||
if (!IsCollection())
|
||
page = wxT("pg/sql-create") + GetTypeName().Lower();
|
||
|
||
return page;
|
||
}
|
||
|
||
|
||
wxMenu *pgObject::GetNewMenu()
|
||
{
|
||
wxMenu *menu = new wxMenu();
|
||
if (CanCreate())
|
||
AppendMenu(menu);
|
||
return menu;
|
||
}
|
||
|
||
void pgObject::ShowStatistics(frmMain *form, ctlListView *statistics)
|
||
{
|
||
}
|
||
|
||
void pgObject::ShowStatisticsTables(frmMain* form, ctlListView* statistics, pgObject *obj)
|
||
{
|
||
wxLogInfo(wxT("Displaying statistics for tables on %s"), obj->GetSchema()->GetIdentifier().c_str());
|
||
bool tabcoll = false;
|
||
bool partcoll = false;
|
||
bool onetable = false;
|
||
if (IsCollection()) {
|
||
wxString t = obj->GetFactory()->GetTypeName();
|
||
if (t == ("Tables")
|
||
//obj->IsCreatedBy(tableFactory)
|
||
) tabcoll = true;
|
||
if ( t == ("Partitions")) partcoll = true;
|
||
}
|
||
else if (obj->GetConnection()->BackendMinimumVersion(12, 0)) onetable = true;
|
||
bool hasSize = obj->GetConnection()->HasFeature(FEATURE_SIZE);
|
||
|
||
// Add the statistics view columns
|
||
statistics->ClearAll();
|
||
statistics->AddColumn(_("Table Name"));
|
||
if (hasSize)
|
||
statistics->AddColumn(_("Size"), wxLIST_AUTOSIZE);
|
||
if (obj->GetConnection()->GetIsPgProEnt()) statistics->AddColumn(_("CFS %"), wxLIST_AUTOSIZE);
|
||
statistics->AddColumn(_("Live tuples"));
|
||
statistics->AddColumn(_("Tuples inserted"));
|
||
statistics->AddColumn(_("Tuples updated"));
|
||
statistics->AddColumn(_("Tuples deleted"));
|
||
if (obj->GetConnection()->BackendMinimumVersion(8, 3))
|
||
{
|
||
statistics->AddColumn(_("Tuples HOT updated"));
|
||
statistics->AddColumn(_("Dead tuples"));
|
||
}
|
||
if (obj->GetConnection()->BackendMinimumVersion(8, 2))
|
||
{
|
||
statistics->AddColumn(_("Last vacuum"));
|
||
statistics->AddColumn(_("Last autovacuum"));
|
||
statistics->AddColumn(_("Last analyze"));
|
||
statistics->AddColumn(_("Last autoanalyze"));
|
||
}
|
||
if (obj->GetConnection()->BackendMinimumVersion(9, 1))
|
||
{
|
||
statistics->AddColumn(_("Vacuum counter"));
|
||
statistics->AddColumn(_("Autovacuum counter"));
|
||
statistics->AddColumn(_("Analyze counter"));
|
||
statistics->AddColumn(_("Autoanalyze counter"));
|
||
}
|
||
|
||
wxString sql = wxT("SELECT st.relname, n_tup_ins, n_tup_upd, n_tup_del");
|
||
if (obj->GetConnection()->BackendMinimumVersion(8, 3))
|
||
sql += wxT(", n_tup_hot_upd, n_live_tup, n_dead_tup");
|
||
if (obj->GetConnection()->BackendMinimumVersion(8, 2))
|
||
sql += wxT(", last_vacuum, last_autovacuum, last_analyze, last_autoanalyze");
|
||
if (obj->GetConnection()->BackendMinimumVersion(9, 1))
|
||
sql += wxT(", vacuum_count, autovacuum_count, analyze_count, autoanalyze_count");
|
||
if (hasSize)
|
||
sql += wxT(", pg_size_pretty(pg_relation_size(st.relid)")
|
||
wxT(" + CASE WHEN cl.reltoastrelid = 0 THEN 0 ELSE pg_relation_size(cl.reltoastrelid) + COALESCE((SELECT SUM(pg_relation_size(indexrelid)) FROM pg_index WHERE indrelid=cl.reltoastrelid)::int8, 0) END")
|
||
wxT(" + COALESCE((SELECT SUM(pg_relation_size(indexrelid)) FROM pg_index WHERE indrelid=st.relid)::int8, 0)) AS size");
|
||
if (obj->GetConnection()->GetIsPgProEnt()) sql += wxT(",left((cfs_fragmentation(cl.oid)*100)::text,5)::text AS cfs_ratio");
|
||
sql += wxT("\n FROM pg_stat_all_tables st")
|
||
wxT(" JOIN pg_class cl on cl.oid=st.relid\n");
|
||
if (partcoll) sql += wxT(" JOIN pg_inherits i ON (cl.oid = i.inhrelid) WHERE ");
|
||
if (tabcoll) sql += wxT(" WHERE schemaname = ")+obj->qtDbString(obj->GetSchema()->GetName());
|
||
if (partcoll) sql += wxT(" i.inhparent = ") + obj->GetOidStr();
|
||
//+ obj->qtDbString(obj->GetSchema()->GetName()) + wxT(" AND i.inhparent = ") + obj->GetOidStr()
|
||
if (onetable) sql += wxT("join (select t.relid::oid oid from pg_partition_tree(")+ (obj->GetOidStr())+wxT("::regclass) t where t.isleaf = 't') t on t.oid=cl.oid");
|
||
|
||
sql += wxT("\n ORDER BY relname");
|
||
|
||
pgSet* stats = obj->GetDatabase()->ExecuteSet(sql);
|
||
|
||
if (stats)
|
||
{
|
||
long pos = 0;
|
||
int i;
|
||
while (!stats->Eof())
|
||
{
|
||
i = 1;
|
||
statistics->InsertItem(pos, stats->GetVal(wxT("relname")), PGICON_STATISTICS);
|
||
if (hasSize)
|
||
statistics->SetItem(pos, i++, stats->GetVal(wxT("size")));
|
||
if (obj->GetConnection()->GetIsPgProEnt()) statistics->SetItem(pos, i++, stats->GetVal(wxT("cfs_ratio")));
|
||
statistics->SetItem(pos, i++, stats->GetVal(wxT("n_live_tup")));
|
||
statistics->SetItem(pos, i++, stats->GetVal(wxT("n_tup_ins")));
|
||
statistics->SetItem(pos, i++, stats->GetVal(wxT("n_tup_upd")));
|
||
statistics->SetItem(pos, i++, stats->GetVal(wxT("n_tup_del")));
|
||
if (obj->GetConnection()->BackendMinimumVersion(8, 3))
|
||
{
|
||
statistics->SetItem(pos, i++, stats->GetVal(wxT("n_tup_hot_upd")));
|
||
statistics->SetItem(pos, i++, stats->GetVal(wxT("n_dead_tup")));
|
||
}
|
||
if (obj->GetConnection()->BackendMinimumVersion(8, 2))
|
||
{
|
||
statistics->SetItem(pos, i++, stats->GetVal(wxT("last_vacuum")));
|
||
statistics->SetItem(pos, i++, stats->GetVal(wxT("last_autovacuum")));
|
||
statistics->SetItem(pos, i++, stats->GetVal(wxT("last_analyze")));
|
||
statistics->SetItem(pos, i++, stats->GetVal(wxT("last_autoanalyze")));
|
||
if (stats->GetVal(wxT("last_analyze")).IsEmpty() && stats->GetVal(wxT("last_autoanalyze")).IsEmpty())
|
||
statistics->SetItemBackgroundColour(pos, wxColour(wxT("#EEAAAA")));
|
||
}
|
||
if (obj->GetConnection()->BackendMinimumVersion(9, 1))
|
||
{
|
||
statistics->SetItem(pos, i++, stats->GetVal(wxT("vacuum_count")));
|
||
statistics->SetItem(pos, i++, stats->GetVal(wxT("autovacuum_count")));
|
||
statistics->SetItem(pos, i++, stats->GetVal(wxT("analyze_count")));
|
||
statistics->SetItem(pos, i++, stats->GetVal(wxT("autoanalyze_count")));
|
||
}
|
||
stats->MoveNext();
|
||
pos++;
|
||
}
|
||
|
||
delete stats;
|
||
}
|
||
statistics->SetColumnWidth(0, wxLIST_AUTOSIZE);
|
||
statistics->ReSort();
|
||
}
|
||
|
||
|
||
bool pgObject::UpdateIcon(ctlTree *browser)
|
||
{
|
||
int icon = GetIconId();
|
||
if (GetId() && browser->GetItemImage(GetId(), wxTreeItemIcon_Normal) != icon)
|
||
{
|
||
browser->SetItemImage(GetId(), GetIconId(), wxTreeItemIcon_Normal);
|
||
browser->SetItemImage(GetId(), GetIconId(), wxTreeItemIcon_Selected);
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
|
||
void pgObject::ShowDependency(pgDatabase *db, ctlListView *list, const wxString &query, const wxString &clsorder)
|
||
{
|
||
list->ClearAll();
|
||
list->AddColumn(_("Type"), 60);
|
||
list->AddColumn(_("Name"), 100);
|
||
list->AddColumn(_("Restriction"), 50);
|
||
|
||
pgConn *conn = GetConnection();
|
||
if (conn)
|
||
{
|
||
pgSet *set;
|
||
// currently missing:
|
||
// - pg_cast
|
||
// - pg_operator
|
||
// - pg_opclass
|
||
|
||
// not being implemented:
|
||
// - pg_index (done by pg_class)
|
||
wxString q=query + wxT("\n")
|
||
wxT(" AND ") + clsorder + wxT(" IN (\n")
|
||
wxT(" SELECT oid FROM pg_class\n")
|
||
wxT(" WHERE relname IN ('pg_class', 'pg_constraint', 'pg_conversion', 'pg_language', 'pg_proc', 'pg_extension', \n")
|
||
wxT(" 'pg_rewrite', 'pg_namespace', 'pg_trigger', 'pg_type', 'pg_attrdef', 'pg_event_trigger','pg_publication_rel','pg_subscription_rel'))\n")
|
||
wxT(" ORDER BY ") + clsorder + wxT(", cl.relkind");
|
||
set = conn->ExecuteSet(q);
|
||
|
||
if (set)
|
||
{
|
||
while (!set->Eof())
|
||
{
|
||
wxString refname;
|
||
wxString _refname = set->GetVal(wxT("refname"));
|
||
|
||
if (db)
|
||
refname = db->GetQuotedSchemaPrefix(set->GetVal(wxT("nspname")));
|
||
else
|
||
{
|
||
refname = qtIdent(set->GetVal(wxT("nspname")));
|
||
if (!refname.IsEmpty())
|
||
refname += wxT(".");
|
||
}
|
||
|
||
wxString typestr = set->GetVal(wxT("type"));
|
||
pgaFactory *depFactory = 0;
|
||
int icon=-1;
|
||
if (typestr.Length() > 0) {
|
||
switch ((wxChar)typestr.c_str()[0])
|
||
{
|
||
case 'c':
|
||
case 's': // we don't know these; internally handled
|
||
case 't':
|
||
set->MoveNext();
|
||
continue;
|
||
|
||
case 'r':
|
||
{
|
||
if (StrToLong(typestr.Mid(1)) > 0)
|
||
depFactory = &columnFactory;
|
||
else
|
||
depFactory = &tableFactory;
|
||
break;
|
||
}
|
||
case 'i':
|
||
depFactory = &indexFactory;
|
||
break;
|
||
case 'I':
|
||
depFactory = &indexFactory;
|
||
break;
|
||
case 'E':
|
||
depFactory = &extensionFactory;
|
||
break;
|
||
|
||
case 'S':
|
||
depFactory = &sequenceFactory;
|
||
break;
|
||
case 'v':
|
||
depFactory = &viewFactory;
|
||
break;
|
||
case 'm':
|
||
depFactory = &viewFactory;
|
||
icon = viewFactory.GetMaterializedIconId();
|
||
break;
|
||
case 'x':
|
||
depFactory = &extTableFactory;
|
||
break;
|
||
case 'p':
|
||
depFactory = &functionFactory;
|
||
break;
|
||
case 'n':
|
||
depFactory = &schemaFactory;
|
||
break;
|
||
case 'y':
|
||
depFactory = &typeFactory;
|
||
break;
|
||
case 'T':
|
||
depFactory = &triggerFactory;
|
||
break;
|
||
case 'f':
|
||
depFactory = &foreignTableFactory;
|
||
break;
|
||
|
||
case 'l':
|
||
depFactory = &languageFactory;
|
||
break;
|
||
case 'P':
|
||
depFactory = &publicationFactory;
|
||
break;
|
||
case 'R':
|
||
{
|
||
refname = _refname + wxT(" ON ") + refname + set->GetVal(wxT("ownertable"));
|
||
_refname = wxEmptyString;
|
||
depFactory = &ruleFactory;
|
||
break;
|
||
}
|
||
case 'C':
|
||
{
|
||
switch ((wxChar)typestr.c_str()[1])
|
||
{
|
||
case 'c':
|
||
depFactory = &checkFactory;
|
||
break;
|
||
case 'f':
|
||
refname += set->GetVal(wxT("ownertable")) + wxT(".");
|
||
depFactory = &foreignKeyFactory;
|
||
break;
|
||
case 'p':
|
||
depFactory = &primaryKeyFactory;
|
||
break;
|
||
case 'u':
|
||
depFactory = &uniqueFactory;
|
||
break;
|
||
case 'x':
|
||
depFactory = &excludeFactory;
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
break;
|
||
}
|
||
case 'A':
|
||
{
|
||
// Include only functions
|
||
if (set->GetVal(wxT("adbin")).StartsWith(wxT("{FUNCEXPR")))
|
||
{
|
||
depFactory = &functionFactory;
|
||
refname = set->GetVal(wxT("adsrc"));
|
||
break;
|
||
}
|
||
else
|
||
{
|
||
set->MoveNext();
|
||
continue;
|
||
}
|
||
}
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
refname += _refname;
|
||
|
||
wxString typname;
|
||
if (depFactory)
|
||
{
|
||
typname = depFactory->GetTypeName();
|
||
if (icon==-1) icon = depFactory->GetIconId();
|
||
}
|
||
else
|
||
{
|
||
typname = _("Unknown");
|
||
icon = -1;
|
||
}
|
||
|
||
wxString deptype;
|
||
|
||
switch ( (wxChar) set->GetVal(wxT("deptype")).c_str()[0])
|
||
{
|
||
case 'n':
|
||
deptype = wxT("normal");
|
||
break;
|
||
case 'a':
|
||
deptype = wxT("auto");
|
||
break;
|
||
case 'i':
|
||
{
|
||
if (settings->GetShowSystemObjects())
|
||
deptype = wxT("internal");
|
||
else
|
||
{
|
||
set->MoveNext();
|
||
continue;
|
||
}
|
||
break;
|
||
}
|
||
case 'p':
|
||
deptype = wxT("pin");
|
||
typname = wxEmptyString;
|
||
break;
|
||
case 'S':
|
||
deptype = wxT("sec_partition");
|
||
break;
|
||
case 'P':
|
||
deptype = wxT("pri_partition");
|
||
break;
|
||
case 'e':
|
||
deptype = _("extension");
|
||
break;
|
||
case 'x':
|
||
deptype = _("auto");
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
|
||
list->AppendItem(icon, typname, refname, deptype);
|
||
set->MoveNext();
|
||
}
|
||
delete set;
|
||
}
|
||
}
|
||
}
|
||
|
||
void pgObject::CreateList3Columns(ctlListView *list, const wxString &left, const wxString &middle, const wxString &right)
|
||
{
|
||
list->ClearAll();
|
||
list->AddColumn(left, 80);
|
||
list->AddColumn(middle, 80);
|
||
list->AddColumn(right, list->GetSize().GetWidth() - 170);
|
||
}
|
||
|
||
|
||
void pgObject::CreateListColumns(ctlListView *list, const wxString &left, const wxString &right)
|
||
{
|
||
list->ClearAll();
|
||
list->AddColumn(left, 130);
|
||
list->AddColumn(right, list->GetSize().GetWidth() - 140);
|
||
}
|
||
|
||
wxString GetClassTableName(int metatype) {
|
||
switch (metatype)
|
||
{
|
||
case PGM_CONSTRAINT:
|
||
return "pg_constraint";
|
||
case PGM_FOREIGNKEY:
|
||
return "pg_constraint";
|
||
case PGM_PRIMARYKEY:
|
||
return "pg_constraint";
|
||
case PGM_UNIQUE:
|
||
return "pg_constraint";
|
||
case PGM_EXCLUDE:
|
||
return "pg_constraint";
|
||
case PGM_OPCLASS:
|
||
return "pg_amop";
|
||
case PGM_COLUMN:
|
||
return "pg_attribute";
|
||
case PGM_FUNCTION:
|
||
return "pg_proc";
|
||
case PGM_INDEX:
|
||
return "pg_class";
|
||
case PGM_TABLE:
|
||
return "pg_class";
|
||
case PGM_VIEW:
|
||
return "pg_class";
|
||
case PG_PARTITION:
|
||
return "pg_class";
|
||
case PGM_LANGUAGE:
|
||
return "pg_language";
|
||
case PGM_ROLE:
|
||
return "pg_authid";
|
||
case PGM_SCHEMA:
|
||
return "pg_namespace";
|
||
case PGM_SEQUENCE:
|
||
return "pg_class";
|
||
case PGM_TABLESPACE:
|
||
return "pg_tablespace";
|
||
case PGM_TRIGGER:
|
||
return "pg_trigger";
|
||
case PGM_OPFAMILY:
|
||
return "pg_opfamily";
|
||
default:
|
||
break;
|
||
}
|
||
return "";
|
||
}
|
||
|
||
void pgObject::ShowDependencies(frmMain *form, ctlListView *Dependencies, const wxString &wh)
|
||
{
|
||
if (this->IsCollection())
|
||
return;
|
||
|
||
// Bail out if this is a pgAgent object, as they use the OID for IDs
|
||
if (GetMetaType() == PGM_JOB || GetMetaType() == PGM_SCHEDULE || GetMetaType() == PGM_STEP || GetMetaType() == PGM_PROJOB)
|
||
return;
|
||
|
||
wxString where;
|
||
if (wh.IsEmpty())
|
||
{
|
||
if (!GetOidStr().IsSameAs(wxT("0")))
|
||
{
|
||
wxString ts = GetClassTableName(GetMetaType());
|
||
where = wxT(" WHERE dep.objid=") + GetOidStr();
|
||
if (!ts.IsEmpty()) where += " and dep.classid IN(select oid from pg_class where oid=dep.classid and relname='" + ts + "')";
|
||
}
|
||
else
|
||
return;
|
||
}
|
||
else
|
||
where = wh;
|
||
/*
|
||
* Behavior of concatinating operator (||) is different for EnterpriseDB.
|
||
* For the following query:
|
||
* SELECT a || null;
|
||
* When selected postgresql compatible mode, it will return null.
|
||
* And when selected oracle compatible mode, it will return 'a'.
|
||
*
|
||
* Hence, the following query may or may not work for EnterpriseDB depending on the
|
||
* compatiblity mode:
|
||
* COALESCE(cl.relname || '.' || att.attname, cl.relname, co.conname, pr.proname,
|
||
* tg.tgname, ty.typname, la.lanname, rw.rulename, ns.nspname)
|
||
*
|
||
* Instead of that, we will use the following query to run it correctly everytime:
|
||
* CASE WHEN cl.relname IS NOT NULL AND att.attname IS NOT NULL
|
||
* THEN cl.relname || '.' || att.attname
|
||
* ELSE COALESCE(cl.relname, co.conname, pr.proname, tg.tgname, ty.typname,
|
||
* la.lanname, rw.rulename, ns.nspname)
|
||
* END
|
||
*/
|
||
ShowDependency(GetDatabase(), Dependencies,
|
||
wxT("SELECT DISTINCT dep.deptype, dep.refclassid, cl.relkind, ad.adbin, pg_get_expr(ad.adbin,ad.adrelid) adsrc, \n")
|
||
wxT(" CASE WHEN cl.relkind IS NOT NULL THEN cl.relkind::text || COALESCE(dep.refobjsubid::text, '')\n")
|
||
wxT(" WHEN tg.oid IS NOT NULL THEN 'T'::text\n")
|
||
wxT(" WHEN ty.oid IS NOT NULL THEN 'y'::text\n")
|
||
wxT(" WHEN ns.oid IS NOT NULL THEN 'n'::text\n")
|
||
wxT(" WHEN pr.oid IS NOT NULL THEN 'p'::text\n")
|
||
wxT(" WHEN la.oid IS NOT NULL THEN 'l'::text\n")
|
||
wxT(" WHEN rw.oid IS NOT NULL THEN 'R'::text\n")
|
||
wxT(" WHEN co.oid IS NOT NULL THEN 'C'::text || contype::text\n")
|
||
wxT(" WHEN ad.oid IS NOT NULL THEN 'A'::text\n")
|
||
wxT(" WHEN ext.oid IS NOT NULL THEN 'E'::text\n")
|
||
wxT(" WHEN pub.oid IS NOT NULL THEN 'r'::text\n")
|
||
wxT(" WHEN pub2.oid IS NOT NULL THEN 'P'::text\n")
|
||
wxT(" ELSE '' END AS type,\n")
|
||
wxT(" COALESCE(coc.relname, clrw.relname) AS ownertable,\n")
|
||
wxT(" CASE WHEN cl.relname IS NOT NULL AND att.attname IS NOT NULL THEN cl.relname || '.' || att.attname\n")
|
||
wxT(" ELSE COALESCE(ext.extname,cl.relname, co.conname, pr.proname, tg.tgname, ty.typname, la.lanname, rw.rulename, ns.nspname,pub.prrelid::regclass::text,pubname.pubname)\n")
|
||
wxT(" END AS refname,\n")
|
||
wxT(" COALESCE(nsc.nspname, nso.nspname, nsp.nspname, nst.nspname, nsrw.nspname) AS nspname\n")
|
||
wxT(" FROM pg_depend dep join pg_class nc on nc.oid=dep.refclassid\n")
|
||
wxT(" LEFT JOIN pg_class cl ON dep.refobjid=cl.oid and nc.relname='pg_class'\n")
|
||
wxT(" LEFT JOIN pg_attribute att ON dep.refobjid=att.attrelid AND dep.refobjsubid=att.attnum and nc.relname='pg_attribute'\n")
|
||
wxT(" LEFT JOIN pg_namespace nsc ON cl.relnamespace=nsc.oid\n")
|
||
wxT(" LEFT JOIN pg_proc pr ON dep.refobjid=pr.oid and nc.relname='pg_proc'\n")
|
||
wxT(" LEFT JOIN pg_namespace nsp ON pr.pronamespace=nsp.oid\n")
|
||
wxT(" LEFT JOIN pg_trigger tg ON dep.refobjid=tg.oid and nc.relname='pg_trigger'\n")
|
||
wxT(" LEFT JOIN pg_type ty ON dep.refobjid=ty.oid and nc.relname='pg_type'\n")
|
||
wxT(" LEFT JOIN pg_namespace nst ON ty.typnamespace=nst.oid\n")
|
||
wxT(" LEFT JOIN pg_constraint co ON dep.refobjid=co.oid and nc.relname='pg_constraint'\n")
|
||
wxT(" LEFT JOIN pg_class coc ON co.conrelid=coc.oid\n")
|
||
wxT(" LEFT JOIN pg_namespace nso ON co.connamespace=nso.oid\n")
|
||
wxT(" LEFT JOIN pg_rewrite rw ON dep.refobjid=rw.oid and nc.relname='pg_rewrite'\n")
|
||
wxT(" LEFT JOIN pg_class clrw ON clrw.oid=rw.ev_class\n")
|
||
wxT(" LEFT JOIN pg_namespace nsrw ON clrw.relnamespace=nsrw.oid\n")
|
||
wxT(" LEFT JOIN pg_language la ON dep.refobjid=la.oid and nc.relname='pg_language'\n")
|
||
wxT(" LEFT JOIN pg_namespace ns ON dep.refobjid=ns.oid and nc.relname='pg_namespace'\n")
|
||
wxT(" LEFT JOIN pg_attrdef ad ON ad.adrelid=att.attrelid AND ad.adnum=att.attnum\n")
|
||
wxT(" LEFT JOIN pg_publication_rel pub ON dep.objid=pub.oid AND pub.prpubid=dep.refobjid and nc.relname='pg_publication_rel'\n")
|
||
wxT(" LEFT JOIN pg_publication_rel pub2 ON dep.objid=pub2.oid and nc.relname='pg_publication_rel'\n")
|
||
wxT(" LEFT JOIN pg_publication pubname on pubname.oid=pub2.prpubid \n")
|
||
wxT(" LEFT JOIN pg_extension ext ON ext.oid=dep.refobjid\n")
|
||
+ where, wxT("refclassid"));
|
||
|
||
pgConn *conn = GetConnection();
|
||
if (conn)
|
||
{
|
||
if (where.Find(wxT("subid")) < 0 && conn->BackendMinimumVersion(8, 1))
|
||
{
|
||
int iconId = groupRoleFactory.GetCollectionFactory()->GetIconId();
|
||
pgSetIterator set(conn,
|
||
wxT("SELECT rolname AS refname, refclassid, deptype\n")
|
||
wxT(" FROM pg_shdepend dep\n")
|
||
wxT(" LEFT JOIN pg_roles r ON refclassid=1260 AND refobjid=r.oid\n")
|
||
+ where + wxT("\n")
|
||
wxT(" ORDER BY 1"));
|
||
|
||
while (set.RowsLeft())
|
||
{
|
||
wxString refname = set.GetVal(wxT("refname"));
|
||
wxString deptype = set.GetVal(wxT("deptype"));
|
||
if (deptype == wxT("a"))
|
||
deptype = wxT("ACL");
|
||
else if (deptype == wxT("o"))
|
||
deptype = _("Owner");
|
||
|
||
if (set.GetOid(wxT("refclassid")) == PGOID_CLASS_PG_AUTHID)
|
||
Dependencies->AppendItem(iconId, wxT("Role"), refname, deptype);
|
||
}
|
||
}
|
||
/*
|
||
*
|
||
* A Corner case, reported by Guillaume Lelarge, could be found at:
|
||
* http://archives.postgresql.org/pgadmin-hackers/2009-03/msg00026.php
|
||
*
|
||
* SQL:
|
||
* CREATE TABLE t1 (id serial);
|
||
* CREATE TABLE t2 (LIKE t1 INCLUDING DEFAULTS);
|
||
*
|
||
* When we try to drop the table t1, it gives the following notice:
|
||
* "NOTICE: default for table t2 column id depends on sequence t1_id_seq"
|
||
*
|
||
* This suggests that the column 't2.id' should be shown in the "Dependency" list
|
||
* of the sequence 't1_seq_id'
|
||
*
|
||
* As we could not find any direct relationship between 't1_seq_id' and 't2'
|
||
* table, we come up with this solution.
|
||
*
|
||
*/
|
||
if (GetMetaType() == PGM_SEQUENCE)
|
||
{
|
||
int iconId = columnFactory.GetIconId();
|
||
/*
|
||
* Behavior of concatinating operator (||) is different for EnterpriseDB.
|
||
* For the following query:
|
||
* SELECT a || null;
|
||
* When selected postgresql compatible mode, it will return null.
|
||
* And when selected oracle compatible mode, it will return 'a'.
|
||
*
|
||
* Hence, the following query may or may not work for EnterpriseDB depending on the
|
||
* compatiblity mode:
|
||
* COALESCE(ref.relname || '.' || att.attname, ref.relname)
|
||
*
|
||
* Instead of that, we will use the following query to run it correctly everytime:
|
||
* CASE WHEN ref.relname IS NOT NULL AND att.attname IS NOT NULL
|
||
* THEN ref.relname || '.' || att.attname
|
||
* ELSE ref.relname
|
||
* END
|
||
*/
|
||
pgSetIterator set(conn,
|
||
wxT("SELECT \n")
|
||
wxT(" CASE WHEN att.attname IS NOT NULL AND ref.relname IS NOT NULL THEN ref.relname || '.' || att.attname\n")
|
||
wxT(" ELSE ref.relname \n")
|
||
wxT(" END AS refname, \n")
|
||
wxT(" d2.refclassid, d1.deptype AS deptype\n")
|
||
wxT("FROM pg_depend d1\n")
|
||
wxT(" LEFT JOIN pg_depend d2 ON d1.objid=d2.objid AND d1.refobjid != d2.refobjid\n")
|
||
wxT(" LEFT JOIN pg_class ref ON ref.oid = d2.refobjid\n")
|
||
wxT(" LEFT JOIN pg_attribute att ON d2.refobjid=att.attrelid AND d2.refobjsubid=att.attnum\n")
|
||
wxT("WHERE d1.classid=(SELECT oid FROM pg_class WHERE relname='pg_attrdef')\n")
|
||
wxT(" AND d2.refobjid NOT IN (SELECT d3.refobjid FROM pg_depend d3 WHERE d3.objid=d1.refobjid)\n")
|
||
wxT(" AND d1.refobjid=") + GetOidStr());
|
||
while (set.RowsLeft())
|
||
{
|
||
wxString refname = set.GetVal(wxT("refname"));
|
||
wxString deptype = set.GetVal(wxT("deptype"));
|
||
if (deptype == wxT("n"))
|
||
deptype = wxT("normal");
|
||
else if (deptype == wxT("i"))
|
||
deptype = _("internal");
|
||
else if (deptype == wxT("a"))
|
||
deptype = _("auto");
|
||
|
||
Dependencies->AppendItem(iconId, wxT("Column"), refname, deptype);
|
||
}
|
||
}
|
||
}
|
||
|
||
}
|
||
void pgObject::ShowDependents(frmMain* form, ctlListView* referencedBy, const wxString& wh)
|
||
{
|
||
if (this->IsCollection())
|
||
return;
|
||
|
||
// Bail out if this is a pgAgent object, as they use the OID for IDs
|
||
if (GetMetaType() == PGM_JOB || GetMetaType() == PGM_SCHEDULE || GetMetaType() == PGM_STEP || GetMetaType() == PGM_PROJOB)
|
||
return;
|
||
|
||
wxString where;
|
||
if (wh.IsEmpty())
|
||
{
|
||
wxString ts = GetClassTableName(GetMetaType());
|
||
where = wxT(" WHERE dep.refobjid=") + GetOidStr();
|
||
if (!ts.IsEmpty()) where += " and dep.refclassid IN(select oid from pg_class where oid=dep.refclassid and relname='" + ts + "')";
|
||
}
|
||
else
|
||
where = wh;
|
||
/*
|
||
* Behavior of concatinating operator (||) is different for EnterpriseDB.
|
||
* For the following query:
|
||
* SELECT a || null;
|
||
* When selected postgresql compatible mode, it will return null.
|
||
* And when selected oracle compatible mode, it will return 'a'.
|
||
*
|
||
* Hence, the following query may or may not work for EnterpriseDB depending on the
|
||
* compatiblity mode:
|
||
* COALESCE(cl.relname || '.' || att.attname, cl.relname, co.conname, pr.proname,
|
||
* tg.tgname, ty.typname, la.lanname, rw.rulename, ns.nspname)
|
||
*
|
||
* Instead of that, we will use the following query to run it correctly everytime:
|
||
* CASE WHEN cl.relname IS NOT NULL AND att.attname IS NOT NULL
|
||
* THEN cl.relname || '.' || att.attname
|
||
* ELSE COALESCE(cl.relname, co.conname, pr.proname, tg.tgname, ty.typname,
|
||
* la.lanname, rw.rulename, ns.nspname)
|
||
* END
|
||
*/
|
||
ShowDependency(GetDatabase(), referencedBy,
|
||
wxT("SELECT DISTINCT dep.deptype, dep.classid, cl.relkind, ad.adbin, pg_get_expr(ad.adbin,ad.adrelid) adsrc, \n")
|
||
wxT(" CASE WHEN cl.relkind IS NOT NULL THEN cl.relkind::text || COALESCE(dep.objsubid::text, '')\n")
|
||
wxT(" WHEN tg.oid IS NOT NULL THEN 'T'::text\n")
|
||
wxT(" WHEN ty.oid IS NOT NULL THEN 'y'::text\n")
|
||
wxT(" WHEN ns.oid IS NOT NULL THEN 'n'::text\n")
|
||
wxT(" WHEN pr.oid IS NOT NULL THEN 'p'::text\n")
|
||
wxT(" WHEN la.oid IS NOT NULL THEN 'l'::text\n")
|
||
wxT(" WHEN rw.oid IS NOT NULL THEN 'R'::text\n")
|
||
wxT(" WHEN co.oid IS NOT NULL THEN 'C'::text || contype::text\n")
|
||
wxT(" WHEN ad.oid IS NOT NULL THEN 'A'::text\n")
|
||
wxT(" WHEN pub.oid IS NOT NULL THEN 'r'::text\n")
|
||
wxT(" WHEN pub2.oid IS NOT NULL THEN 'P'::text\n")
|
||
wxT(" WHEN ext.oid IS NOT NULL THEN 'E'::text\n")
|
||
wxT(" ELSE '' END AS type,\n")
|
||
wxT(" COALESCE(coc.relname, clrw.relname) AS ownertable,\n")
|
||
wxT(" CASE WHEN cl.relname IS NOT NULL AND att.attname IS NOT NULL THEN cl.relname || '.' || att.attname \n")
|
||
wxT(" ELSE COALESCE(ext.extname,cl.relname, co.conname, pr.proname, tg.tgname, ty.typname, la.lanname, rw.rulename, ns.nspname,pub.prrelid::regclass::text,pubname.pubname) \n")
|
||
wxT(" END AS refname,\n")
|
||
wxT(" COALESCE(nsc.nspname, nso.nspname, nsp.nspname, nst.nspname, nsrw.nspname) AS nspname\n")
|
||
wxT(" FROM pg_depend dep join pg_class nc on nc.oid=dep.classid\n")
|
||
wxT(" LEFT JOIN pg_class cl ON dep.objid=cl.oid and nc.relname='pg_class'\n")
|
||
wxT(" LEFT JOIN pg_attribute att ON dep.objid=att.attrelid AND dep.objsubid=att.attnum and nc.relname='pg_attribute'\n")
|
||
wxT(" LEFT JOIN pg_namespace nsc ON cl.relnamespace=nsc.oid\n")
|
||
wxT(" LEFT JOIN pg_proc pr ON dep.objid=pr.oid and nc.relname='pg_proc'\n")
|
||
wxT(" LEFT JOIN pg_namespace nsp ON pr.pronamespace=nsp.oid\n")
|
||
wxT(" LEFT JOIN pg_trigger tg ON dep.objid=tg.oid and nc.relname='pg_trigger'\n")
|
||
wxT(" LEFT JOIN pg_type ty ON dep.objid=ty.oid and nc.relname='pg_type'\n")
|
||
wxT(" LEFT JOIN pg_namespace nst ON ty.typnamespace=nst.oid\n")
|
||
wxT(" LEFT JOIN pg_constraint co ON dep.objid=co.oid and nc.relname='pg_constraint'\n")
|
||
wxT(" LEFT JOIN pg_class coc ON co.conrelid=coc.oid\n")
|
||
wxT(" LEFT JOIN pg_namespace nso ON co.connamespace=nso.oid\n")
|
||
wxT(" LEFT JOIN pg_rewrite rw ON dep.objid=rw.oid and nc.relname='pg_rewrite'\n")
|
||
wxT(" LEFT JOIN pg_class clrw ON clrw.oid=rw.ev_class\n")
|
||
wxT(" LEFT JOIN pg_namespace nsrw ON clrw.relnamespace=nsrw.oid\n")
|
||
wxT(" LEFT JOIN pg_language la ON dep.objid=la.oid and nc.relname='pg_language'\n")
|
||
wxT(" LEFT JOIN pg_namespace ns ON dep.objid=ns.oid and nc.relname='pg_namespace'\n")
|
||
wxT(" LEFT JOIN pg_attrdef ad ON ad.oid=dep.objid and nc.relname='pg_attrdef'\n")
|
||
wxT(" LEFT JOIN pg_extension ext ON ext.oid=dep.objid and nc.relname='pg_extension'\n")
|
||
wxT(" LEFT JOIN pg_publication_rel pub ON dep.objid=pub.oid AND pub.prpubid=dep.refobjid and nc.relname='pg_publication_rel'\n")
|
||
wxT(" LEFT JOIN pg_publication_rel pub2 ON dep.objid=pub2.oid and nc.relname='pg_publication_rel'\n")
|
||
wxT(" LEFT JOIN pg_publication pubname on pubname.oid=pub2.prpubid \n")
|
||
+ where, wxT("classid"));
|
||
|
||
/*
|
||
*
|
||
* A Corner case, reported by Guillaume Lelarge, could be found at:
|
||
* http://archives.postgresql.org/pgadmin-hackers/2009-03/msg00026.php
|
||
*
|
||
* SQL:
|
||
* CREATE TABLE t1 (id serial);
|
||
* CREATE TABLE t2 (LIKE t1 INCLUDING DEFAULTS);
|
||
*
|
||
* When we try to drop the table t1, it gives the following notice:
|
||
* "NOTICE: default for table t2 column id depends on sequence t1_id_seq"
|
||
*
|
||
* This suggests that the sequence 't1_seq_id' should be shown in the
|
||
* "Dependents" list of the table 't2' and column 't2.id'
|
||
*
|
||
* As we could not find any direct relationship between 't1_seq_id' and 't2'
|
||
* table, we come up with this solution.
|
||
*
|
||
*/
|
||
pgConn *conn = GetConnection();
|
||
if (conn && (GetMetaType() == PGM_TABLE || GetMetaType() == PGM_COLUMN))
|
||
{
|
||
int iconId = sequenceFactory.GetIconId();
|
||
wxString strQuery =
|
||
wxT("SELECT ref.relname AS refname, d2.refclassid, dep.deptype AS deptype\n")
|
||
wxT(" FROM pg_depend dep\n")
|
||
wxT(" LEFT JOIN pg_depend d2 ON dep.objid=d2.objid AND dep.refobjid != d2.refobjid AND d2.classid=dep.classid\n")
|
||
wxT(" LEFT JOIN pg_class ref ON ref.oid=d2.refobjid\n")
|
||
wxT(" LEFT JOIN pg_attribute att ON d2.refclassid=att.attrelid AND d2.refobjsubid=att.attnum\n")
|
||
+ where +
|
||
wxT(" AND dep.classid=(SELECT oid FROM pg_class WHERE relname='pg_attrdef')\n")
|
||
wxT(" AND dep.refobjid NOT IN (SELECT d3.refobjid FROM pg_depend d3 WHERE d3.objid=d2.refobjid)");
|
||
|
||
|
||
pgSetIterator set(conn, strQuery);
|
||
|
||
while (set.RowsLeft())
|
||
{
|
||
wxString refname = set.GetVal(wxT("refname"));
|
||
if (refname.IsEmpty())
|
||
continue;
|
||
|
||
wxString deptype = set.GetVal(wxT("deptype"));
|
||
if (deptype == wxT("a"))
|
||
deptype = _("auto");
|
||
else if (deptype == wxT("n"))
|
||
deptype = _("normal");
|
||
else if (deptype == wxT("i"))
|
||
deptype = _("internal");
|
||
|
||
referencedBy->AppendItem(iconId, wxT("Sequence"), refname, deptype);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
void pgObject::ShowTree(frmMain *form, ctlTree *browser, ctlListView *properties, ctlSQLBox *sqlPane)
|
||
{
|
||
pgConn *conn = GetConnection();
|
||
if (conn)
|
||
{
|
||
int status = conn->GetStatus();
|
||
if (status == PGCONN_BROKEN || status == PGCONN_BAD)
|
||
{
|
||
form->SetStatusText(_(" Connection broken."));
|
||
return;
|
||
}
|
||
}
|
||
|
||
wxLogInfo(wxT("Displaying properties for %s %s"), GetTypeName().c_str(), GetIdentifier().c_str());
|
||
|
||
if (form)
|
||
{
|
||
form->StartMsg(GetTranslatedMessage(RETRIEVINGDETAILS));
|
||
|
||
SetContextInfo(form);
|
||
|
||
form->ShowObjStatistics(this);
|
||
}
|
||
|
||
ShowTreeDetail(browser, form, properties, sqlPane);
|
||
if (form)
|
||
form->EndMsg(!GetConnection() || GetConnection()->GetStatus() == PGCONN_OK);
|
||
}
|
||
|
||
|
||
wxTreeItemId pgObject::AppendBrowserItem(ctlTree *browser, pgObject *object)
|
||
{
|
||
return browser->AppendObject(this, object);
|
||
}
|
||
|
||
|
||
wxString pgObject::GetCommentSql()
|
||
{
|
||
wxString cmt;
|
||
if (!comment.IsNull())
|
||
{
|
||
cmt = wxT("COMMENT ON ") + GetTypeName().Upper() + wxT(" ") + GetQuotedFullIdentifier()
|
||
+ wxT("\n IS ") + qtDbString(comment) + wxT(";\n");
|
||
}
|
||
return cmt;
|
||
}
|
||
|
||
|
||
wxString pgObject::GetOwnerSql(int major, int minor, wxString objname, wxString objtype)
|
||
{
|
||
wxString sql;
|
||
if (GetConnection()->BackendMinimumVersion(major, minor))
|
||
{
|
||
// if (GetConnection()->GetUser() != owner) // optional?
|
||
{
|
||
if (objtype.IsEmpty())
|
||
objtype = GetTypeName().Upper();
|
||
|
||
if (objname.IsEmpty())
|
||
objname = objtype + wxT(" ") + GetQuotedFullIdentifier();
|
||
|
||
sql = wxT("ALTER ") + objname + wxT("\n OWNER TO ") + qtIdent(owner) + wxT(";\n");
|
||
}
|
||
}
|
||
return sql;
|
||
}
|
||
|
||
|
||
void pgObject::AppendRight(wxString &rights, const wxString &acl, wxChar c, const wxChar *rightName, const wxString &column)
|
||
{
|
||
if (acl.Find(c) >= 0)
|
||
{
|
||
if (!rights.IsNull())
|
||
rights.Append(wxT(", "));
|
||
rights.Append(rightName);
|
||
|
||
if (!column.IsEmpty())
|
||
rights.Append(wxT("(") + column + wxT(")"));
|
||
}
|
||
}
|
||
|
||
|
||
wxString pgObject::GetPrivilegeGrant(const wxString &allPattern, const wxString &acl, const wxString &grantOnObject, const wxString &user, const wxString &column)
|
||
{
|
||
wxString rights;
|
||
|
||
if (allPattern.Length() > 1 && acl == allPattern)
|
||
{
|
||
rights = wxT("ALL");
|
||
if (!column.IsEmpty())
|
||
rights += wxT("(") + column + wxT(")");
|
||
}
|
||
else
|
||
{
|
||
AppendRight(rights, acl, 'r', wxT("SELECT"), column);
|
||
AppendRight(rights, acl, 'w', wxT("UPDATE"), column);
|
||
AppendRight(rights, acl, 'a', wxT("INSERT"), column);
|
||
AppendRight(rights, acl, 'D', wxT("TRUNCATE"), column);
|
||
AppendRight(rights, acl, 'c', wxT("CONNECT"), column);
|
||
AppendRight(rights, acl, 'd', wxT("DELETE"), column);
|
||
AppendRight(rights, acl, 'R', wxT("RULE"), column);
|
||
AppendRight(rights, acl, 'x', wxT("REFERENCES"), column);
|
||
AppendRight(rights, acl, 't', wxT("TRIGGER"), column);
|
||
AppendRight(rights, acl, 'm', wxT("MAINTAIN"), column);
|
||
AppendRight(rights, acl, 'X', wxT("EXECUTE"), column);
|
||
AppendRight(rights, acl, 'U', wxT("USAGE"), column);
|
||
AppendRight(rights, acl, 'C', wxT("CREATE"), column);
|
||
AppendRight(rights, acl, 'T', wxT("TEMPORARY"), column);
|
||
}
|
||
wxString grant;
|
||
if (rights.IsNull())
|
||
{
|
||
grant += wxT("REVOKE ALL");
|
||
if (!column.IsEmpty())
|
||
grant += wxT("(") + column + wxT(")");
|
||
}
|
||
else
|
||
grant += wxT("GRANT ") + rights;
|
||
|
||
grant += wxT(" ON ") + grantOnObject;
|
||
|
||
if (rights.IsNull()) grant += wxT(" FROM ");
|
||
else grant += wxT(" TO ");
|
||
|
||
grant += user;
|
||
|
||
return grant;
|
||
}
|
||
|
||
|
||
wxArrayString pgObject::GetProviderLabelArray()
|
||
{
|
||
wxArrayString providersArray, labelsArray, seclabelsArray;
|
||
wxString currentChar;
|
||
wxString tmp;
|
||
bool wrappedInQuotes, antislash;
|
||
|
||
if (labels.IsEmpty())
|
||
return seclabelsArray;
|
||
|
||
// parse the labels string
|
||
// we start at 1 and stop at length-1 to get rid of the { and } of the array
|
||
for (unsigned int index = 1 ; index < labels.Length() - 1 ; index++)
|
||
{
|
||
// get current char
|
||
currentChar = labels.Mid(index, 1);
|
||
|
||
// if there is a double quote at the beginning of a label,
|
||
// the whole label will be wrapped in quotes
|
||
if (currentChar == wxT("\"") && tmp.IsEmpty())
|
||
wrappedInQuotes = true;
|
||
else if (currentChar == wxT("\\") && wrappedInQuotes)
|
||
antislash = true;
|
||
else
|
||
{
|
||
if ((currentChar == wxT(",") && !wrappedInQuotes && !tmp.IsEmpty())
|
||
|| (currentChar == wxT("\"") && wrappedInQuotes && !antislash && !tmp.IsEmpty()))
|
||
{
|
||
// put new label in the array
|
||
labelsArray.Add(tmp);
|
||
|
||
// reinit tmp
|
||
tmp = wxEmptyString;
|
||
wrappedInQuotes = false;
|
||
}
|
||
else
|
||
tmp += currentChar;
|
||
antislash = false;
|
||
}
|
||
}
|
||
|
||
// last label
|
||
if (!tmp.IsEmpty())
|
||
{
|
||
// put last label in the array
|
||
labelsArray.Add(tmp);
|
||
}
|
||
|
||
// reinit tmp
|
||
tmp = wxEmptyString;
|
||
wrappedInQuotes = false;
|
||
|
||
// parse the providers string
|
||
// we start at 1 and stop at length-1 to get rid of the { and } of the array
|
||
for (unsigned int index = 1 ; index < providers.Length() - 1 ; index++)
|
||
{
|
||
// get current char
|
||
currentChar = providers.Mid(index, 1);
|
||
|
||
// if there is a double quote at the beginning of a provider,
|
||
// the whole provider will be wrapped in quotes
|
||
if (currentChar == wxT("\"") && tmp.IsEmpty())
|
||
wrappedInQuotes = true;
|
||
else if (currentChar == wxT("\\") && wrappedInQuotes)
|
||
antislash = true;
|
||
else
|
||
{
|
||
if ((currentChar == wxT(",") && !wrappedInQuotes && !tmp.IsEmpty())
|
||
|| (currentChar == wxT("\"") && wrappedInQuotes && !antislash && !tmp.IsEmpty()))
|
||
{
|
||
// put new provider in the array
|
||
providersArray.Add(tmp);
|
||
|
||
// reinit tmp
|
||
tmp = wxEmptyString;
|
||
wrappedInQuotes = false;
|
||
}
|
||
else
|
||
tmp += currentChar;
|
||
antislash = false;
|
||
}
|
||
}
|
||
|
||
// last provider
|
||
if (!tmp.IsEmpty())
|
||
{
|
||
// put last provider in the array
|
||
providersArray.Add(tmp);
|
||
}
|
||
|
||
// now, build one wxArrayString from these two
|
||
for (unsigned int index = 0 ; index < providersArray.GetCount() ; index++)
|
||
{
|
||
seclabelsArray.Add(providersArray.Item(index));
|
||
seclabelsArray.Add(labelsArray.Item(index));
|
||
}
|
||
|
||
// return the final one
|
||
return seclabelsArray;
|
||
}
|
||
|
||
|
||
wxString pgObject::GetSeqLabelsSql()
|
||
{
|
||
wxString sql = wxEmptyString;
|
||
wxArrayString seclabels = GetProviderLabelArray();
|
||
if (seclabels.GetCount() > 0)
|
||
{
|
||
for (unsigned int index = 0 ; index < seclabels.GetCount() - 1 ; index += 2)
|
||
{
|
||
sql += wxT("SECURITY LABEL FOR ") + seclabels.Item(index)
|
||
+ wxT("\n ON ") + GetTypeName().Upper() + wxT(" ") + GetQuotedFullIdentifier()
|
||
+ wxT("\n IS ") + qtDbString(seclabels.Item(index + 1)) + wxT(";\n");
|
||
}
|
||
}
|
||
|
||
return sql;
|
||
}
|
||
|
||
|
||
wxString pgObject::GetPrivileges(const wxString &allPattern, const wxString &str, const wxString &grantOnObject, const wxString &user, const wxString &column)
|
||
{
|
||
wxString aclWithGrant, aclWithoutGrant;
|
||
|
||
const wxChar *p = str.c_str();
|
||
while (*p)
|
||
{
|
||
if (allPattern.Find(*p) >= 0)
|
||
{
|
||
if (p[1] == (wxChar)'*')
|
||
aclWithGrant += *p;
|
||
else
|
||
aclWithoutGrant += *p;
|
||
}
|
||
p++;
|
||
if (*p == (wxChar)'*')
|
||
p++;
|
||
}
|
||
|
||
wxString grant;
|
||
if (!aclWithoutGrant.IsEmpty() || aclWithGrant.IsEmpty())
|
||
grant += GetPrivilegeGrant(allPattern, aclWithoutGrant, grantOnObject, user, column) + wxT(";\n");
|
||
if (!aclWithGrant.IsEmpty())
|
||
grant += GetPrivilegeGrant(allPattern, aclWithGrant, grantOnObject, user, column) + wxT(" WITH GRANT OPTION;\n");
|
||
|
||
return grant;
|
||
}
|
||
|
||
|
||
wxString pgObject::GetGrant(const wxString &allPattern, const wxString &_grantFor, const wxString &_column)
|
||
{
|
||
wxString grant, str, user, grantFor, tmpUser;
|
||
|
||
if (_grantFor.IsNull())
|
||
{
|
||
grantFor = GetTypeName();
|
||
grantFor.MakeUpper();
|
||
grantFor += wxT(" ") + GetQuotedFullIdentifier();
|
||
}
|
||
else
|
||
grantFor = _grantFor;
|
||
|
||
if (!acl.IsNull())
|
||
{
|
||
if (acl == wxT("{}"))
|
||
{
|
||
grant += GetPrivileges(allPattern, str, grantFor, wxT("public"), qtIdent(_column));
|
||
grant += GetPrivileges(allPattern, str, grantFor, qtIdent(owner), qtIdent(_column));
|
||
}
|
||
else
|
||
{
|
||
// checks if certain privilege is granted to public
|
||
bool grantedToPublic = false;
|
||
// checks if certain privilege is granted to owner
|
||
bool grantedToOwner = false;
|
||
|
||
//queryTokenizer acls(acl.Mid(1, acl.Length() - 2), ',');
|
||
wxSortedArrayString acls(wxSplit(acl.Mid(1, acl.Length() - 2),','));
|
||
|
||
//while (acls.HasMoreTokens())
|
||
for (int j=0;j<acls.Count();j++)
|
||
{
|
||
//str = acls.GetNextToken();
|
||
str = acls[j];
|
||
if (str.Left(1) == '"')
|
||
str = str.Mid(1, str.Length() - 2);
|
||
user = str.BeforeFirst('=');
|
||
str = str.AfterFirst('=').BeforeFirst('/');
|
||
if (user == wxT(""))
|
||
{
|
||
user = wxT("public");
|
||
grantedToPublic = true;
|
||
}
|
||
else
|
||
{
|
||
if (user.Left(6) == wxT("group "))
|
||
{
|
||
tmpUser = user.Mid(6);
|
||
if (user.Mid(6).StartsWith(wxT("\\\"")) && user.Mid(6).EndsWith(wxT("\\\"")))
|
||
user = wxT("GROUP ") + qtIdent(user.Mid(8, user.Length() - 10));
|
||
else
|
||
user = wxT("GROUP ") + qtIdent(user.Mid(6));
|
||
}
|
||
else
|
||
{
|
||
tmpUser = user;
|
||
if (user.StartsWith(wxT("\\\"")) && user.EndsWith(wxT("\\\"")))
|
||
user = qtIdent(user.Mid(2, user.Length() - 4));
|
||
else
|
||
user = qtIdent(user);
|
||
}
|
||
|
||
if (tmpUser.Contains(owner))
|
||
grantedToOwner = true;
|
||
}
|
||
|
||
grant += GetPrivileges(allPattern, str, grantFor, user, qtIdent(_column));
|
||
}
|
||
|
||
str = wxEmptyString;
|
||
int metaType = GetMetaType();
|
||
|
||
// We check here that whether the user has revoked prvileges granted to databases, functions
|
||
// and languages. If so then this must be part of reverse engineered sql statement
|
||
if (!grantedToPublic && (metaType == PGM_LANGUAGE || metaType == PGM_FUNCTION || metaType == PGM_DATABASE))
|
||
grant += GetPrivileges(allPattern, str, grantFor, wxT("public"), qtIdent(_column));
|
||
// We check here that whether the owner has revoked prvileges on himself to this postgres
|
||
// object. If so then this must be part of reverse engineered sql statement
|
||
if (!grantedToOwner)
|
||
grant += GetPrivileges(allPattern, str, grantFor, qtIdent(owner), qtIdent(_column));
|
||
}
|
||
}
|
||
return grant;
|
||
}
|
||
|
||
|
||
|
||
pgConn *pgObject::GetConnection() const
|
||
{
|
||
pgDatabase *db = GetDatabase();
|
||
if (db)
|
||
return db->connection();
|
||
|
||
pgServer *server;
|
||
|
||
if (IsCreatedBy(serverFactory))
|
||
server = (pgServer *)this;
|
||
else
|
||
server = GetServer();
|
||
|
||
if (server)
|
||
return server->connection();
|
||
return 0;
|
||
}
|
||
|
||
|
||
bool pgObject::CheckOpenDialogs(ctlTree *browser, wxTreeItemId node)
|
||
{
|
||
pgObject *obj = browser->GetObject(node);
|
||
if (obj && obj->GetWindowPtr())
|
||
return true;
|
||
|
||
wxTreeItemIdValue cookie;
|
||
wxTreeItemId child = browser->GetFirstChild(node, cookie);
|
||
|
||
while (child.IsOk())
|
||
{
|
||
obj = browser->GetObject(child);
|
||
if (obj && obj->GetWindowPtr())
|
||
return true;
|
||
|
||
wxTreeItemIdValue subCookie;
|
||
wxTreeItemId subChildItem = browser->GetFirstChild(child, subCookie);
|
||
if (browser->IsExpanded(child))
|
||
{
|
||
if (CheckOpenDialogs(browser, child))
|
||
return true;
|
||
}
|
||
// It may be the case the user might have expanded the node opened the
|
||
// dialog and then collapsed the node again. This case is handled in the
|
||
// check below
|
||
else if (subChildItem && browser->GetItemData(subChildItem))
|
||
{
|
||
if (CheckOpenDialogs(browser, child))
|
||
return true;
|
||
}
|
||
|
||
child = browser->GetNextChild(node, cookie);
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////
|
||
|
||
bool pgServerObject::CanDrop()
|
||
{
|
||
if (GetMetaType() == PGM_DATABASE)
|
||
return (server->GetCreatePrivilege() || server->GetSuperUser());
|
||
else
|
||
{
|
||
if (server->GetConnection()->BackendMinimumVersion(8, 1) && GetMetaType() == PGM_ROLE)
|
||
return (server->GetCreateRole() || server->GetSuperUser());
|
||
else
|
||
return server->GetSuperUser();
|
||
}
|
||
}
|
||
|
||
|
||
bool pgServerObject::CanCreate()
|
||
{
|
||
if (GetMetaType() == PGM_DATABASE)
|
||
return (server->GetCreatePrivilege() || server->GetSuperUser());
|
||
else
|
||
{
|
||
if (server->GetConnection()->BackendMinimumVersion(8, 1) && GetMetaType() == PGM_ROLE)
|
||
return (server->GetCreateRole() || server->GetSuperUser());
|
||
else
|
||
return server->GetSuperUser();
|
||
}
|
||
}
|
||
|
||
|
||
void pgServerObject::FillOwned(ctlTree *browser, ctlListView *referencedBy, const wxArrayString &dblist, const wxString &query)
|
||
{
|
||
pgCollection *databases = 0;
|
||
|
||
wxCookieType cookie;
|
||
wxTreeItemId item = browser->GetFirstChild(GetServer()->GetId(), cookie);
|
||
while (item)
|
||
{
|
||
databases = (pgCollection *)browser->GetObject(item);
|
||
if (databases && databases->GetMetaType() == PGM_DATABASE)
|
||
break;
|
||
else
|
||
databases = 0;
|
||
|
||
item = browser->GetNextChild(GetServer()->GetId(), cookie);
|
||
}
|
||
|
||
size_t i;
|
||
for (i = 0 ; i < dblist.GetCount() ; i++)
|
||
{
|
||
wxString dbname = dblist.Item(i);
|
||
pgConn *conn = 0;
|
||
pgConn *tmpConn = 0;
|
||
|
||
if (GetServer()->GetDatabaseName() == dbname)
|
||
conn = GetServer()->GetConnection();
|
||
else
|
||
{
|
||
item = browser->GetFirstChild(databases->GetId(), cookie);
|
||
while (item)
|
||
{
|
||
pgDatabase *db = (pgDatabase *)browser->GetObject(item);
|
||
if (db && db->GetMetaType() == PGM_DATABASE && db->GetName() == dbname)
|
||
{
|
||
if (db->GetConnected())
|
||
conn = db->GetConnection();
|
||
break;
|
||
}
|
||
item = browser->GetNextChild(databases->GetId(), cookie);
|
||
}
|
||
}
|
||
if (conn && conn->GetStatus() != PGCONN_OK)
|
||
conn = 0;
|
||
|
||
if (!conn)
|
||
{
|
||
tmpConn = GetServer()->CreateConn(dbname);
|
||
conn = tmpConn;
|
||
}
|
||
|
||
if (conn)
|
||
{
|
||
pgSet *set = conn->ExecuteSet(query);
|
||
|
||
if (set)
|
||
{
|
||
while (!set->Eof())
|
||
{
|
||
wxString relname = qtIdent(set->GetVal(wxT("nspname")));
|
||
if (!relname.IsEmpty())
|
||
relname += wxT(".");
|
||
relname += qtIdent(set->GetVal(wxT("relname")));
|
||
pgaFactory *ownerFactory = 0;
|
||
|
||
switch ( (wxChar)set->GetVal(wxT("relkind")).c_str()[0])
|
||
{
|
||
case 'r':
|
||
ownerFactory = &tableFactory;
|
||
break;
|
||
case 'i':
|
||
ownerFactory = &indexFactory;
|
||
relname = qtIdent(set->GetVal(wxT("indname"))) + wxT(" ON ") + relname;
|
||
break;
|
||
case 'S':
|
||
ownerFactory = &sequenceFactory;
|
||
break;
|
||
case 'v':
|
||
ownerFactory = &viewFactory;
|
||
break;
|
||
case 'x':
|
||
ownerFactory = &extTableFactory;
|
||
break;
|
||
case 'c': // composite type handled in PG_TYPE
|
||
case 's': // special
|
||
case 't': // toast
|
||
break;
|
||
case 'n':
|
||
ownerFactory = &schemaFactory;
|
||
break;
|
||
case 'y':
|
||
ownerFactory = &typeFactory;
|
||
break;
|
||
case 'd':
|
||
ownerFactory = &domainFactory;
|
||
break;
|
||
case 'C':
|
||
ownerFactory = &conversionFactory;
|
||
break;
|
||
case 'p':
|
||
ownerFactory = &functionFactory;
|
||
break;
|
||
case 'T':
|
||
ownerFactory = &triggerFunctionFactory;
|
||
break;
|
||
case 'o':
|
||
ownerFactory = &operatorFactory;
|
||
relname = set->GetVal(wxT("relname")); // unquoted
|
||
break;
|
||
}
|
||
|
||
if (ownerFactory)
|
||
{
|
||
wxString typname;
|
||
int icon;
|
||
typname = ownerFactory->GetTypeName();
|
||
icon = ownerFactory->GetIconId();
|
||
referencedBy->AppendItem(icon, typname, dbname, relname);
|
||
}
|
||
|
||
set->MoveNext();
|
||
}
|
||
delete set;
|
||
}
|
||
}
|
||
|
||
if (tmpConn)
|
||
delete tmpConn;
|
||
}
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////
|
||
|
||
pgServer *pgDatabaseObject::GetServer() const
|
||
{
|
||
return database->GetServer();
|
||
}
|
||
|
||
|
||
bool pgDatabaseObject::CanDrop()
|
||
{
|
||
return (database->GetCreatePrivilege() && (GetMetaType() != PGM_CATALOG));
|
||
}
|
||
|
||
|
||
bool pgDatabaseObject::CanCreate()
|
||
{
|
||
return database->GetCreatePrivilege();
|
||
}
|
||
|
||
|
||
wxString pgDatabaseObject::GetSchemaPrefix(const wxString &schemaname) const
|
||
{
|
||
return database->GetSchemaPrefix(schemaname);
|
||
}
|
||
|
||
|
||
wxString pgDatabaseObject::GetQuotedSchemaPrefix(const wxString &schemaname) const
|
||
{
|
||
return database->GetQuotedSchemaPrefix(schemaname);
|
||
}
|
||
|
||
|
||
void pgDatabaseObject::DisplayStatistics(ctlListView *statistics, const wxString &query)
|
||
{
|
||
if (statistics)
|
||
{
|
||
wxLogInfo(wxT("Displaying statistics for %s %s"), GetTypeName().c_str(), GetFullIdentifier().c_str());
|
||
|
||
// Add the statistics view columns
|
||
CreateListColumns(statistics, _("Statistic"), _("Value"));
|
||
|
||
pgSet *stats = database->ExecuteSet(query);
|
||
|
||
if (stats)
|
||
{
|
||
bool pretty = settings->GetNumberPretty();
|
||
int col;
|
||
wxArrayInt a;
|
||
int vmax = -1;
|
||
int lt = -1;
|
||
wxWindowDC dc(statistics);
|
||
int wspc, wR = 0, h, wtext, widhspace = 50;
|
||
wxString rowmark(L"\x21d2");
|
||
dc.GetTextExtent(' ', &wspc, &h);
|
||
dc.GetTextExtent(rowmark, &wR, &h);
|
||
if (wR == 0) {
|
||
rowmark = "R";
|
||
dc.GetTextExtent(rowmark, &wR, &h);
|
||
}
|
||
for (col = 0 ; col < stats->NumCols() ; col++)
|
||
{
|
||
if (!stats->ColName(col).IsEmpty()) {
|
||
wxString name = stats->ColName(col);
|
||
wxString vl = stats->GetVal(col);
|
||
if (vl.IsNumber() && vl.Length()>0) {
|
||
dc.GetTextExtent(vl, &wtext, &h);
|
||
if (wtext > vmax) vmax = wtext;
|
||
a.Add(wtext);
|
||
if (_("Live Tuples") == name) lt = a.GetCount() - 1;
|
||
} else
|
||
a.Add(-1);
|
||
statistics->AppendItem(name, vl);
|
||
}
|
||
}
|
||
if (vmax > 0 && pretty) {
|
||
for (int i = 0; i < a.Count(); i++) {
|
||
if (a[i] >= 0) {
|
||
wxString str = statistics->GetItemText(i, 1);
|
||
wxLongLong l = StrToLongLong(str);
|
||
wxString h = NumToStrHuman(l);
|
||
if (h.IsEmpty()) continue;
|
||
int nspc = (vmax - a[i] + widhspace) / wspc;
|
||
if (lt == i) {
|
||
nspc = (vmax - a[i]) / wspc;
|
||
wxString s = generate_spaces((widhspace - wR) / wspc / 2);
|
||
str += generate_spaces(nspc) + s + rowmark + s + h;
|
||
}
|
||
else str += generate_spaces(nspc) + h;
|
||
statistics->SetItem(i, 1, str);
|
||
}
|
||
|
||
}
|
||
}
|
||
delete stats;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
///////////////////////////////////////////////////////////////
|
||
|
||
void pgSchemaObject::SetSchema(pgSchema *newSchema)
|
||
{
|
||
schema = newSchema;
|
||
database = schema->GetDatabase();
|
||
}
|
||
|
||
|
||
void pgSchemaObject::UpdateSchema(ctlTree *browser, OID schemaOid)
|
||
{
|
||
// used e.g. for triggers that use trigger functions from other namespaces
|
||
if (!browser)
|
||
return;
|
||
|
||
if (schema->GetOid() != schemaOid)
|
||
{
|
||
pgObject *schemas = browser->GetObject(browser->GetItemParent(schema->GetId()));
|
||
|
||
wxASSERT(schemas);
|
||
treeObjectIterator schIt(browser, schemas);
|
||
pgSchema *sch;
|
||
|
||
while ((sch = (pgSchema *)schIt.GetNextObject()) != 0)
|
||
{
|
||
if (sch->GetOid() == schemaOid)
|
||
{
|
||
SetSchema(sch);
|
||
return;
|
||
}
|
||
}
|
||
|
||
// If we get this far, it's possible the schema is actually a catalog
|
||
// in this case. We need to find the catalogs node and then search that.
|
||
pgObject *db = browser->GetObject(browser->GetItemParent(schemas->GetId()));
|
||
|
||
wxASSERT(db);
|
||
treeObjectIterator catsIt(browser, db);
|
||
pgObject *catalogs;
|
||
|
||
while ((catalogs = (pgObject *)catsIt.GetNextObject()) != 0)
|
||
{
|
||
if (catalogs->GetMetaType() == PGM_CATALOG)
|
||
break;
|
||
}
|
||
|
||
// Assuming we got the catalogs node, now get the catalog
|
||
if (catalogs)
|
||
{
|
||
treeObjectIterator catIt(browser, catalogs);
|
||
pgCatalog *cat;
|
||
|
||
while ((cat = (pgCatalog *)catIt.GetNextObject()) != 0)
|
||
{
|
||
if (cat->GetOid() == schemaOid)
|
||
{
|
||
SetSchema((pgSchema *)cat);
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
wxMessageBox(_("The schema oid can't be located, please refresh all schemas!"),
|
||
_("Missing information"), wxICON_EXCLAMATION | wxOK, browser);
|
||
}
|
||
}
|
||
|
||
|
||
bool pgSchemaObject::GetSystemObject() const
|
||
{
|
||
if (!schema)
|
||
return false;
|
||
return GetOid() < GetConnection()->GetLastSystemOID();
|
||
}
|
||
|
||
|
||
bool pgSchemaObject::CanDrop()
|
||
{
|
||
return schema->GetCreatePrivilege() && schema->GetMetaType() != PGM_CATALOG;
|
||
}
|
||
|
||
|
||
bool pgSchemaObject::CanCreate()
|
||
{
|
||
return schema->GetCreatePrivilege() && schema->GetMetaType() != PGM_CATALOG;
|
||
}
|
||
|
||
|
||
void pgSchemaObject::SetContextInfo(frmMain *form)
|
||
{
|
||
// form->SetDatabase(schema->GetDatabase());
|
||
}
|
||
|
||
pgSet *pgSchemaObject::ExecuteSet(const wxString &sql)
|
||
{
|
||
return schema->GetDatabase()->ExecuteSet(sql);
|
||
}
|
||
|
||
wxString pgSchemaObject::ExecuteScalar(const wxString &sql)
|
||
{
|
||
return schema->GetDatabase()->ExecuteScalar(sql);
|
||
}
|
||
|
||
bool pgSchemaObject::ExecuteVoid(const wxString &sql)
|
||
{
|
||
return schema->GetDatabase()->ExecuteVoid(sql);
|
||
}
|
||
|
||
|
||
wxString pgSchemaObject::GetFullIdentifier() const
|
||
{
|
||
return schema->GetPrefix() + GetName();
|
||
}
|
||
|
||
|
||
wxString pgSchemaObject::GetQuotedFullIdentifier() const
|
||
{
|
||
if (schema->GetTypeName() == wxT("Table"))
|
||
return schema->GetSchema()->GetQuotedPrefix() + GetQuotedIdentifier();
|
||
else
|
||
return schema->GetQuotedPrefix() + GetQuotedIdentifier();
|
||
}
|
||
|
||
|
||
|
||
|
||
enum tokentype
|
||
{
|
||
SQLTK_NORMAL = 0,
|
||
SQLTK_JOINMOD,
|
||
SQLTK_JOIN,
|
||
SQLTK_ON,
|
||
SQLTK_UNION
|
||
|
||
};
|
||
|
||
typedef struct __tokenaction
|
||
{
|
||
const wxChar *keyword, *replaceKeyword;
|
||
int actionBefore, actionAfter;
|
||
tokentype special;
|
||
bool doBreak;
|
||
} tokenAction;
|
||
|
||
|
||
tokenAction sqlTokens[] =
|
||
{
|
||
{ wxT("WHERE")}, // initializing fails, so we're doing it in the code
|
||
{ wxT("SELECT"), wxT(" SELECT"), 0, 8, SQLTK_NORMAL, true},
|
||
{ wxT("FROM"), wxT(" FROM"), -8, 8, SQLTK_NORMAL, true},
|
||
{ wxT("LEFT"), wxT(" LEFT"), -8, 13, SQLTK_JOINMOD, true},
|
||
{ wxT("RIGHT"), wxT(" RIGHT"), -8, 13, SQLTK_JOINMOD, true},
|
||
{ wxT("NATURAL"), wxT(" NATURAL"), -8, 13, SQLTK_JOINMOD, true},
|
||
{ wxT("FULL"), wxT(" FULL"), -8, 13, SQLTK_JOINMOD, true},
|
||
{ wxT("CROSS"), wxT(" CROSS"), -8, 13, SQLTK_JOINMOD, true},
|
||
{ wxT("UNION"), wxT(" UNION"), -8, 13, SQLTK_UNION, true},
|
||
{ wxT("JOIN"), wxT(" JOIN"), -8, 13, SQLTK_JOIN, true},
|
||
{ wxT("ON"), wxT("ON"), 0, -5, SQLTK_ON, false},
|
||
{ wxT("ORDER"), wxT(" ORDER"), -8, 8, SQLTK_NORMAL, true},
|
||
{ wxT("GROUP"), wxT(" GROUP"), -8, 8, SQLTK_NORMAL, true},
|
||
{ wxT("HAVING"), wxT(" HAVING"), -8, 8, SQLTK_NORMAL, true},
|
||
{ wxT("LIMIT"), wxT(" LIMIT"), -8, 8, SQLTK_NORMAL, true},
|
||
{ wxT("CASE"), wxT("CASE"), 0, 4, SQLTK_NORMAL, true},
|
||
{ wxT("WHEN"), wxT("WHEN"), 0, 0, SQLTK_NORMAL, true},
|
||
{ wxT("ELSE"), wxT("ELSE"), 0, 0, SQLTK_NORMAL, true},
|
||
{ wxT("END"), wxT("END "), -4, 0, SQLTK_NORMAL, true},
|
||
{0, 0, 0, 0, SQLTK_NORMAL, false}
|
||
};
|
||
|
||
tokenAction secondOnToken =
|
||
{ wxT("ON"), wxT("ON"), -5, 0, SQLTK_ON, true};
|
||
|
||
|
||
|
||
wxString pgRuleObject::GetFormattedDefinition()
|
||
{
|
||
// pgsql 7.4 does formatting itself
|
||
if (!GetDatabase()->GetPrettyOption().IsEmpty())
|
||
return GetDefinition();
|
||
|
||
////////////////////////////////
|
||
// ok, this code looks weird. It's necessary, because somebody (NOT the running code)
|
||
// will screw up that entry. It's broken in pgAdmin3::OnInit() already.
|
||
// maybe your compiler does better (VC6SP5, but an older c2xx to avoid other bugs)
|
||
|
||
sqlTokens[0].replaceKeyword = wxT(" WHERE");
|
||
sqlTokens[0].actionBefore = -8;
|
||
sqlTokens[0].actionAfter = 8;
|
||
sqlTokens[0].special = SQLTK_NORMAL;
|
||
sqlTokens[0].doBreak = true;
|
||
|
||
wxString fc, token;
|
||
queryTokenizer tokenizer(GetDefinition());
|
||
int indent = 0;
|
||
int position = 0; // col position. updated, but not used at the moment.
|
||
bool wasOn = false;
|
||
|
||
while (tokenizer.HasMoreTokens())
|
||
{
|
||
token = tokenizer.GetNextToken();
|
||
|
||
gotToken:
|
||
wxString trailingChars;
|
||
|
||
// token may contain brackets
|
||
int bracketPos;
|
||
bracketPos = token.Find('(', true);
|
||
while (bracketPos >= 0)
|
||
{
|
||
fc += token.Left(bracketPos + 1);
|
||
token = token.Mid(bracketPos + 1);
|
||
bracketPos = token.Find('(', true);
|
||
}
|
||
|
||
bracketPos = token.Find(')', true);
|
||
while (bracketPos >= 0)
|
||
{
|
||
trailingChars = token.Mid(bracketPos) + trailingChars;
|
||
token = token.Left(bracketPos);
|
||
bracketPos = token.Find(')', true);
|
||
}
|
||
// identify token
|
||
tokenAction *tp = sqlTokens;
|
||
while (tp->keyword)
|
||
{
|
||
if (!token.CmpNoCase(tp->keyword))
|
||
{
|
||
if (tp->special == SQLTK_ON && wasOn)
|
||
tp = &secondOnToken;
|
||
else
|
||
wasOn = (tp->special == SQLTK_ON);
|
||
break;
|
||
}
|
||
tp++;
|
||
}
|
||
|
||
if (tp && tp->keyword)
|
||
{
|
||
// we found a keyword.
|
||
if (tp->special == SQLTK_UNION || tp->special == SQLTK_JOINMOD)
|
||
{
|
||
token = tokenizer.GetNextToken();
|
||
if (tp->special == SQLTK_UNION && token.CmpNoCase(wxT("JOIN")))
|
||
{
|
||
fc += wxT("\nUNION\n");
|
||
indent = 0;
|
||
goto gotToken;
|
||
}
|
||
else
|
||
{
|
||
trailingChars = token + wxT(" ") + trailingChars;
|
||
indent += tp->actionBefore;
|
||
if (indent < 0) indent = 0;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
indent += tp->actionBefore;
|
||
if (indent < 0) indent = 0;
|
||
}
|
||
if (tp->doBreak)
|
||
{
|
||
fc += wxT("\n") + wxString(' ', (size_t)indent);
|
||
position = indent;
|
||
}
|
||
else
|
||
{
|
||
fc += wxT(" ");
|
||
position += 1;
|
||
}
|
||
fc += tp->replaceKeyword;
|
||
position += wxString(tp->replaceKeyword).Length();
|
||
|
||
indent += tp->actionAfter;
|
||
if (indent < 0) indent = 0;
|
||
}
|
||
else
|
||
{
|
||
fc += token;
|
||
position += token.Length();
|
||
}
|
||
fc += wxT(" ");
|
||
position++;
|
||
if (!trailingChars.IsNull())
|
||
{
|
||
fc += trailingChars + wxT(" ");;
|
||
position += trailingChars.Length() + 1;
|
||
}
|
||
}
|
||
return fc;
|
||
}
|
||
|
||
wxString pgObject::qtDbString(const wxString &str)
|
||
{
|
||
// Use the server aware version if possible
|
||
if (GetDatabase() && GetDatabase()->GetConnection())
|
||
return GetDatabase()->GetConnection()->qtDbString(str);
|
||
else
|
||
{
|
||
wxString ret = str;
|
||
ret.Replace(wxT("\\"), wxT("\\\\"));
|
||
ret.Replace(wxT("'"), wxT("''"));
|
||
ret.Append(wxT("'"));
|
||
ret.Prepend(wxT("'"));
|
||
return ret;
|
||
}
|
||
}
|
||
|
||
wxString pgObject::GetDefaultPrivileges(const wxString &strType, const wxString &strSupportedPrivs,
|
||
const wxString &strSchema, const wxString &strOrigDefPrivs,
|
||
const wxString &strNewDefPrivs, const wxString &strRole)
|
||
{
|
||
wxString strDefPrivs, strGrant, strRevoke, strGrantOption, strRevokeGrantOption;
|
||
int privilegeCount = strSupportedPrivs.Length();
|
||
|
||
for (int index = 0; index < privilegeCount; index++)
|
||
{
|
||
bool inOrigPriv = false, inNewPriv = false, grantOptInOrigPriv = false, grantOptInNewPriv = false;
|
||
wxChar privChar = strSupportedPrivs.GetChar(index);
|
||
int privAt = strOrigDefPrivs.Find(privChar);
|
||
if (privAt != wxNOT_FOUND)
|
||
{
|
||
inOrigPriv = true;
|
||
if ((unsigned int)privAt < strOrigDefPrivs.Length() - 1 &&
|
||
strOrigDefPrivs.GetChar(privAt + 1) == wxT('*'))
|
||
grantOptInOrigPriv = true;
|
||
}
|
||
|
||
privAt = strNewDefPrivs.Find(privChar);
|
||
if (privAt != wxNOT_FOUND)
|
||
{
|
||
inNewPriv = true;
|
||
if ((unsigned int)privAt < strNewDefPrivs.Length() - 1 &&
|
||
strNewDefPrivs.GetChar(privAt + 1) == wxT('*'))
|
||
grantOptInNewPriv = true;
|
||
}
|
||
if (inOrigPriv || inNewPriv || grantOptInOrigPriv || grantOptInNewPriv)
|
||
{
|
||
wxString strPrivilege = GetPrivilegeName(privChar);
|
||
if (!inOrigPriv && inNewPriv)
|
||
{
|
||
// GRANT PRIVILEGES
|
||
if (!grantOptInNewPriv)
|
||
strGrant += strPrivilege + wxT(", ");
|
||
// GRANT PRVILEGES WITH GRANT OPTION
|
||
else
|
||
strGrantOption += strPrivilege + wxT(", ");
|
||
}
|
||
// REVOKE PRIVILEGES
|
||
else if (inOrigPriv && !inNewPriv)
|
||
strRevoke += strPrivilege + wxT(", ");
|
||
else if (inOrigPriv && inNewPriv)
|
||
{
|
||
// REVOKE ONLY 'WITH GRANT OPTION'
|
||
if(grantOptInOrigPriv && !grantOptInNewPriv)
|
||
strRevokeGrantOption += strPrivilege + wxT(", ");
|
||
// GRANT PRVILEGES WITH GRANT OPTION
|
||
else if (!grantOptInOrigPriv && grantOptInNewPriv)
|
||
strGrantOption += strPrivilege + wxT(", ");
|
||
}
|
||
}
|
||
}
|
||
|
||
bool isModified = false;
|
||
wxString strAltDefPriv;
|
||
|
||
if (!strSchema.IsEmpty())
|
||
strAltDefPriv = wxT("ALTER DEFAULT PRIVILEGES IN SCHEMA ") + strSchema;
|
||
else
|
||
strAltDefPriv = wxT("ALTER DEFAULT PRIVILEGES ");
|
||
|
||
if (!strRevoke.IsEmpty())
|
||
{
|
||
isModified = true;
|
||
strRevoke = strRevoke.SubString(0, strRevoke.Length() - 3);
|
||
strDefPrivs += strAltDefPriv +
|
||
wxT("\n REVOKE ") + strRevoke + wxT(" ON ") + strType +
|
||
wxT("\n FROM ") + strRole + wxT(";\n");
|
||
}
|
||
if (!strRevokeGrantOption.IsEmpty())
|
||
{
|
||
isModified = true;
|
||
strRevokeGrantOption = strRevokeGrantOption.SubString(0, strRevokeGrantOption.Length() - 3);
|
||
strDefPrivs += strAltDefPriv +
|
||
wxT("\n REVOKE GRANT OPTION FOR ") + strRevokeGrantOption + wxT(" ON ") + strType +
|
||
wxT("\n FROM ") + strRole + wxT(";\n");
|
||
}
|
||
if (!strGrant.IsEmpty())
|
||
{
|
||
isModified = true;
|
||
|
||
strGrant = strGrant.SubString(0, strGrant.Length() - 3);
|
||
strDefPrivs += strAltDefPriv +
|
||
wxT("\n GRANT ") + strGrant + wxT(" ON ") + strType +
|
||
wxT("\n TO ") + strRole + wxT(";\n");
|
||
}
|
||
if (!strGrantOption.IsEmpty())
|
||
{
|
||
isModified = true;
|
||
strGrantOption = strGrantOption.SubString(0, strGrantOption.Length() - 3);
|
||
strDefPrivs += strAltDefPriv +
|
||
wxT("\n GRANT ") + strGrantOption + wxT(" ON ") + strType +
|
||
wxT("\n TO ") + strRole + wxT(" WITH GRANT OPTION;\n");
|
||
}
|
||
if (isModified)
|
||
return strDefPrivs + wxT("\n");
|
||
|
||
return wxT("");
|
||
}
|
||
|
||
// Find the user-privileges pair from ACLs
|
||
// i.e. {=wDx/user1,postgres=adDxt/user1}
|
||
// Remove starting and ending curly braces, before supplying as the input
|
||
bool pgObject::findUserPrivs(wxString &strDefPrivs, wxString &strUser, wxString &strPriv)
|
||
{
|
||
strUser = strPriv = wxT("");
|
||
if (strDefPrivs.IsEmpty()) return false;
|
||
|
||
bool startsWithQuote = false;
|
||
if (strDefPrivs.StartsWith(wxT("\""))) startsWithQuote = true;
|
||
|
||
if (strDefPrivs.StartsWith(wxT("\"\"")))
|
||
{
|
||
wxChar currChar;
|
||
int quoteCount = 0;
|
||
int index = 0;
|
||
|
||
currChar = strDefPrivs.GetChar(index);
|
||
|
||
while (true)
|
||
{
|
||
if (currChar == wxT('=') && quoteCount % 2 == 0)
|
||
break;
|
||
strUser += currChar;
|
||
currChar = strDefPrivs.GetChar(++index);
|
||
if (currChar == wxT('"')) quoteCount++;
|
||
}
|
||
strUser = strUser.SubString(2, strUser.Length() - 2);
|
||
strUser.Replace(wxT("\"\""), wxT("\""), true);
|
||
strDefPrivs = strDefPrivs.SubString(index + 1, strDefPrivs.Length());
|
||
}
|
||
else
|
||
{
|
||
/* Remove first quote */
|
||
if (startsWithQuote) strDefPrivs = strDefPrivs.SubString(1, strDefPrivs.Length());
|
||
|
||
int equalCharAt = strDefPrivs.Find(wxT('='));
|
||
|
||
if (equalCharAt != 0)
|
||
strUser = strDefPrivs.SubString(0, equalCharAt - 1);
|
||
else
|
||
strUser = wxT("public");
|
||
strDefPrivs = strDefPrivs.SubString(equalCharAt + 1, strDefPrivs.Length());
|
||
}
|
||
|
||
int slashCharAt = strDefPrivs.Find(wxT('/'));
|
||
strPriv = strDefPrivs.SubString(0, slashCharAt - 1);
|
||
|
||
strDefPrivs = strDefPrivs.SubString(strPriv.Length() + 2, strDefPrivs.Length());
|
||
|
||
if (!strDefPrivs.StartsWith(wxT("\"")))
|
||
{
|
||
int commaCharAt = strDefPrivs.Find(wxT(','));
|
||
if (commaCharAt == wxNOT_FOUND) strDefPrivs = wxT("");
|
||
else strDefPrivs = strDefPrivs.SubString(commaCharAt + 1, strDefPrivs.Length());
|
||
}
|
||
else
|
||
{
|
||
wxChar currChar;
|
||
int quoteCount = 0;
|
||
int index = 0;
|
||
|
||
currChar = strDefPrivs.GetChar(index);
|
||
while (true)
|
||
{
|
||
if (currChar == wxT(',') && quoteCount % 2 == 0)
|
||
break;
|
||
currChar = strDefPrivs.GetChar(++index);
|
||
if (currChar == wxT('"')) quoteCount++;
|
||
}
|
||
strDefPrivs = strDefPrivs.SubString(index, strDefPrivs.Length());
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
wxString pgObject::GetPrivilegeName(wxChar privilege)
|
||
{
|
||
switch(privilege)
|
||
{
|
||
case 'a':
|
||
return wxT("INSERT");
|
||
case 'r':
|
||
return wxT("SELECT");
|
||
case 'w':
|
||
return wxT("UPDATE");
|
||
case 'd':
|
||
return wxT("DELETE");
|
||
case 'D':
|
||
return wxT("TRUNCATE");
|
||
case 'x':
|
||
return wxT("REFERENCES");
|
||
case 't':
|
||
return wxT("TRIGGER");
|
||
case 'm':
|
||
return wxT("MAINTAIN");
|
||
case 'U':
|
||
return wxT("USAGE");
|
||
case 'X':
|
||
return wxT("EXECUTE");
|
||
default:
|
||
return wxT("UNKNOWN");
|
||
}
|
||
}
|
||
|
||
void pgObject::SetWindowPtr(dlgProperty *dlgprop)
|
||
{
|
||
dlg = dlgprop;
|
||
}
|
||
wxString pgObject::GetSqlReCreate(frmMain *form, pgObject *obj)
|
||
{
|
||
pgConn *conn = GetConnection();
|
||
if (conn)
|
||
{
|
||
int status = conn->GetStatus();
|
||
if (status == PGCONN_BROKEN || status == PGCONN_BAD)
|
||
{
|
||
form->SetStatusText(_(" Connection broken."));
|
||
return wxT("");;
|
||
}
|
||
}
|
||
wxLogInfo(wxT("Recreate cascade object for %s %s"), GetTypeName().c_str(), GetIdentifier().c_str());
|
||
|
||
wxString sql;
|
||
wxString line;
|
||
ctlTree *browser=form->GetBrowser();
|
||
wxString databasePath = form->GetNodePath(obj->GetDatabase()->GetId());
|
||
// получение правила и от него уже зависимости будем раскручивать
|
||
int colcount = 0;
|
||
pgSetIterator set(GetConnection(),
|
||
wxT("WITH RECURSIVE t(lvl,classid,objid,type_child) AS (\n")
|
||
wxT(" select 1,c_classid::regclass,c_objid,type_child from (\n")
|
||
wxT(" select dd.*\n")
|
||
wxT(" ,(pg_identify_object_as_address(dd.classid, dd.objid, dd.objsubid)).type type_child\n")
|
||
wxT(" ,rule_to_view.classid c_classid\n")
|
||
wxT(" ,rule_to_view.objid c_objid\n")
|
||
wxT(" from pg_depend dd, lateral \n")
|
||
wxT(" pg_get_object_address(case when (pg_identify_object_as_address(classid, objid, objsubid)).type='rule' then 'view' \n")
|
||
wxT(" else (pg_identify_object_as_address(classid, objid, objsubid)).type end , \n")
|
||
wxT(" case when (pg_identify_object_as_address(classid, objid, objsubid)).type='rule' then \n")
|
||
wxT(" (pg_identify_object_as_address(classid, objid, objsubid)).object_names[1:2]\n")
|
||
wxT(" else (pg_identify_object_as_address(classid, objid, objsubid)).object_names\n")
|
||
wxT(" end\n")
|
||
wxT(" ,(pg_identify_object_as_address(classid, objid, objsubid)).object_args)\n")
|
||
wxT(" rule_to_view\n")
|
||
wxT(" where dd.deptype='n' and dd.refobjid=") + GetOidStr() + wxT("\n")
|
||
wxT(" ) d\n")
|
||
wxT(" where d.refobjid<>d.c_objid\n")
|
||
wxT(" group by c_classid,c_objid,type_child\n")
|
||
wxT("UNION ALL\n")
|
||
wxT("select lvl+1,c_classid::regclass classid,c_objid,type_child from (\n")
|
||
wxT(" select dd.*\n")
|
||
wxT(" ,(pg_identify_object_as_address(dd.classid, dd.objid, dd.objsubid)).type type_child\n")
|
||
wxT(" ,rule_to_view.classid c_classid\n")
|
||
wxT(" ,rule_to_view.objid c_objid\n")
|
||
wxT(" ,t.lvl\n")
|
||
wxT(" from pg_depend dd,t, lateral \n")
|
||
wxT(" pg_get_object_address(case when (pg_identify_object_as_address(dd.classid, dd.objid, dd.objsubid)).type='rule' then 'view' \n")
|
||
wxT(" else (pg_identify_object_as_address(dd.classid, dd.objid, dd.objsubid)).type end , \n")
|
||
wxT(" case when (pg_identify_object_as_address(dd.classid, dd.objid, dd.objsubid)).type='rule' then \n")
|
||
wxT(" (pg_identify_object_as_address(dd.classid, dd.objid, dd.objsubid)).object_names[1:2]\n")
|
||
wxT(" else (pg_identify_object_as_address(dd.classid, dd.objid, dd.objsubid)).object_names\n")
|
||
wxT(" end\n")
|
||
wxT(" ,(pg_identify_object_as_address(dd.classid, dd.objid, dd.objsubid)).object_args)\n")
|
||
wxT(" rule_to_view\n")
|
||
wxT(" where dd.deptype='n' and dd.refobjid=t.objid\n")
|
||
wxT(" ) d\n")
|
||
wxT(" where d.refobjid<>d.c_objid\n")
|
||
wxT(" group by c_classid,c_objid,type_child,lvl\n")
|
||
wxT(") select t.*,coalesce(cc.conname,vv.relname,tt.tgname) objname,coalesce(cc.connamespace::regnamespace,vv.relnamespace::regnamespace)::text sch\n")
|
||
wxT(",coalesce(cc.conrelid::regclass) parent,coalesce(vv.relkind) kind from t\n")
|
||
wxT("left join pg_constraint cc on t.classid='pg_constraint'::regclass and t.objid=cc.oid\n")
|
||
wxT("left join pg_class vv on (t.classid='pg_class'::regclass and t.objid=vv.oid) or (cc.conrelid=vv.oid)\n")
|
||
wxT("left join pg_trigger tt on t.classid='pg_trigger'::regclass and t.objid=tt.oid\n")
|
||
wxT("")
|
||
wxT("")
|
||
wxT("order by 1 desc,2 asc;")
|
||
);
|
||
|
||
// lvl;classid;objid;child_type;objname;sch;parent
|
||
sql=wxT("-- ");
|
||
pgCollection *database = 0;
|
||
wxTreeItemId dbitem = obj->GetDatabase()->GetId();
|
||
//pgCollection *collect = 0;
|
||
pgCollection *collectSchEl = 0;
|
||
wxCookieType cookie;
|
||
pgCollection *collect= browser->FindCollection(schemaFactory,dbitem);
|
||
|
||
wxTreeItemId item = browser->GetFirstChild(dbitem, cookie);
|
||
if (1==0) {
|
||
while (item)
|
||
{
|
||
collect = (pgCollection *)browser->GetObject(item);
|
||
if (collect && collect->GetMetaType() == PGM_SCHEMA)
|
||
break;
|
||
else
|
||
collect = 0;
|
||
item = browser->GetNextChild(dbitem, cookie);
|
||
}
|
||
}
|
||
wxString findobj=wxT(",");
|
||
wxString dropblock=wxT("-- \n");
|
||
wxString createblock=wxEmptyString;
|
||
while (set.RowsLeft())
|
||
{
|
||
wxString refname = set.GetVal(wxT("objname"));
|
||
wxString parent = set.GetVal(wxT("parent"));
|
||
wxString type = set.GetVal(wxT("type_child"));
|
||
wxString kind = set.GetVal(wxT("kind"));
|
||
wxString sxema = parent.BeforeFirst('.');
|
||
wxString table = parent.AfterFirst('.');
|
||
if (sxema.IsEmpty()) {
|
||
sxema=set.GetVal(wxT("sch"));
|
||
table=refname;
|
||
}
|
||
pgaFactory *depFactory = 0;
|
||
if (refname.IsEmpty())
|
||
continue;
|
||
if (findobj.Contains(wxT(",")+table+wxT(".")+refname+wxT(","))) continue;
|
||
//pgaCollectionFactory
|
||
|
||
item = browser->GetFirstChild(collect->GetId(), cookie);
|
||
while (item)
|
||
{
|
||
collectSchEl = (pgCollection *)browser->GetObject(item);
|
||
if (collectSchEl && collectSchEl->GetMetaType() == PGM_SCHEMA && collectSchEl->GetName()==sxema)
|
||
{
|
||
pgCollection *coll= 0;
|
||
wxString whatfindt;
|
||
wxString tmpsql;
|
||
wxString fn=collectSchEl->GetFullName();
|
||
if (type.Contains(wxT("rule"))||(type.Contains(wxT("view")))) whatfindt=wxT("...bb");
|
||
if (type.Contains(wxT("table constraint"))) {
|
||
//coll=browser->FindCollection(tableFactory,item);
|
||
whatfindt=wxT("Tables");
|
||
}
|
||
// look for the item starting with the given prefix after it
|
||
|
||
|
||
wxTreeItemId id=item;
|
||
wxString collTypeName;
|
||
while ( id.IsOk() &&
|
||
( browser->GetItemText(id) == wxT("Dummy") && !browser->GetItemData(id)
|
||
|| true ))
|
||
{
|
||
pgObject *oo= ((pgObject *) browser->GetItemData(id));
|
||
if (oo) {
|
||
pgaFactory *ff=oo->GetFactory();
|
||
collTypeName =ff->GetTypeName();
|
||
fn=oo->GetName();
|
||
if ((oo->GetFactory()==&viewFactory || oo->GetFactory()==&tableFactory
|
||
|| oo->GetFactory()==&pg_partitionFactory )
|
||
&& fn==table) {
|
||
break;
|
||
}
|
||
}
|
||
wxCookieType cookie;
|
||
if ( browser->HasChildren(id) &&((oo->GetFactory()==&viewFactory || oo->GetFactory()==&tableFactory
|
||
|| oo->GetFactory()==&pg_partitionFactory
|
||
|| oo->GetFactory()==&schemaFactory
|
||
|| collTypeName ==whatfindt
|
||
|| collTypeName ==wxT("Constraints")
|
||
|| collTypeName ==wxT("Partitions")
|
||
|| collTypeName ==wxT("Views")
|
||
)))
|
||
{
|
||
if (oo) oo->ShowTreeDetail(browser);
|
||
id = browser->GetFirstChild(id, cookie);
|
||
}
|
||
else
|
||
{
|
||
// Try a sibling of this or ancestor instead
|
||
wxTreeItemId p = id;
|
||
wxTreeItemId toFind;
|
||
do
|
||
{
|
||
toFind = browser->GetNextSibling(p);
|
||
p = browser->GetItemParent(p);
|
||
}
|
||
while (p.IsOk() && !toFind.IsOk());
|
||
id = toFind;
|
||
}
|
||
}
|
||
if ( id.IsOk() ) {
|
||
// найден нужный элемнт
|
||
pgObject *db=(pgTable *) browser->GetItemData(id);
|
||
if (kind=='r') {
|
||
// "table constraint"
|
||
pgTable *tbl=(pgTable *) db;
|
||
tbl->ShowTreeDetail(browser);
|
||
pgCollection *cll=tbl->GetConstraintCollection(browser);
|
||
if (cll) {
|
||
fn=cll->GetFullName();
|
||
}
|
||
wxTreeItemId matchItem = browser->FindItem(cll->GetId(),refname);
|
||
if ( matchItem.IsOk() ) {
|
||
pgObject *o=browser->GetObject(matchItem);
|
||
//fn=o->GetName()+wxT(" - type : ")+o->GetTypeName();
|
||
//sql+=fn+wxT("\n");
|
||
tmpsql=browser->GetObject(matchItem)->GetSql(browser);
|
||
}
|
||
|
||
}
|
||
else
|
||
{
|
||
tmpsql=db->GetSql(browser);
|
||
|
||
}
|
||
} else {
|
||
// не найдет в дереве элемент с указанным именем
|
||
|
||
}
|
||
findobj=findobj+table+wxT(".")+refname+wxT(",");
|
||
wxStringTokenizer rowslist(tmpsql, wxT("\n"),wxTOKEN_RET_EMPTY );
|
||
wxString cn;
|
||
wxString distributionColumns;
|
||
wxString createblocktmp=wxEmptyString;
|
||
int i=1;
|
||
while (rowslist.HasMoreTokens())
|
||
{
|
||
cn = rowslist.GetNextToken();
|
||
if (i==1) {
|
||
createblocktmp+=cn+wxT("\n");
|
||
dropblock+=cn+wxT("\n");
|
||
i++;
|
||
continue;
|
||
}
|
||
if (i==3) {
|
||
dropblock+=cn.Mid(3)+wxT("\n");
|
||
i++;
|
||
continue;
|
||
}
|
||
createblocktmp+=cn+wxT("\n");
|
||
i++;
|
||
}
|
||
createblock=createblocktmp+createblock;
|
||
// if (node->IsCollection())
|
||
// owneritem = browser->GetParentObject(node->GetId())->GetId();
|
||
// else
|
||
// owneritem = browser->GetParentObject(browser->GetParentObject(node->GetId())->GetId())->GetId();
|
||
}
|
||
else
|
||
collectSchEl = 0;
|
||
item = browser->GetNextChild(collect->GetId(), cookie);
|
||
}
|
||
|
||
}
|
||
|
||
sql+=dropblock+wxT("\n\n\n")+wxT("-- Create depends objects (reverse order))\n")+createblock;
|
||
return sql;
|
||
}
|