mirror of
https://github.com/levinsv/pgadmin3.git
synced 2026-05-15 14:15:49 -06:00
575 lines
15 KiB
C++
575 lines
15 KiB
C++
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// pgAdmin III - PostgreSQL Tools
|
|
//
|
|
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
|
// This software is released under the PostgreSQL Licence
|
|
//
|
|
// gpExtTable.cpp - Greenplum External Table
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
// wxWindows headers
|
|
#include <wx/wx.h>
|
|
|
|
// App headers
|
|
#include "pgAdmin3.h"
|
|
#include "utils/misc.h"
|
|
#include "schema/pgColumn.h"
|
|
#include "schema/gpExtTable.h"
|
|
#include "frm/frmHint.h"
|
|
|
|
|
|
gpExtTable::gpExtTable(pgSchema *newSchema, const wxString &newName)
|
|
: pgSchemaObject(newSchema, extTableFactory, newName)
|
|
{
|
|
}
|
|
|
|
gpExtTable::~gpExtTable()
|
|
{
|
|
}
|
|
|
|
wxString gpExtTable::GetTranslatedMessage(int kindOfMessage) const
|
|
{
|
|
wxString message = wxEmptyString;
|
|
|
|
switch (kindOfMessage)
|
|
{
|
|
case RETRIEVINGDETAILS:
|
|
message = _("Retrieving details on external table");
|
|
message += wxT(" ") + GetName();
|
|
break;
|
|
case REFRESHINGDETAILS:
|
|
message = _("Refreshing external table");
|
|
message += wxT(" ") + GetName();
|
|
break;
|
|
case DROPINCLUDINGDEPS:
|
|
message = wxString::Format(_("Are you sure you wish to drop external table \"%s\" including all objects that depend on it?"),
|
|
GetFullIdentifier().c_str());
|
|
break;
|
|
case DROPEXCLUDINGDEPS:
|
|
message = wxString::Format(_("Are you sure you wish to drop external table \"%s\"?"),
|
|
GetFullIdentifier().c_str());
|
|
break;
|
|
case DROPCASCADETITLE:
|
|
message = _("Drop external table cascaded?");
|
|
break;
|
|
case DROPTITLE:
|
|
message = _("Drop external table?");
|
|
break;
|
|
case PROPERTIESREPORT:
|
|
message = _("External table properties report");
|
|
message += wxT(" - ") + GetName();
|
|
break;
|
|
case PROPERTIES:
|
|
message = _("External table properties");
|
|
break;
|
|
case DDLREPORT:
|
|
message = _("External table DDL report");
|
|
message += wxT(" - ") + GetName();
|
|
break;
|
|
case DDL:
|
|
message = _("External table DDL");
|
|
break;
|
|
case DEPENDENCIESREPORT:
|
|
message = _("External table dependencies report");
|
|
message += wxT(" - ") + GetName();
|
|
break;
|
|
case DEPENDENCIES:
|
|
message = _("External table dependencies");
|
|
break;
|
|
case DEPENDENTSREPORT:
|
|
message = _("External table dependents report");
|
|
message += wxT(" - ") + GetName();
|
|
break;
|
|
case DEPENDENTS:
|
|
message = _("External table dependents");
|
|
break;
|
|
}
|
|
|
|
return message;
|
|
}
|
|
|
|
bool gpExtTable::IsUpToDate()
|
|
{
|
|
wxString sql = wxT("SELECT xmin FROM pg_class WHERE oid = ") + this->GetOidStr();
|
|
if (!this->GetDatabase()->GetConnection() || this->GetDatabase()->ExecuteScalar(sql) != NumToStr(GetXid()))
|
|
return false;
|
|
else
|
|
return true;
|
|
}
|
|
|
|
wxMenu *gpExtTable::GetNewMenu()
|
|
{
|
|
wxMenu *menu = pgObject::GetNewMenu();
|
|
if (schema->GetCreatePrivilege())
|
|
schemaFactory.AppendMenu(menu);
|
|
|
|
return menu;
|
|
}
|
|
|
|
|
|
bool gpExtTable::DropObject(wxFrame *frame, ctlTree *browser, bool cascaded)
|
|
{
|
|
wxString sql = wxT("DROP EXTERNAL TABLE ") + this->GetSchema()->GetQuotedIdentifier() + wxT(".") + this->GetQuotedIdentifier();
|
|
if (cascaded)
|
|
sql += wxT(" CASCADE");
|
|
return GetDatabase()->ExecuteVoid(sql);
|
|
}
|
|
|
|
wxString gpExtTable::GetSql(ctlTree *browser)
|
|
{
|
|
wxString colDetails;
|
|
wxString prevComment;
|
|
wxString q;
|
|
|
|
if (sql.IsNull())
|
|
{
|
|
sql = wxT("-- External Table: ") + GetQuotedFullIdentifier() + wxT("\n\n")
|
|
+ wxT("-- DROP EXTERNAL TABLE ") + GetQuotedFullIdentifier() + wxT(";\n\n");
|
|
/* Now get required information from pg_exttable */
|
|
if (GetDatabase()->BackendMinimumVersion(8, 2, 5))
|
|
{
|
|
q += wxT(
|
|
"SELECT x.location, x.fmttype, x.fmtopts, x.command, ")
|
|
wxT("x.rejectlimit, x.rejectlimittype,")
|
|
wxT("(SELECT relname ")
|
|
wxT("FROM pg_class ")
|
|
wxT("WHERE Oid=x.fmterrtbl) AS errtblname, ")
|
|
wxT("pg_catalog.pg_encoding_to_char(x.encoding) ")
|
|
wxT("FROM pg_catalog.pg_exttable x, pg_catalog.pg_class c ")
|
|
wxT("WHERE x.reloid = c.oid AND c.oid = ") + GetOidStr();
|
|
}
|
|
else
|
|
{
|
|
/* not SREH and encoding colums yet */
|
|
q += wxT(
|
|
"SELECT x.location, x.fmttype, x.fmtopts, x.command, ")
|
|
wxT("-1 as rejectlimit, null as rejectlimittype,")
|
|
wxT("null as errtblname, ")
|
|
wxT("null as encoding ")
|
|
wxT("FROM pg_catalog.pg_exttable x, pg_catalog.pg_class c ")
|
|
wxT("WHERE x.reloid = c.oid AND c.oid = ") + GetOidStr();
|
|
|
|
}
|
|
|
|
pgSet *extTable = GetDatabase()->ExecuteSet(q);
|
|
|
|
wxString locations = extTable->GetVal(0);
|
|
wxString fmttype = extTable->GetVal(1);
|
|
wxString fmtopts = extTable->GetVal(2);
|
|
wxString command = extTable->GetVal(3);
|
|
wxString rejlim = extTable->GetVal(4);
|
|
wxString rejlimtype = extTable->GetVal(5);
|
|
wxString errtblname = extTable->GetVal(6);
|
|
wxString extencoding = extTable->GetVal(7);
|
|
|
|
delete extTable;
|
|
|
|
if ((command.Length() > 0) ||
|
|
(locations.Mid(1, 4) == wxT("http")))
|
|
{
|
|
sql += wxT("CREATE EXTERNAL WEB TABLE ") +
|
|
GetQuotedFullIdentifier() + wxT("\n(\n");
|
|
}
|
|
else
|
|
{
|
|
sql += wxT("CREATE EXTERNAL TABLE ") +
|
|
GetQuotedFullIdentifier() + wxT("\n(\n");
|
|
}
|
|
|
|
// Get the columns
|
|
pgCollection *columns = browser->FindCollection(columnFactory, GetId());
|
|
if (columns)
|
|
{
|
|
columns->ShowTreeDetail(browser);
|
|
treeObjectIterator colIt1(browser, columns);
|
|
treeObjectIterator colIt2(browser, columns);
|
|
|
|
|
|
int lastRealCol = 0;
|
|
int currentCol = 0;
|
|
pgColumn *column;
|
|
|
|
// Iterate the columns to find the last 'real' one
|
|
while ((column = (pgColumn *)colIt1.GetNextObject()) != 0)
|
|
{
|
|
currentCol++;
|
|
|
|
if (column->GetInheritedCount() == 0)
|
|
lastRealCol = currentCol;
|
|
}
|
|
|
|
// Now build the actual column list
|
|
int colCount = 0;
|
|
while ((column = (pgColumn *)colIt2.GetNextObject()) != 0)
|
|
{
|
|
column->ShowTreeDetail(browser);
|
|
if (column->GetColNumber() > 0)
|
|
{
|
|
if (colCount)
|
|
{
|
|
// Only add a comma if this isn't the last 'real' column
|
|
if (colCount != lastRealCol)
|
|
sql += wxT(",");
|
|
if (!prevComment.IsEmpty())
|
|
sql += wxT(" -- ") + firstLineOnly(prevComment);
|
|
|
|
sql += wxT("\n");
|
|
}
|
|
|
|
/* if (column->GetInheritedCount() > 0)
|
|
{
|
|
if (!column->GetIsLocal())
|
|
sql += wxString::Format(wxT("-- %s "), _("Inherited"))
|
|
+ wxT("from table ") + column->GetInheritedTableName() + wxT(":");
|
|
}*/
|
|
|
|
sql += wxT(" ") + column->GetQuotedIdentifier() + wxT(" ")
|
|
+ column->GetDefinition();
|
|
|
|
prevComment = column->GetComment();
|
|
|
|
// Whilst we are looping round the columns, grab their comments as well.
|
|
// Perhaps we should also get storage types here?
|
|
colDetails += column->GetCommentSql();
|
|
if (colDetails.Length() > 0)
|
|
if (colDetails.Last() != '\n') colDetails += wxT("\n");
|
|
|
|
colCount++;
|
|
}
|
|
}
|
|
}
|
|
if (!prevComment.IsEmpty())
|
|
sql += wxT(" -- ") + firstLineOnly(prevComment);
|
|
|
|
sql += wxT("\n)\n");
|
|
|
|
if(command.Length() > 0)
|
|
{
|
|
|
|
wxString on_clause = locations;
|
|
|
|
/* remove curly braces */
|
|
on_clause = locations.Mid(1, locations.Length() - 2);
|
|
|
|
|
|
/* add EXECUTE clause */
|
|
sql += wxT(" EXECUTE E'");
|
|
for (size_t i = 0; i < command.Length(); i++)
|
|
{
|
|
if (command[i] == wxT('\\')) sql += wxT('\\');
|
|
if (command[i] == wxT('\'')) sql += wxT('\'');
|
|
sql += command[i];
|
|
}
|
|
sql += wxT("' ");
|
|
|
|
|
|
/* add ON clause */
|
|
wxString temp;
|
|
|
|
if(on_clause.StartsWith(wxT("HOST:"), &temp))
|
|
{
|
|
sql += wxT("ON HOST '") + temp + wxT("'");
|
|
}
|
|
else if(on_clause.StartsWith(wxT("PER_HOST"), &temp))
|
|
sql += wxT("ON HOST ");
|
|
else if(on_clause.StartsWith(wxT("MASTER_ONLY"), &temp))
|
|
sql += wxT("ON MASTER ");
|
|
else if(on_clause.StartsWith(wxT("SEGMENT_ID:"), &temp))
|
|
sql += wxT("ON SEGMENT ") + temp + wxT(" ");
|
|
else if(on_clause.StartsWith(wxT("TOTAL_SEGS:"), &temp))
|
|
sql += wxT("ON ") + temp + wxT(" ");
|
|
else if(on_clause.StartsWith(wxT("ALL_SEGMENTS"), &temp))
|
|
sql += wxT("ON ALL ");
|
|
else
|
|
sql += on_clause;
|
|
|
|
sql += wxT("\n ");
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
/* add LOCATION clause */
|
|
|
|
locations = locations.Mid(1, locations.Length() - 2);
|
|
wxStringTokenizer locs(locations, wxT(","));
|
|
wxString token;
|
|
token = locs.GetNextToken();
|
|
sql += wxT(" LOCATION (\n '");
|
|
sql += token;
|
|
sql += wxT("'");
|
|
while (locs.HasMoreTokens())
|
|
{
|
|
sql += wxT(",\n '");
|
|
sql += locs.GetNextToken();
|
|
sql += wxT("'");
|
|
}
|
|
sql += wxT("\n)\n ");
|
|
|
|
}
|
|
|
|
/* add FORMAT clause */
|
|
sql += wxT("FORMAT '");
|
|
sql += fmttype[0] == 't' ? wxT("text") : wxT("csv");
|
|
sql += wxT("'");
|
|
sql += wxT(" (");
|
|
for (size_t i = 0; i < fmtopts.Length(); i++)
|
|
{
|
|
if (fmtopts[i] == wxT('\\')) sql += wxT('\\');
|
|
sql += fmtopts[i];
|
|
}
|
|
sql += wxT(")\n");
|
|
|
|
if (GetDatabase()->BackendMinimumVersion(8, 2))
|
|
{
|
|
/* add ENCODING clause */
|
|
sql += wxT("ENCODING '");
|
|
sql += extencoding ;
|
|
sql += wxT("'");
|
|
|
|
/* add Single Row Error Handling clause (if any) */
|
|
if(rejlim.Length() > 0)
|
|
{
|
|
sql += wxT("\n");
|
|
|
|
/*
|
|
* NOTE: error tables get automatically generated if don't exist.
|
|
* therefore we must be sure that this statement will be dumped after
|
|
* the error relation CREATE is dumped, so that we won't try to
|
|
* create it twice. For now we rely on the fact that we pick dumpable
|
|
* objects sorted by OID, and error table oid *should* always be less
|
|
* than its external table oid (could that not be true sometimes?)
|
|
*/
|
|
if(errtblname.Length() > 0)
|
|
{
|
|
sql += wxT("LOG ERRORS INTO ");
|
|
sql += errtblname;
|
|
sql += wxT(" ");
|
|
}
|
|
|
|
/* reject limit */
|
|
sql += wxT("SEGMENT REJECT LIMIT ");
|
|
sql += rejlim;
|
|
|
|
/* reject limit type */
|
|
if(rejlimtype[0] == 'r')
|
|
sql += wxT(" ROWS");
|
|
else
|
|
sql += wxT(" PERCENT");
|
|
}
|
|
}
|
|
|
|
sql += wxT(";\n")
|
|
+ GetOwnerSql(7, 3, wxEmptyString, wxT("TABLE"));
|
|
|
|
|
|
sql += GetGrant(wxT("r"), wxT("TABLE ") + GetQuotedFullIdentifier());
|
|
|
|
sql += GetCommentSql()
|
|
+ wxT("\n");
|
|
|
|
// Column/constraint comments
|
|
if (!colDetails.IsEmpty())
|
|
sql += colDetails + wxT("\n");
|
|
|
|
}
|
|
return sql;
|
|
}
|
|
|
|
|
|
wxString gpExtTable::GetCols(ctlTree *browser, size_t indent, wxString &QMs, bool withQM)
|
|
{
|
|
wxString sql;
|
|
wxString line;
|
|
|
|
int colcount = 0;
|
|
pgSetIterator set(GetConnection(),
|
|
wxT("SELECT attname\n")
|
|
wxT(" FROM pg_attribute\n")
|
|
wxT(" WHERE attrelid=") + GetOidStr() + wxT(" AND attnum>0\n")
|
|
wxT(" ORDER BY attnum"));
|
|
|
|
|
|
while (set.RowsLeft())
|
|
{
|
|
if (colcount++)
|
|
{
|
|
line += wxT(", ");
|
|
QMs += wxT(", ");
|
|
}
|
|
if (line.Length() > 60)
|
|
{
|
|
if (!sql.IsEmpty())
|
|
{
|
|
sql += wxT("\n") + wxString(' ', indent);
|
|
}
|
|
sql += line;
|
|
line = wxEmptyString;
|
|
QMs += wxT("\n") + wxString(' ', indent);
|
|
}
|
|
|
|
line += qtIdent(set.GetVal(0));
|
|
if (withQM)
|
|
line += wxT("=?");
|
|
QMs += wxT("?");
|
|
}
|
|
|
|
if (!line.IsEmpty())
|
|
{
|
|
if (!sql.IsEmpty())
|
|
sql += wxT("\n") + wxString(' ', indent);
|
|
sql += line;
|
|
}
|
|
return sql;
|
|
}
|
|
|
|
|
|
wxString gpExtTable::GetSelectSql(ctlTree *browser)
|
|
{
|
|
wxString qms;
|
|
wxString sql =
|
|
wxT("SELECT ") + GetCols(browser, 7, qms, false) + wxT("\n")
|
|
wxT(" FROM ") + GetQuotedFullIdentifier() + wxT(";\n");
|
|
return sql;
|
|
}
|
|
|
|
|
|
void gpExtTable::ShowTreeDetail(ctlTree *browser, frmMain *form, ctlListView *properties, ctlSQLBox *sqlPane)
|
|
{
|
|
if (!expandedKids)
|
|
{
|
|
expandedKids = true;
|
|
browser->RemoveDummyChild(this);
|
|
|
|
browser->AppendCollection(this, columnFactory);
|
|
}
|
|
if (properties)
|
|
{
|
|
CreateListColumns(properties);
|
|
|
|
properties->AppendItem(_("Name"), GetName());
|
|
properties->AppendItem(_("OID"), GetOid());
|
|
properties->AppendItem(_("Owner"), GetOwner());
|
|
properties->AppendItem(_("ACL"), GetAcl());
|
|
properties->AppendYesNoItem(_("System Table?"), GetSystemObject());
|
|
properties->AppendItem(_("Comment"), firstLineOnly(GetComment()));
|
|
}
|
|
}
|
|
|
|
|
|
|
|
pgObject *gpExtTable::Refresh(ctlTree *browser, const wxTreeItemId item)
|
|
{
|
|
pgObject *extTable = 0;
|
|
pgCollection *coll = browser->GetParentCollection(item);
|
|
if (coll)
|
|
{
|
|
extTable = extTableFactory.CreateObjects(coll, 0, wxT("\n AND c.oid=") + GetOidStr());
|
|
}
|
|
|
|
return extTable;
|
|
}
|
|
|
|
void gpExtTable::ShowHint(frmMain *form, bool force)
|
|
{
|
|
wxArrayString hints;
|
|
hints.Add(HINT_OBJECT_EDITING);
|
|
frmHint::ShowHint((wxWindow *)form, hints, GetFullIdentifier(), force);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////
|
|
|
|
|
|
pgObject *gpExtTableFactory::CreateObjects(pgCollection *collection, ctlTree *browser, const wxString &restriction)
|
|
{
|
|
gpExtTable *extTable = 0;
|
|
|
|
|
|
pgSet *extTables = collection->GetDatabase()->ExecuteSet(
|
|
wxT("SELECT c.oid, c.xmin, c.relname, pg_get_userbyid(c.relowner) AS exttableowner, c.relacl AS relacl, description \n")
|
|
wxT(" FROM pg_class c\n")
|
|
wxT(" LEFT OUTER JOIN pg_description des ON (des.objoid=c.oid AND des.objsubid=0 AND des.classoid='pg_class'::regclass)\n")
|
|
wxT(" WHERE (c.relkind = 'x' OR (c.relkind = 'r' AND c.relstorage = 'x'))\n")
|
|
wxT(" AND relnamespace = ") + collection->GetSchema()->GetOidStr() + wxT("\n")
|
|
+ restriction
|
|
+ wxT(" ORDER BY relname"));
|
|
|
|
if (extTables)
|
|
{
|
|
while (!extTables->Eof())
|
|
{
|
|
extTable = new gpExtTable(collection->GetSchema(), extTables->GetVal(wxT("relname")));
|
|
|
|
extTable->iSetOid(extTables->GetOid(wxT("oid")));
|
|
extTable->iSetXid(extTables->GetOid(wxT("xmin")));
|
|
extTable->iSetOwner(extTables->GetVal(wxT("exttableowner")));
|
|
extTable->iSetComment(extTables->GetVal(wxT("description")));
|
|
extTable->iSetAcl(extTables->GetVal(wxT("relacl")));
|
|
//extTable->iSetDefinition(extTables->GetVal(wxT("definition")));
|
|
|
|
if (browser)
|
|
{
|
|
collection->AppendBrowserItem(browser, extTable);
|
|
extTables->MoveNext();
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
|
|
delete extTables;
|
|
}
|
|
return extTable;
|
|
}
|
|
|
|
/////////////////////////////
|
|
|
|
gpExtTableCollection::gpExtTableCollection(pgaFactory *factory, pgSchema *sch)
|
|
: pgSchemaObjCollection(factory, sch)
|
|
{
|
|
}
|
|
|
|
wxString gpExtTableCollection::GetTranslatedMessage(int kindOfMessage) const
|
|
{
|
|
wxString message = wxEmptyString;
|
|
|
|
switch (kindOfMessage)
|
|
{
|
|
case RETRIEVINGDETAILS:
|
|
message = _("Retrieving details on external tables");
|
|
break;
|
|
case REFRESHINGDETAILS:
|
|
message = _("Refreshing external tables");
|
|
break;
|
|
case GRANTWIZARDTITLE:
|
|
message = _("Privileges for external tables");
|
|
break;
|
|
case OBJECTSLISTREPORT:
|
|
message = _("External tables list report");
|
|
break;
|
|
}
|
|
|
|
return message;
|
|
}
|
|
|
|
/////////////////////////////
|
|
|
|
#include "images/exttable.pngc"
|
|
#include "images/exttable-sm.pngc"
|
|
#include "images/exttables.pngc"
|
|
|
|
gpExtTableFactory::gpExtTableFactory()
|
|
: pgSchemaObjFactory(__("External Table"), __("New External Table..."), __("Create a new External Table."), exttable_png_img, exttable_sm_png_img)
|
|
{
|
|
metaType = GP_EXTTABLE;
|
|
}
|
|
|
|
|
|
pgCollection *gpExtTableFactory::CreateCollection(pgObject *obj)
|
|
{
|
|
return new gpExtTableCollection(GetCollectionFactory(), (pgSchema *)obj);
|
|
}
|
|
|
|
gpExtTableFactory extTableFactory;
|
|
static pgaCollectionFactory cf(&extTableFactory, __("External Tables"), exttables_png_img);
|