////////////////////////////////////////////////////////////////////////// // // pgAdmin III - PostgreSQL Tools // RCS-ID: $Id: $ // Copyright (C) 2002 - 2009, The pgAdmin Development Team // This software is released under the Artistic Licence // // pgTable.cpp - Greenplum External Table // ////////////////////////////////////////////////////////////////////////// // wxWindows headers #include // App headers #include "pgAdmin3.h" #include "utils/misc.h" #include "schema/pgColumn.h" #include "schema/pgExtTable.h" #include "frm/frmHint.h" pgExtTable::pgExtTable(pgSchema *newSchema, const wxString& newName) : pgSchemaObject(newSchema, extTableFactory, newName) { } pgExtTable::~pgExtTable() { } bool pgExtTable::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 *pgExtTable::GetNewMenu() { wxMenu *menu=pgObject::GetNewMenu(); if (schema->GetCreatePrivilege()) schemaFactory.AppendMenu(menu); return menu; } bool pgExtTable::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 pgExtTable::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); 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 statment 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); sql += GetGrant(wxT("r"), wxT("TABLE ") + GetQuotedFullIdentifier()); sql += GetCommentSql() + wxT("\n"); // Column/constraint comments if (!colDetails.IsEmpty()) sql += colDetails + wxT("\n"); } return sql; } wxString pgExtTable::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 pgExtTable::GetSelectSql(ctlTree *browser) { wxString qms; wxString sql= wxT("SELECT ") + GetCols(browser, 7, qms, false) + wxT("\n") wxT(" FROM ") + GetQuotedFullIdentifier() + wxT(";\n"); return sql; } void pgExtTable::ShowTreeDetail(ctlTree *browser, frmMain *form, ctlListView *properties, ctlSQLBox *sqlPane) { if (!expandedKids) { expandedKids = true; browser->RemoveDummyChild(this); browser->AppendCollection(this, columnFactory); pgCollection *collection = browser->AppendCollection(this, extTableFactory); collection->iSetOid(GetOid()); collection->ShowTreeDetail(browser); treeObjectIterator colIt(browser, collection); } if (properties) { CreateListColumns(properties); // wxString def=GetDefinition().Left(250); // def.Replace(wxT("\n"), wxT(" ")); properties->AppendItem(_("Name"), GetName()); properties->AppendItem(_("OID"), GetOid()); properties->AppendItem(_("Owner"), GetOwner()); properties->AppendItem(_("ACL"), GetAcl()); //properties->AppendItem(_("Definition"), def); properties->AppendItem(_("System Table?"), GetSystemObject()); properties->AppendItem(_("Comment"), firstLineOnly(GetComment())); } } pgObject *pgExtTable::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 pgExtTable::ShowHint(frmMain *form, bool force) { wxArrayString hints; hints.Add(HINT_OBJECT_EDITING); frmHint::ShowHint((wxWindow *)form, hints, GetFullIdentifier(), force); } /////////////////////////////////////////////////////// pgObject *pgExtTableFactory::CreateObjects(pgCollection *collection, ctlTree *browser, const wxString &restriction) { pgExtTable *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)\n") wxT(" WHERE (c.relkind = 'x'::char)\n") wxT(" AND relnamespace = ") + collection->GetSchema()->GetOidStr() + wxT("\n") + restriction + wxT(" ORDER BY relname")); if (extTables) { while (!extTables->Eof()) { extTable = new pgExtTable(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; } #include "images/exttable.xpm" #include "images/exttable-sm.xpm" #include "images/exttables.xpm" pgExtTableFactory::pgExtTableFactory() : pgSchemaObjFactory(__("External Table"), __("New External Table..."), __("Create a new External Table."), exttable_xpm, exttable_sm_xpm) { metaType = PGM_EXTTABLE; } pgExtTableFactory extTableFactory; static pgaCollectionFactory cf(&extTableFactory, __("External Tables"), exttables_xpm);