blob: 2dd0da6d57cd5b4a08a169dd2722c8f5a516cc8c [file] [log] [blame]
/*---------------------------------------------------------------------
*
* gp_dump_oids.c
*
* Copyright (c) 2015-Present VMware, Inc. or its affiliates.
*
*
* IDENTIFICATION
* src/backend/utils/adt/gp_dump_oids.c
*
*---------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/genam.h"
#include "access/table.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/pg_attrdef.h"
#include "catalog/pg_depend.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_proc.h"
#include "common/hashfn.h"
#include "tcop/tcopprot.h"
#include "optimizer/optimizer.h"
#include "optimizer/planmain.h"
#include "parser/analyze.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/hsearch.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
static List *proc_oids_for_dump = NIL;
static bool is_proc_oids_valid = false;
Datum gp_dump_query_oids(PG_FUNCTION_ARGS);
List* get_proc_oids_for_dump(void);
PG_FUNCTION_INFO_V1(gp_dump_query_oids);
/*
* Append a list of Oids to a StringInfo, separated by commas.
*/
static void
appendOids(StringInfo buf, List *oids)
{
ListCell *lc;
bool first = true;
foreach(lc, oids)
{
if (!first)
appendStringInfoChar(buf, ',');
appendStringInfo(buf, "%u", lfirst_oid(lc));
first = false;
}
}
/*
* Error context callback for handling errors in the SQL query passed as
* argument to gp_dump_query_oids()
*/
static void
sql_query_parse_error_callback(void *arg)
{
const char *query_text = (const char *) arg;
int syntaxerrposition = geterrposition();
/*
* The error is not in the original query. Report the query that was
* passed as argument as an "internal query", and the position in it.
*/
errposition(0);
internalerrposition(syntaxerrposition);
internalerrquery(query_text);
}
void
add_proc_oids_for_dump(Oid funcid)
{
if (is_proc_oids_valid)
proc_oids_for_dump = lappend_oid(proc_oids_for_dump, funcid);
}
List*
get_proc_oids_for_dump()
{
return proc_oids_for_dump;
}
/*
* Collect a list of OIDs of all attribute default of the specified relation,
* if they have a dependency from the attribute default to the relation.
*/
static List *
getAttrDefaultOfRel(Oid relid)
{
List *result = NIL;
Relation depRel;
ScanKeyData key[2];
SysScanDesc scan;
HeapTuple tup;
depRel = table_open(DependRelationId, AccessShareLock);
ScanKeyInit(&key[0],
Anum_pg_depend_refclassid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(RelationRelationId));
ScanKeyInit(&key[1],
Anum_pg_depend_refobjid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(relid));
scan = systable_beginscan(depRel, DependReferenceIndexId, true,
NULL, 2, key);
while (HeapTupleIsValid(tup = systable_getnext(scan)))
{
Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);
if (deprec->classid == AttrDefaultRelationId &&
deprec->objsubid == 0 &&
deprec->refobjsubid != 0)
{
result = lappend_oid(result, deprec->objid);
}
}
systable_endscan(scan);
table_close(depRel, AccessShareLock);
return result;
}
/*
* Collect a list of OIDs of all sequences referenced by the specified relation,
* whether or not they are owned by relation.
*/
static List *
getRefedSequences(Oid relid)
{
List *result = getAttrDefaultOfRel(relid);
List *seqresult = NIL;
Relation depRel;
ScanKeyData key[2];
SysScanDesc scan;
HeapTuple tup;
ListCell *lc;
if (!result)
return seqresult;
depRel = table_open(DependRelationId, AccessShareLock);
foreach(lc, result)
{
Oid objid = lfirst_oid(lc);
ScanKeyInit(&key[0],
Anum_pg_depend_classid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(AttrDefaultRelationId));
ScanKeyInit(&key[1],
Anum_pg_depend_objid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(objid));
scan = systable_beginscan(depRel, DependDependerIndexId, true,
NULL, 2, key);
while (HeapTupleIsValid(tup = systable_getnext(scan)))
{
Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);
/*
* The attrdef have dependency on both relation column and seuqence if they invoke
* nextval(). We need the relkind test)
*/
if(deprec->refclassid == RelationRelationId &&
deprec->refobjsubid == 0 &&
get_rel_relkind(deprec->refobjid) == RELKIND_SEQUENCE)
{
seqresult = lappend_oid(seqresult, deprec->refobjid);
}
}
systable_endscan(scan);
}
table_close(depRel, AccessShareLock);
return seqresult;
}
/*
* Function dumping dependent relation & function oids for a given SQL text
*/
Datum
gp_dump_query_oids(PG_FUNCTION_ARGS)
{
char *sqlText = text_to_cstring(PG_GETARG_TEXT_P(0));
List *raw_parsetree_list;
List *flat_query_list;
ListCell *lc;
HASHCTL ctl;
HTAB *dedup_htab;
StringInfoData str;
ErrorContextCallback sqlerrcontext;
List *invalidItems = NIL;
List *relationOids = NIL;
List *seqrelationOids = NIL;
List *deduped_relationOids = NIL;
List *procOids = NIL;
/*
* Setup error traceback support for ereport().
*/
sqlerrcontext.callback = sql_query_parse_error_callback;
sqlerrcontext.arg = sqlText;
sqlerrcontext.previous = error_context_stack;
error_context_stack = &sqlerrcontext;
/*
* Parse and analyze the query.
*/
raw_parsetree_list = pg_parse_query(sqlText);
flat_query_list = NIL;
foreach(lc, raw_parsetree_list)
{
RawStmt *parsetree = lfirst_node(RawStmt, lc);
List *queryTree_sublist;
Query *query = parse_analyze_varparams(parsetree, sqlText, NULL, 0, NULL);
query->expandMatViews = true;
queryTree_sublist = pg_rewrite_query(query);
flat_query_list = list_concat(flat_query_list,
list_copy(queryTree_sublist));
}
error_context_stack = sqlerrcontext.previous;
/* Then scan each Query and scrape any relation and function OIDs */
is_proc_oids_valid = true;
PG_TRY();
{
foreach(lc, flat_query_list)
{
Query *q = lfirst(lc);
List *q_relationOids = NIL;
List *q_invalidItems = NIL;
bool hasRowSecurity = false;
extract_query_dependencies((Node *) q, &q_relationOids, &q_invalidItems, &hasRowSecurity);
relationOids = list_concat(relationOids, q_relationOids);
invalidItems = list_concat(invalidItems, q_invalidItems);
}
}
PG_CATCH();
{
/* Make proc_oids_valid set false and proc_oids_for_dump set null */
is_proc_oids_valid = false;
proc_oids_for_dump = NIL;
PG_RE_THROW();
}
PG_END_TRY();
is_proc_oids_valid = false;
/*
* Collect referenced sequences
*/
foreach(lc, relationOids)
{
Oid relid = lfirst_oid(lc);
seqrelationOids = list_concat(seqrelationOids, getRefedSequences(relid));
}
relationOids = list_concat(relationOids, seqrelationOids);
/*
* Deduplicate the relation oids
*/
memset(&ctl, 0, sizeof(HASHCTL));
ctl.keysize = sizeof(Oid);
ctl.entrysize = sizeof(Oid);
ctl.hash = oid_hash;
dedup_htab = hash_create("relid hash table", 100, &ctl, HASH_ELEM | HASH_FUNCTION);
deduped_relationOids = NIL;
foreach(lc, relationOids)
{
Oid relid = lfirst_oid(lc);
bool found;
hash_search(dedup_htab, (void *) &relid, HASH_ENTER, &found);
if (!found)
{
deduped_relationOids = lappend_oid(deduped_relationOids, relid);
/*
* Also find all child table relids including inheritances,
* interior and leaf partitions of given root table oid
*/
List *child_relids = find_all_inheritors(relid, NoLock, NULL);
if (child_relids)
{
child_relids = list_delete_first(child_relids);
deduped_relationOids = list_concat(deduped_relationOids, child_relids);
}
}
}
relationOids = deduped_relationOids;
/*
* Fetch the procedure OIDs based on the PlanInvalItems. Deduplicate them as
* we go.
*/
memset(&ctl, 0, sizeof(HASHCTL));
ctl.keysize = sizeof(Oid);
ctl.entrysize = sizeof(Oid);
ctl.hash = oid_hash;
dedup_htab = hash_create("funcid hash table", 100, &ctl, HASH_ELEM | HASH_FUNCTION);
foreach (lc, get_proc_oids_for_dump())
{
Oid funcId = lfirst_oid(lc);
bool found;
hash_search(dedup_htab, (void *) &funcId, HASH_ENTER, &found);
if (!found)
procOids = lappend_oid(procOids, funcId);
}
proc_oids_for_dump = NIL;
/*
* Construct the final output
*/
initStringInfo(&str);
appendStringInfo(&str, "{\"relids\": \"");
appendOids(&str, relationOids);
appendStringInfoString(&str, "\", \"funcids\": \"");
appendOids(&str, procOids);
appendStringInfo(&str, "\"}");
PG_RETURN_TEXT_P(cstring_to_text(str.data));
}