39#include "lib/stringinfo.h"
40#include "nodes/bitmapset.h"
41#include "nodes/nodes.h"
42#include "nodes/parsenodes.h"
43#include "nodes/pg_list.h"
44#if PG_VERSION_NUM >= 120000
45#include "optimizer/optimizer.h"
47#include "optimizer/tlist.h"
49#include "utils/builtins.h"
50#include "utils/lsyscache.h"
89 if (IsA(n, RangeTblRef))
91 if (IsA(n, JoinExpr)) {
92 JoinExpr *je = (JoinExpr *) n;
93 if (je->jointype != JOIN_INNER)
122 if (q == NULL || q->commandType != CMD_SELECT) {
139 || q->hasModifyingCTE
141 || q->setOperations != NULL
142 || q->distinctClause != NIL
143 || q->groupClause != NIL
144 || q->groupingSets != NIL
145 || q->havingQual != NULL
151 if (*shape_ok && q->jointree != NULL && q->jointree->fromlist != NIL) {
152 foreach (lc, q->jointree->fromlist) {
179 foreach (lc, q->rtable) {
180 RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
183 if (rte->rtekind == RTE_RELATION) {
186 *sole_relid = rte->relid;
189 }
else if (rte->rtekind == RTE_SUBQUERY) {
195 classify_walk(rte->subquery, out, shape_ok, n_meta, sole_relid);
196 }
else if (rte->rtekind == RTE_JOIN) {
221 if (IsA(node, RangeTblRef)) {
222 int rtindex = ((RangeTblRef *) node)->rtindex;
224 if (rtindex < 1 || rtindex > list_length(parent->rtable))
226 rte = (RangeTblEntry *) list_nth(parent->rtable, rtindex - 1);
227 if (rte->rtekind != RTE_SUBQUERY || rte->subquery == NULL)
229 *legs = lappend(*legs, rte->subquery);
232 if (IsA(node, SetOperationStmt)) {
233 SetOperationStmt *s = (SetOperationStmt *) node;
234 if (s->op != SETOP_UNION || !s->all)
281 if (q->setOperations == NULL || !IsA(q->setOperations, SetOperationStmt))
290 Query *leg_query = (Query *) lfirst(lc);
302 Oid relid = lfirst_oid(src_lc);
303 if (list_member_oid(seen, relid)) {
309 seen = lappend_oid(seen, relid);
349 anc_n = palloc0(n *
sizeof(uint16));
350 anc = palloc(n *
sizeof(*anc));
354 Oid relid = lfirst_oid(lc);
373 for (i = 0; i < n; i++)
374 for (j = i + 1; j < n; j++) {
376 for (a = 0; a < anc_n[i]; a++)
377 for (b = 0; b < anc_n[j]; b++)
378 if (anc[i][a] == anc[j][b]) {
402 Oid *out_relid, AttrNumber *out_attno) {
404 if (q == NULL || varno < 1
405 || (
int) varno > list_length(q->rtable))
407 rte = (RangeTblEntry *) list_nth(q->rtable, varno - 1);
408 if (rte->rtekind == RTE_RELATION) {
409 *out_relid = rte->relid;
413 if (rte->rtekind == RTE_SUBQUERY && rte->subquery != NULL) {
414 Query *sub = rte->subquery;
422 foreach (lc, sub->targetList) {
423 TargetEntry *t = (TargetEntry *) lfirst(lc);
424 if (t->resno == attno && !t->resjunk) {
431 e = (Node *) te->expr;
432 while (e != NULL && IsA(e, RelabelType))
433 e = (Node *) ((RelabelType *) e)->arg;
434 if (e == NULL || !IsA(e, Var))
437 if (v->varlevelsup != 0)
440 out_relid, out_attno);
462 foreach (lc, q->targetList) {
463 TargetEntry *te = (TargetEntry *) lfirst(lc);
464 Node *e = (Node *) te->expr;
467 AttrNumber base_attno;
470 while (e != NULL && IsA(e, RelabelType))
471 e = (Node *) ((RelabelType *) e)->arg;
472 if (e == NULL || !IsA(e, Var))
475 if (v->varlevelsup != 0)
478 &base_relid, &base_attno))
480 if (base_relid == source_relid && base_attno == bk) {
502#if PG_VERSION_NUM >= 180000
504 Index group_rtindex = 0;
506 List *groupexprs = NIL;
509 if (e == NULL || !q->hasGroupRTE)
511 foreach (lc, q->rtable) {
512 RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
513 if (rte->rtekind == RTE_GROUP) {
515 groupexprs = rte->groupexprs;
520 if (group_rtindex == 0)
523 while (e != NULL && IsA(e, RelabelType))
524 e = (Node *) ((RelabelType *) e)->arg;
525 if (e == NULL || !IsA(e, Var))
528 if (v->varlevelsup != 0 || v->varno != group_rtindex)
530 if (v->varattno < 1 || v->varattno > list_length(groupexprs))
532 return (Node *) list_nth(groupexprs, v->varattno - 1);
568 Bitmapset *resolved = NULL;
571 if (q->groupClause == NIL)
573 if (q->hasAggs || q->hasWindowFuncs || q->hasTargetSRFs
574 || q->hasSubLinks || q->hasModifyingCTE || q->hasDistinctOn)
576 if (q->cteList != NIL || q->groupingSets != NIL
577 || q->havingQual != NULL || q->setOperations != NULL
578 || q->distinctClause != NIL)
580 if (q->jointree == NULL
581 || list_length(q->jointree->fromlist) != 1)
583 if (!IsA(linitial(q->jointree->fromlist), RangeTblRef))
585 rtr = (RangeTblRef *) linitial(q->jointree->fromlist);
586 if (rtr->rtindex < 1 || rtr->rtindex > list_length(q->rtable))
588 rte = (RangeTblEntry *) list_nth(q->rtable, rtr->rtindex - 1);
589 if (rte->rtekind != RTE_RELATION)
598 foreach (lc, q->groupClause) {
599 SortGroupClause *sgc = (SortGroupClause *) lfirst(lc);
603 bool attno_in_block_key =
false;
605 te = get_sortgroupclause_tle(sgc, q->targetList);
606 if (te == NULL) { bms_free(resolved);
return false; }
607 e = (Node *) te->expr;
612 while (e != NULL && IsA(e, RelabelType))
613 e = (Node *) ((RelabelType *) e)->arg;
614 if (e == NULL || !IsA(e, Var)) { bms_free(resolved);
return false; }
616 if (v->varlevelsup != 0 || v->varno != rtr->rtindex) {
617 bms_free(resolved);
return false;
621 attno_in_block_key =
true;
break;
623 if (!attno_in_block_key) { bms_free(resolved);
return false; }
624 resolved = bms_add_member(resolved, v->varattno);
628 if (!bms_is_member(info.
block_key[i], resolved)) {
629 bms_free(resolved);
return false;
639 bool shape_ok =
true;
641 Oid sole_relid = InvalidOid;
646 if (q == NULL || q->commandType != CMD_SELECT)
676 }
else if (n_meta == 0) {
680 }
else if (n_meta == 1) {
718 initStringInfo(&buf);
719 appendStringInfo(&buf,
"query result is %s",
kind_label(c->
kind));
731 appendStringInfoString(&buf,
" (no provenance-tracked sources)");
733 appendStringInfoString(&buf,
" (sources: ");
735 Oid relid = lfirst_oid(lc);
736 char *nspname = get_namespace_name(get_rel_namespace(relid));
737 char *relname = get_rel_name(relid);
740 appendStringInfoString(&buf,
", ");
743 if (nspname != NULL && relname != NULL)
744 appendStringInfo(&buf,
"%s.%s",
745 quote_identifier(nspname),
746 quote_identifier(relname));
747 else if (relname != NULL)
748 appendStringInfoString(&buf, quote_identifier(relname));
750 appendStringInfo(&buf,
"<oid %u>", relid);
752 appendStringInfoChar(&buf,
')');
provsql_table_kind
How the provenance leaves of a tracked relation are correlated.
#define PROVSQL_TABLE_INFO_MAX_ANCESTORS
Cap on the number of base ancestors recorded per relation.
static void classify_walk(Query *q, ProvSQLClassification *out, bool *shape_ok, int *n_meta, Oid *sole_relid)
Recursive walker shared by the top-level entry point and the RTE_SUBQUERY descent.
bool provsql_classify_top_level
Backing storage for the provsql.classify_top_level GUC.
static bool collect_union_all_legs(Node *node, Query *parent, List **legs)
Walk a SetOperationStmt tree, collecting each leaf leg's Query body into legs.
static bool try_classify_multi_source_tid(ProvSQLClassification *out)
Conservative multi-source promotion: when every tracked source in out->source_relids is TID and the r...
static bool bid_block_key_preserved(Query *q, Oid source_relid, const ProvenanceTableInfo *info)
Decide whether every block-key column of info survives in q's target list – resolved transitively thr...
void provsql_classify_emit_notice(const ProvSQLClassification *c)
Render the result of provsql_classify_query as a NOTICE.
static bool try_classify_groupby_block_key(Query *q, ProvSQLClassification *out)
Pre-dispatch special case for GROUP BY on a single BID source's block-key columns.
static bool try_classify_union_all(Query *q, ProvSQLClassification *out)
Promote a fully-UNION-ALL Query to TID when each leg classifies as TID and the leg source-relid sets ...
static Node * resolve_through_group_rte(Query *q, Node *e)
PG 18+ helper: when q has a synthetic RTE_GROUP entry (set parseCheckAggregates() appends it for ever...
static bool resolve_var_to_base(Query *q, Index varno, AttrNumber attno, Oid *out_relid, AttrNumber *out_attno)
Resolve a base-level (varno, attno) pair in q transitively through RTE_SUBQUERY layers until reaching...
static bool classify_fromlist_shape_ok(Node *n)
Decide whether a jointree fromlist entry has a shape the classifier can certify : a plain RangeTblRef...
static const char * kind_label(provsql_table_kind k)
Map a provsql_table_kind to its uppercase user-facing label.
void provsql_classify_query(Query *q, ProvSQLClassification *out)
Classify the result relation of a parsed top-level Query.
Public surface of the query-time TID / BID / OPAQUE classifier.
#define provsql_notice(fmt,...)
Emit a ProvSQL informational notice (execution continues).
bool provsql_lookup_ancestry(Oid relid, uint16 *ancestor_n_out, Oid *ancestors_out)
Look up the base-ancestor set of a tracked relation.
bool provsql_lookup_table_info(Oid relid, ProvenanceTableInfo *out)
Look up per-table provenance metadata with a backend-local cache.
Core types, constants, and utilities shared across ProvSQL.
Result of provsql_classify_query.
Per-relation metadata for the safe-query optimisation.
AttrNumber block_key[PROVSQL_TABLE_INFO_MAX_BLOCK_KEY]
Block-key column numbers.
uint16_t block_key_n
Number of valid entries in block_key.
uint8_t kind
One of provsql_table_kind.