////////////////////////////////////////////////////////////////////////// // // 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 // 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;jconnection(); 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; }