25#include "access/htup_details.h"
26#include "access/sysattr.h"
27#include "catalog/pg_aggregate.h"
28#include "catalog/pg_class.h"
29#include "catalog/pg_collation.h"
30#include "catalog/pg_operator.h"
31#include "catalog/pg_proc.h"
32#include "catalog/pg_type.h"
33#include "nodes/makefuncs.h"
34#include "nodes/nodeFuncs.h"
35#include "nodes/print.h"
36#include "executor/executor.h"
37#include "optimizer/planner.h"
38#include "parser/parse_coerce.h"
39#include "parser/parse_oper.h"
40#include "utils/builtins.h"
41#include "parser/parsetree.h"
42#include "storage/lwlock.h"
43#include "storage/shmem.h"
44#include "utils/fmgroids.h"
46#include "utils/lsyscache.h"
47#include "utils/ruleutils.h"
48#include "utils/syscache.h"
49#include "catalog/namespace.h"
50#include "catalog/pg_cast.h"
51#include "commands/createas.h"
52#include "executor/spi.h"
53#include "tcop/utility.h"
62#if PG_VERSION_NUM < 100000
63#error "ProvSQL requires PostgreSQL version 10 or later"
95 bool **removed,
bool wrap_root);
117 RangeTblEntry *r, Index relid,
119 Var *v = makeNode(Var);
124#if PG_VERSION_NUM >= 130000
126 v->varattnosyn = attid;
129 v->varoattno = attid;
133 v->varcollid = InvalidOid;
137#if PG_VERSION_NUM >= 160000
138 if (r->perminfoindex != 0) {
139 RTEPermissionInfo *rpi =
140 list_nth_node(RTEPermissionInfo, q->rteperminfos, r->perminfoindex - 1);
141 rpi->selectedCols = bms_add_member(
142 rpi->selectedCols, attid - FirstLowInvalidHeapAttributeNumber);
145 r->selectedCols = bms_add_member(r->selectedCols,
146 attid - FirstLowInvalidHeapAttributeNumber);
173 if (IsA(node, Var)) {
174 Var *v = (Var *)node;
176 if (v->varno == context->
varno) {
177 v->varattno += context->
offset[v->varattno - 1];
201 foreach (lc, targetList) {
202 Node *te = lfirst(lc);
219 if (IsA(node, Var)) {
220 Var *v = (Var *)node;
221 return v->varno == context->
varno && v->varattno == context->
varattno;
247 if (IsA(node, FuncExpr)) {
248 FuncExpr *f = (FuncExpr *)node;
251 if (list_length(f->args) == 1 &&
254 HeapTuple castTuple = SearchSysCache2(CASTSOURCETARGET,
256 ObjectIdGetDatum(f->funcresulttype));
258 if (HeapTupleIsValid(castTuple)) {
259 Form_pg_cast castForm = (Form_pg_cast) GETSTRUCT(castTuple);
260 if (OidIsValid(castForm->castfunc)) {
261 f->funcid = castForm->castfunc;
263 ReleaseSysCache(castTuple);
267 ((Var *)linitial(f->args))->vartype =
274 if (IsA(node, Var)) {
275 Var *v = (Var *)node;
277 if (v->varno == context->
varno && v->varattno == context->
varattno) {
298 Query *q, Index rteid,
304 foreach (lc, targetList) {
305 TargetEntry *te = (TargetEntry *)lfirst(lc);
306 if (IsA(te->expr, FuncExpr)) {
307 FuncExpr *f = (FuncExpr *)te->expr;
310 context.
varno = rteid;
313 QTW_DONT_COPY_QUERY | QTW_IGNORE_RC_SUBQUERIES);
318 foreach (lc2, q->targetList) {
319 TargetEntry *outer_te = (TargetEntry *)lfirst(lc2);
320 if (IsA(outer_te->expr, Var)) {
321 Var *v = (Var *)outer_te->expr;
322 if (v->varno == rteid && v->varattno == attno &&
323 outer_te->ressortgroupref > 0) {
325 foreach (lc3, q->sortClause) {
326 SortGroupClause *sgc = (SortGroupClause *)lfirst(lc3);
327 if (sgc->tleSortGroupRef == outer_te->ressortgroupref)
329 "a subquery not supported");
331 foreach (lc3, q->groupClause) {
332 SortGroupClause *sgc = (SortGroupClause *)lfirst(lc3);
333 if (sgc->tleSortGroupRef == outer_te->ressortgroupref)
335 "a subquery not supported");
360 foreach (lc, rtable) {
361 RangeTblEntry *r = (RangeTblEntry *)lfirst(lc);
362 if (r->rtekind == RTE_CTE) {
364 foreach (lc2, cteList) {
365 CommonTableExpr *cte = (CommonTableExpr *)lfirst(lc2);
366 if (strcmp(cte->ctename, r->ctename) == 0) {
367 if (cte->cterecursive) {
370 r->rtekind = RTE_SUBQUERY;
371 r->subquery = copyObject((Query *)cte->ctequery);
381 }
else if (r->rtekind == RTE_SUBQUERY && r->subquery != NULL) {
393 if (q->cteList == NIL)
421 List *prov_atts = NIL;
423 for(Index rteid = 1; rteid <= q->rtable->length; ++rteid) {
424 RangeTblEntry *r = list_nth_node(RangeTblEntry, q->rtable, rteid-1);
426 if (r->rtekind == RTE_RELATION) {
428 AttrNumber attid = 1;
437 if (r->relkind == RELKIND_VIEW)
440 foreach (lc, r->eref->colnames) {
441 const char *v = strVal(lfirst(lc));
452 }
else if (r->rtekind == RTE_SUBQUERY) {
453 bool *inner_removed = NULL;
454 int old_targetlist_length =
455 r->subquery->targetList ? r->subquery->targetList->length : 0;
456 Query *new_subquery =
457 process_query(constants, r->subquery, &inner_removed,
false);
458 if (new_subquery != NULL) {
460 int *offset = (
int *)palloc(old_targetlist_length *
sizeof(
int));
461 unsigned varattnoprovsql;
462 ListCell *cell, *prev;
464 r->subquery = new_subquery;
466 if (inner_removed != NULL) {
467 for (cell = list_head(r->eref->colnames), prev = NULL;
469 if (inner_removed[i]) {
473 cell =
my_lnext(r->eref->colnames, prev);
475 cell = list_head(r->eref->colnames);
478 cell =
my_lnext(r->eref->colnames, cell);
482 for (i = 0; i < old_targetlist_length; ++i) {
484 (i == 0 ? 0 : offset[i - 1]) - (inner_removed[i] ? 1 : 0);
491 for (cell = list_head(new_subquery->targetList); cell != NULL;
492 cell =
my_lnext(new_subquery->targetList, cell)) {
493 TargetEntry *te = (TargetEntry *)lfirst(cell);
500 r->eref->colnames =
list_insert_nth(r->eref->colnames, varattnoprovsql-1,
504 constants, q, r, rteid, varattnoprovsql));
507 r->subquery->targetList);
509 }
else if (r->rtekind == RTE_JOIN) {
510 if (r->jointype == JOIN_INNER || r->jointype == JOIN_LEFT ||
511 r->jointype == JOIN_FULL || r->jointype == JOIN_RIGHT) {
520 }
else if (r->rtekind == RTE_FUNCTION) {
522 AttrNumber attid = 1;
524 foreach (lc, r->functions) {
525 RangeTblFunction *func = (RangeTblFunction *)lfirst(lc);
527 if (func->funccolcount == 1) {
528 FuncExpr *expr = (FuncExpr *)func->funcexpr;
532 constants, q, r, rteid, attid));
536 "attributes not supported");
539 attid += func->funccolcount;
541 }
else if (r->rtekind == RTE_VALUES) {
543#if PG_VERSION_NUM >= 180000
544 }
else if (r->rtekind == RTE_GROUP) {
581 Bitmapset *ressortgrouprefs = NULL;
582 ListCell *cell, *prev;
583 *removed = (
bool *)palloc(q->targetList->length *
sizeof(
bool));
585 for (cell = list_head(q->targetList), prev = NULL; cell != NULL;) {
586 TargetEntry *rt = (TargetEntry *)lfirst(cell);
587 (*removed)[i] =
false;
589 if (rt->expr->type == T_Var) {
590 Var *v = (Var *)rt->expr;
596 colname = rt->resname;
600 RangeTblEntry *r = (RangeTblEntry *)list_nth(q->rtable, v->varno - 1);
601 colname = strVal(list_nth(r->eref->colnames, v->varattno - 1));
607 (*removed)[i] =
true;
610 if (rt->ressortgroupref > 0)
612 bms_add_member(ressortgrouprefs, rt->ressortgroupref);
619 cell =
my_lnext(q->targetList, prev);
621 cell = list_head(q->targetList);
624 rt->resno -= nbRemoved;
626 cell =
my_lnext(q->targetList, cell);
632 return ressortgrouprefs;
680 OpExpr *fromOpExpr, Expr *toExpr,
690 if (
my_lnext(fromOpExpr->args, list_head(fromOpExpr->args))) {
692 if (IsA(linitial(fromOpExpr->args), Var)) {
693 v1 = linitial(fromOpExpr->args);
694 }
else if (IsA(linitial(fromOpExpr->args), RelabelType)) {
696 RelabelType *rt1 = linitial(fromOpExpr->args);
697 if (IsA(rt1->arg, Var)) {
698 v1 = (Var *)rt1->arg;
703 if (!columns[v1->varno - 1])
705 first_arg = Int16GetDatum(columns[v1->varno - 1][v1->varattno - 1]);
707 if (IsA(lsecond(fromOpExpr->args), Var)) {
708 v2 = lsecond(fromOpExpr->args);
709 }
else if (IsA(lsecond(fromOpExpr->args), RelabelType)) {
711 RelabelType *rt2 = lsecond(fromOpExpr->args);
712 if (IsA(rt2->arg, Var)) {
713 v2 = (Var *)rt2->arg;
718 if (!columns[v2->varno - 1])
720 second_arg = Int16GetDatum(columns[v2->varno - 1][v2->varattno - 1]);
722 fc = makeNode(FuncExpr);
724 fc->funcvariadic =
false;
728 c1 = makeConst(constants->
OID_TYPE_INT, -1, InvalidOid,
sizeof(int16),
729 first_arg,
false,
true);
731 c2 = makeConst(constants->
OID_TYPE_INT, -1, InvalidOid,
sizeof(int16),
732 second_arg,
false,
true);
734 fc->args = list_make3(toExpr, c1, c2);
756 Node *quals, Expr *result,
763 if (IsA(quals, OpExpr)) {
764 oe = (OpExpr *)quals;
767 else if (IsA(quals, BoolExpr)) {
768 BoolExpr *be = (BoolExpr *)quals;
770 if (be->boolop == OR_EXPR || be->boolop == NOT_EXPR) {
772 "clause are not supported");
775 foreach (lc2, be->args) {
776 if (IsA(lfirst(lc2), OpExpr)) {
777 oe = (OpExpr *)lfirst(lc2);
802 if (
my_lnext(prov_atts, list_head(prov_atts)) == NULL)
803 return (Expr *)linitial(prov_atts);
805 combine = makeNode(FuncExpr);
807 ArrayExpr *array = makeNode(ArrayExpr);
810 combine->funcvariadic =
true;
814 array->elements = prov_atts;
815 array->location = -1;
817 combine->args = list_make1(array);
820 combine->args = prov_atts;
823 combine->location = -1;
824 return (Expr *)combine;
856 Aggref *agg_ref, List *prov_atts,
859 Expr *rv_arg = ((TargetEntry *)linitial(agg_ref->args))->expr;
869 wrap = makeNode(FuncExpr);
872 wrap->args = list_make2(prov_expr, rv_arg);
878 te = makeNode(TargetEntry);
880 te->expr = (Expr *)wrap;
882 new_agg = makeNode(Aggref);
883 new_agg->aggfnoid = agg_ref->aggfnoid;
886 new_agg->aggkind = AGGKIND_NORMAL;
887 new_agg->aggtranstype = InvalidOid;
888 new_agg->args = list_make1(te);
889 new_agg->location = agg_ref->location;
890#if PG_VERSION_NUM >= 140000
891 new_agg->aggno = new_agg->aggtransno = -1;
894 return (Expr *)new_agg;
917 Aggref *agg_ref, List *prov_atts,
920 FuncExpr *expr, *expr_s;
921 Aggref *agg = makeNode(Aggref);
922 FuncExpr *plus = makeNode(FuncExpr);
923 TargetEntry *te_inner = makeNode(TargetEntry);
924 Const *fn = makeNode(Const);
925 Const *typ = makeNode(Const);
928 result = linitial(prov_atts);
930 Oid aggregation_function = agg_ref->aggfnoid;
944 if (
my_lnext(prov_atts, list_head(prov_atts)) == NULL)
945 expr = linitial(prov_atts);
947 expr = makeNode(FuncExpr);
949 ArrayExpr *array = makeNode(ArrayExpr);
952 expr->funcvariadic =
true;
956 array->elements = prov_atts;
957 array->location = -1;
959 expr->args = list_make1(array);
962 expr->args = prov_atts;
969 expr_s = makeNode(FuncExpr);
974 if (aggregation_function ==
F_COUNT_ ||
977 Const *one = makeConst(constants->
OID_TYPE_INT, -1, InvalidOid,
978 sizeof(int32), Int32GetDatum(1),
false,
true);
979 expr_s->args = list_make2(one, expr);
983 list_make2(((TargetEntry *)linitial(agg_ref->args))->expr, expr);
986 expr_s->location = -1;
990 te_inner->expr = (Expr *)expr_s;
993 agg->args = list_make1(te_inner);
994 agg->aggkind = AGGKIND_NORMAL;
996#if PG_VERSION_NUM >= 140000
997 agg->aggno = agg->aggtransno = -1;
1000 agg->aggargtypes = list_make1_oid(constants->
OID_TYPE_UUID);
1005 fn = makeConst(constants->
OID_TYPE_INT, -1, InvalidOid,
sizeof(int32),
1006 Int32GetDatum(aggregation_function),
false,
true);
1008 typ = makeConst(constants->
OID_TYPE_INT, -1, InvalidOid,
sizeof(int32),
1009 Int32GetDatum(agg_ref->aggtype),
false,
true);
1012 plus->args = list_make4(fn, typ, agg_ref, agg);
1013 plus->location = -1;
1015 result = (Expr *)plus;
1054 Oid opno = opExpr->opno;
1056 for (
unsigned i = 0; i < 2; ++i) {
1057 Node *node = (Node *)lfirst(list_nth_cell(opExpr->args, i));
1059 if (IsA(node, FuncExpr)) {
1060 FuncExpr *fe = (FuncExpr *)node;
1061 if (fe->funcformat == COERCE_IMPLICIT_CAST ||
1062 fe->funcformat == COERCE_EXPLICIT_CAST) {
1063 if (fe->args->length == 1)
1064 node = lfirst(list_head(fe->args));
1068 if (IsA(node, FuncExpr)) {
1069 FuncExpr *fe = (FuncExpr *)node;
1072 FuncExpr *castToUUID = makeNode(FuncExpr);
1076 castToUUID->args = list_make1(fe);
1077 castToUUID->location = -1;
1079 arguments[i] = (Node *)castToUUID;
1083 }
else if (IsA(node, Var)) {
1084 Var *v = (Var *)node;
1088 FuncExpr *castToUUID = makeNode(FuncExpr);
1092 castToUUID->args = list_make1(v);
1093 castToUUID->location = -1;
1095 arguments[i] = (Node *)castToUUID;
1099 }
else if (IsA(node, Const)) {
1100 Const *literal = (Const *)node;
1101 FuncExpr *oneExpr, *semimodExpr;
1104 oneExpr = makeNode(FuncExpr);
1107 oneExpr->args = NIL;
1108 oneExpr->location = -1;
1111 semimodExpr = makeNode(FuncExpr);
1114 semimodExpr->args = list_make2((Expr *)literal, (Expr *)oneExpr);
1115 semimodExpr->location = -1;
1117 arguments[i] = (Node *)semimodExpr;
1124 opno = get_negator(opno);
1129 oid = makeConst(constants->
OID_TYPE_INT, -1, InvalidOid,
sizeof(int32),
1130 Int32GetDatum(opno),
false,
true);
1132 cmpExpr = makeNode(FuncExpr);
1135 cmpExpr->args = list_make3(arguments[0], oid, arguments[1]);
1136 cmpExpr->location = opExpr->location;
1155 if(be->boolop == NOT_EXPR) {
1156 Expr *expr = (Expr *) lfirst(list_head(be->args));
1162 ArrayExpr *array = makeNode(ArrayExpr);
1166 array->location = -1;
1168 result = makeNode(FuncExpr);
1170 result->funcvariadic =
true;
1171 result->location = be->location;
1172 result->args = list_make1(array);
1174 if ((be->boolop == AND_EXPR && !negated) || (be->boolop == OR_EXPR && negated))
1176 else if ((be->boolop == AND_EXPR && negated) || (be->boolop == OR_EXPR && !negated))
1181 foreach (lc, be->args) {
1182 Expr *expr = (Expr *)lfirst(lc);
1184 l = lappend(l, arg);
1187 array->elements = l;
1206 if (IsA(expr, BoolExpr))
1208 else if (IsA(expr, OpExpr))
1211 provsql_error(
"Unknown structure within Boolean expression");
1244 for (
int i = 0; i < 6; ++i) {
1265 RelabelType *rt = makeNode(RelabelType);
1266 rt->arg = (Expr *) operand;
1268 rt->resulttypmod = -1;
1269 rt->resultcollid = InvalidOid;
1270 rt->relabelformat = COERCE_IMPLICIT_CAST;
1302 Oid opno = opExpr->opno;
1303 Node *left = (Node *)linitial(opExpr->args);
1304 Node *right = (Node *)lsecond(opExpr->args);
1307 opno = get_negator(opno);
1309 provsql_error(
"Missing negator for random_variable comparison");
1312 oid_const = makeConst(constants->
OID_TYPE_INT, -1, InvalidOid,
1313 sizeof(int32), Int32GetDatum(opno),
false,
true);
1315 cmpExpr = makeNode(FuncExpr);
1318 cmpExpr->args = list_make3(
1322 cmpExpr->location = opExpr->location;
1344 if (be->boolop == NOT_EXPR) {
1345 Expr *child = (Expr *)linitial(be->args);
1349 array = makeNode(ArrayExpr);
1352 array->location = -1;
1354 result = makeNode(FuncExpr);
1356 result->funcvariadic =
true;
1357 result->location = be->location;
1358 result->args = list_make1(array);
1360 if ((be->boolop == AND_EXPR && !negated) ||
1361 (be->boolop == OR_EXPR && negated))
1363 else if ((be->boolop == AND_EXPR && negated) ||
1364 (be->boolop == OR_EXPR && !negated))
1367 provsql_error(
"Unknown Boolean operator in random_variable WHERE clause");
1369 foreach (lc, be->args) {
1371 constants, negated);
1372 l = lappend(l, arg);
1374 array->elements = l;
1388 if (IsA(expr, BoolExpr))
1390 if (IsA(expr, OpExpr)) {
1391 OpExpr *opExpr = (OpExpr *)expr;
1395 provsql_error(
"Unsupported sub-expression in random_variable WHERE clause "
1396 "(only Boolean combinations of RV comparisons are accepted)");
1413 if (IsA(node, OpExpr)) {
1414 OpExpr *opExpr = (OpExpr *)node;
1418 if (IsA(node, BoolExpr)) {
1419 BoolExpr *be = (BoolExpr *)node;
1421 foreach (lc, be->args) {
1450 if (IsA(expr, OpExpr))
1451 return rv_cmp_index(constants, ((OpExpr *)expr)->opfuncid) >= 0;
1452 if (IsA(expr, BoolExpr)) {
1453 BoolExpr *be = (BoolExpr *)expr;
1455 foreach (lc, be->args) {
1511 List *prov_atts,
bool aggregation,
1512 bool group_by_rewrite,
1514 int nbcols,
bool wrap_assumed_boolean) {
1519 result = linitial(prov_atts);
1521 if (
my_lnext(prov_atts, list_head(prov_atts)) == NULL) {
1522 result = linitial(prov_atts);
1524 FuncExpr *expr = makeNode(FuncExpr);
1526 ArrayExpr *array = makeNode(ArrayExpr);
1529 expr->funcvariadic =
true;
1533 array->elements = prov_atts;
1534 array->location = -1;
1536 expr->args = list_make1(array);
1539 expr->args = prov_atts;
1542 expr->location = -1;
1544 result = (Expr *)expr;
1547 if (group_by_rewrite || aggregation) {
1548 Aggref *agg = makeNode(Aggref);
1549 FuncExpr *plus = makeNode(FuncExpr);
1550 TargetEntry *te_inner = makeNode(TargetEntry);
1554 te_inner->resno = 1;
1555 te_inner->expr = (Expr *)result;
1559 agg->args = list_make1(te_inner);
1560 agg->aggkind = AGGKIND_NORMAL;
1562#if PG_VERSION_NUM >= 140000
1563 agg->aggno = agg->aggtransno = -1;
1566 agg->aggargtypes = list_make1_oid(constants->
OID_TYPE_UUID);
1569 plus->args = list_make1(agg);
1571 plus->location = -1;
1573 result = (Expr *)plus;
1586 bool lift_having = q->havingQual != NULL &&
1589 if (aggregation && !lift_having) {
1590 FuncExpr *deltaExpr = makeNode(FuncExpr);
1594 deltaExpr->args = list_make1(result);
1596 deltaExpr->location = -1;
1598 result = (Expr *)deltaExpr;
1603 q->havingQual = NULL;
1613 foreach (lc, q->jointree->fromlist) {
1614 if (IsA(lfirst(lc), JoinExpr)) {
1615 JoinExpr *je = (JoinExpr *)lfirst(lc);
1627 ArrayExpr *array = makeNode(ArrayExpr);
1628 FuncExpr *fe = makeNode(FuncExpr);
1629 bool projection =
false;
1643 int *prov_offset = (
int *)palloc0((q->rtable->length + 1) *
sizeof(
int));
1648 fe->funcvariadic =
true;
1654 array->elements = NIL;
1655 array->location = -1;
1657 for (r = 1; r <= (Index)q->rtable->length; ++r) {
1658 prov_offset[r] = cum;
1660 RangeTblEntry *rte_r = (RangeTblEntry *)list_nth(q->rtable, r-1);
1661 int ncols = list_length(rte_r->eref->colnames);
1662 bool is_prov =
false;
1665 for (k = 0; k < ncols; ++k) {
1666 if (columns[r-1][k] == -1) is_prov =
true;
1667 else if (columns[r-1][k] > 0) nb_user++;
1669 if (is_prov) cum += nb_user;
1673 foreach (lc_v, q->targetList) {
1674 TargetEntry *te_v = (TargetEntry *)lfirst(lc_v);
1675 if (IsA(te_v->expr, Var)) {
1676 Var *vte_v = (Var *)te_v->expr;
1677 RangeTblEntry *rte_v =
1678 (RangeTblEntry *)lfirst(list_nth_cell(q->rtable, vte_v->varno - 1));
1680#if PG_VERSION_NUM >= 180000
1681 if (rte_v->rtekind == RTE_GROUP) {
1682 Expr *ge = lfirst(list_nth_cell(rte_v->groupexprs, vte_v->varattno - 1));
1684 Var *v = (Var *) ge;
1685 value_v = columns[v->varno - 1] ?
1686 columns[v->varno - 1][v->varattno - 1] : 0;
1688 Const *ce = makeConst(constants->
OID_TYPE_INT, -1, InvalidOid,
1689 sizeof(int32), Int32GetDatum(0),
false,
true);
1691 array->elements = lappend(array->elements, ce);
1696 if (rte_v->rtekind != RTE_JOIN) {
1697 if (rte_v->rtekind == RTE_RELATION && columns[vte_v->varno - 1]) {
1701 bool is_prov =
false;
1702 int ncols_rte = list_length(rte_v->eref->colnames);
1703 for (
int k = 0; k < ncols_rte; k++) {
1704 if (columns[vte_v->varno - 1][k] == -1) {
1710 int raw = columns[vte_v->varno - 1][vte_v->varattno - 1];
1718 value_v = (raw == -1) ? -1
1719 : (int)vte_v->varattno
1720 + prov_offset[vte_v->varno];
1729 sizeof(int32), Int32GetDatum(0),
false,
true);
1730 array->elements = lappend(array->elements, ce);
1738 value_v = columns[vte_v->varno - 1] ?
1739 columns[vte_v->varno - 1][vte_v->varattno - 1] : 0;
1742 Var *jav_v = (Var *)lfirst(
1743 list_nth_cell(rte_v->joinaliasvars, vte_v->varattno - 1));
1744 if (jav_v && IsA(jav_v, Var) && columns[jav_v->varno - 1]) {
1745 RangeTblEntry *jrte_v = (RangeTblEntry *)lfirst(
1746 list_nth_cell(q->rtable, jav_v->varno - 1));
1747 if (jrte_v->rtekind == RTE_RELATION) {
1750 bool is_prov =
false;
1751 int ncols_jrte = list_length(jrte_v->eref->colnames);
1752 for (
int k = 0; k < ncols_jrte; k++) {
1753 if (columns[jav_v->varno - 1][k] == -1) {
1759 int raw = columns[jav_v->varno - 1][jav_v->varattno - 1];
1760 value_v = (raw == -1) ? -1
1761 : (int)jav_v->varattno
1762 + prov_offset[jav_v->varno];
1766 sizeof(int32), Int32GetDatum(0),
false,
true);
1767 array->elements = lappend(array->elements, ce);
1772 value_v = columns[jav_v->varno - 1][jav_v->varattno - 1];
1782 makeConst(constants->
OID_TYPE_INT, -1, InvalidOid,
sizeof(int32),
1783 Int32GetDatum(value_v),
false,
true);
1785 array->elements = lappend(array->elements, ce);
1787 if (value_v != ++nb_column)
1794 Const *ce = makeConst(constants->
OID_TYPE_INT, -1, InvalidOid,
1795 sizeof(int32), Int32GetDatum(0),
false,
true);
1797 array->elements = lappend(array->elements, ce);
1802 if (nb_column != nbcols)
1806 fe->args = list_make2(result, array);
1807 result = (Expr *)fe;
1825 if (wrap_assumed_boolean &&
1836#if PG_VERSION_NUM >= 180000
1838 Index group_rtindex;
1840} resolve_group_rte_ctx;
1843resolve_group_rte_vars_mutator(Node *node,
void *raw_ctx) {
1844 resolve_group_rte_ctx *ctx = (resolve_group_rte_ctx *)raw_ctx;
1847 if (IsA(node, Var)) {
1848 Var *v = (Var *)node;
1849 if (v->varno == ctx->group_rtindex) {
1850 Node *resolved = copyObject(list_nth(ctx->groupexprs, v->varattno - 1));
1856 if (IsA(resolved, Var))
1857 ((Var *)resolved)->varnullingrels = NULL;
1861 return expression_tree_mutator(node, resolve_group_rte_vars_mutator, raw_ctx);
1879 resolve_group_rte_ctx grp_ctx;
1885 if (!q->hasGroupRTE)
1888 foreach (lc, q->rtable) {
1889 RangeTblEntry *r = (RangeTblEntry *) lfirst(lc);
1890 if (r->rtekind == RTE_GROUP) {
1891 grp_ctx.group_rtindex = idx;
1892 grp_ctx.groupexprs = r->groupexprs;
1903 q->rtable = list_truncate(q->rtable, rte_len);
1904 q->hasGroupRTE =
false;
1906 foreach (lc, q->targetList) {
1907 TargetEntry *te = (TargetEntry *) lfirst(lc);
1908 te->expr = (Expr *) resolve_group_rte_vars_mutator(
1909 (Node *) te->expr, &grp_ctx);
1911 if (q->jointree && q->jointree->quals)
1912 q->jointree->quals = resolve_group_rte_vars_mutator(
1913 q->jointree->quals, &grp_ctx);
1936 List *groupby_tes) {
1941 int resno = 1, sgref = 1;
1943 inner = copyObject(q);
1945 inner->hasAggs =
false;
1946 inner->sortClause = NIL;
1947 inner->limitCount = NULL;
1948 inner->limitOffset = NULL;
1949 inner->distinctClause = NIL;
1950 inner->hasDistinctOn =
false;
1951 inner->havingQual = NULL;
1955 TargetEntry *kte = makeNode(TargetEntry);
1956 SortGroupClause *sgc = makeNode(SortGroupClause);
1958 kte->expr = copyObject(key_expr);
1959 kte->resno = resno++;
1960 kte->resname =
"key";
1961 sgc->tleSortGroupRef = kte->ressortgroupref = sgref++;
1962 get_sort_group_operators(exprType((Node *)kte->expr),
true,
true,
false,
1963 &sgc->sortop, &sgc->eqop, NULL, &sgc->hashable);
1964 new_gc = list_make1(sgc);
1965 new_tl = list_make1(kte);
1969 foreach (lc, groupby_tes) {
1970 TargetEntry *gyte = copyObject((TargetEntry *)lfirst(lc));
1971 SortGroupClause *sgc = makeNode(SortGroupClause);
1973 gyte->resno = resno++;
1974 gyte->resjunk =
false;
1975 sgc->tleSortGroupRef = gyte->ressortgroupref = sgref++;
1976 get_sort_group_operators(exprType((Node *)gyte->expr),
true,
true,
false,
1977 &sgc->sortop, &sgc->eqop, NULL, &sgc->hashable);
1978 new_gc = lappend(new_gc, sgc);
1979 new_tl = lappend(new_tl, gyte);
1982 inner->targetList = new_tl;
1983 inner->groupClause = new_gc;
2006 Query *inner,
int n_gb,
2008 Query *outer = makeNode(Query);
2009 RangeTblEntry *rte = makeNode(RangeTblEntry);
2010 Alias *alias = makeNode(Alias), *eref = makeNode(Alias);
2011 RangeTblRef *rtr = makeNode(RangeTblRef);
2012 FromExpr *jt = makeNode(FromExpr);
2013 List *new_tl = NIL, *new_gc = NIL;
2015 int resno = 1, sgref = 1;
2016 int inner_len = list_length(inner->targetList);
2020 alias->aliasname = eref->aliasname =
"d";
2021 eref->colnames = NIL;
2022 foreach (lc, inner->targetList) {
2023 TargetEntry *te = lfirst(lc);
2024 eref->colnames = lappend(eref->colnames,
2025 makeString(te->resname ? pstrdup(te->resname) :
""));
2029 rte->rtekind = RTE_SUBQUERY;
2030 rte->subquery = inner;
2031 rte->inFromCl =
true;
2032#if PG_VERSION_NUM < 160000
2033 rte->requiredPerms = ACL_SELECT;
2037 jt->fromlist = list_make1(rtr);
2039 outer->commandType = CMD_SELECT;
2040 outer->canSetTag =
true;
2041 outer->rtable = list_make1(rte);
2042 outer->jointree = jt;
2043 outer->hasAggs =
true;
2047 TargetEntry *agg_te = copyObject(orig_agg_te);
2048 Aggref *ar = (Aggref *)agg_te->expr;
2049 Var *key_var = makeNode(Var);
2050 TargetEntry *arg_te = makeNode(TargetEntry);
2053 key_var->varattno = 1;
2054 key_var->vartype = linitial_oid(ar->aggargtypes);
2055 key_var->varcollid = exprCollation((Node *)((TargetEntry *)linitial(ar->args))->expr);
2056 key_var->vartypmod = -1;
2057 key_var->location = -1;
2059 arg_te->expr = (Expr *)key_var;
2061 ar->args = list_make1(arg_te);
2062 ar->aggdistinct = NIL;
2063 agg_te->resno = resno++;
2064 new_tl = list_make1(agg_te);
2068 for (attno = inner_len - n_gb + 1; attno <= inner_len; attno++) {
2069 TargetEntry *inner_te = list_nth(inner->targetList, attno - 1);
2070 Var *gb_var = makeNode(Var);
2071 TargetEntry *gb_te = makeNode(TargetEntry);
2072 SortGroupClause *sgc = makeNode(SortGroupClause);
2075 gb_var->varattno = attno;
2076 gb_var->vartype = exprType((Node *)inner_te->expr);
2077 gb_var->varcollid = exprCollation((Node *)inner_te->expr);
2078 gb_var->vartypmod = -1;
2079 gb_var->location = -1;
2081 gb_te->resno = resno++;
2082 gb_te->expr = (Expr *)gb_var;
2083 gb_te->resname = inner_te->resname;
2085 sgc->tleSortGroupRef = gb_te->ressortgroupref = sgref++;
2086 sgc->nulls_first =
false;
2087 get_sort_group_operators(gb_var->vartype,
true,
true,
false,
2088 &sgc->sortop, &sgc->eqop, NULL, &sgc->hashable);
2089 new_gc = lappend(new_gc, sgc);
2090 new_tl = lappend(new_tl, gb_te);
2093 outer->targetList = new_tl;
2094 outer->groupClause = new_gc;
2114 List *distinct_agg_tes = NIL;
2115 List *groupby_tes = NIL;
2118#if PG_VERSION_NUM >= 180000
2138 foreach (lc, q->targetList) {
2139 TargetEntry *te = lfirst(lc);
2140 if (IsA(te->expr, Aggref)) {
2141 Aggref *ar = (Aggref *)te->expr;
2142 if (list_length(ar->aggdistinct) > 0)
2143 distinct_agg_tes = lappend(distinct_agg_tes, te);
2145 (
void *)constants)) {
2150 TargetEntry *te_copy = copyObject(te);
2151 te_copy->resjunk =
false;
2152 groupby_tes = lappend(groupby_tes, te_copy);
2156 if (distinct_agg_tes == NIL)
2160 int n_aggs = list_length(distinct_agg_tes);
2161 int n_gb = list_length(groupby_tes);
2162 List *outer_queries = NIL;
2179 foreach (lc, distinct_agg_tes) {
2180 TargetEntry *agg_te = lfirst(lc);
2181 Aggref *ar = (Aggref *)agg_te->expr;
2182 if(list_length(ar->args) != 1)
2183 provsql_error(
"AGG(DISTINCT) with more than one argument is not supported");
2185 Expr *key_expr = (Expr *)((TargetEntry *)linitial(ar->args))->expr;
2188 outer_queries = lappend(outer_queries, outer);
2195 foreach (lc, outer_queries) {
2196 Query *oq = lfirst(lc);
2197 RangeTblEntry *rte = makeNode(RangeTblEntry);
2198 Alias *alias = makeNode(Alias), *eref = makeNode(Alias);
2202 snprintf(buf,
sizeof(buf),
"d%d", i + 1);
2203 alias->aliasname = eref->aliasname = pstrdup(buf);
2204 eref->colnames = NIL;
2205 foreach (lc2, oq->targetList) {
2206 TargetEntry *te = lfirst(lc2);
2207 eref->colnames = lappend(eref->colnames,
2208 makeString(te->resname ? pstrdup(te->resname) :
""));
2212 rte->rtekind = RTE_SUBQUERY;
2214 rte->inFromCl =
true;
2215#if PG_VERSION_NUM < 160000
2216 rte->requiredPerms = ACL_SELECT;
2218 q->rtable = lappend(q->rtable, rte);
2225 FromExpr *jt = q->jointree;
2226 List *from_list = jt->fromlist;
2227 unsigned fll = list_length(from_list);
2228 List *where_args = NIL;
2230 for (i = fll+1; i <= fll+n_aggs; i++) {
2231 RangeTblRef *rtr = makeNode(RangeTblRef);
2236 from_list = lappend(from_list, rtr);
2239 foreach(lc2, groupby_tes) {
2240 TargetEntry *gb_te = lfirst(lc2);
2241 int gb_attno = ++j + 1;
2242 Oid ytype = exprType((Node *)gb_te->expr);
2244 Operator opInfo = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno));
2245 Form_pg_operator opform;
2246 OpExpr *oe = makeNode(OpExpr);
2247 Expr *le = copyObject(gb_te->expr);
2248 Var *rv = makeNode(Var);
2249 Oid collation=exprCollation((Node*) le);
2251 if (!HeapTupleIsValid(opInfo))
2252 provsql_error(
"could not find equality operator for type %u",
2254 opform = (Form_pg_operator)GETSTRUCT(opInfo);
2257 oe->opfuncid = opform->oprcode;
2258 oe->opresulttype = opform->oprresult;
2259 oe->opcollid = InvalidOid;
2260 oe->inputcollid = collation;
2262 ReleaseSysCache(opInfo);
2264 rv->varno = i; rv->varattno = gb_attno;
2265 rv->vartype = ytype; rv->varcollid = collation;
2266 rv->vartypmod = -1; rv->location = -1;
2268 oe->args = list_make2(le, rv);
2269 where_args = lappend(where_args, oe);
2273 if (list_length(where_args) == 0) {
2275 }
else if (list_length(where_args) == 1) {
2276 jt->quals = linitial(where_args);
2278 BoolExpr *be = makeNode(BoolExpr);
2279 be->boolop = AND_EXPR;
2280 be->args = where_args;
2282 jt->quals = (Node *)be;
2289 int agg_idx = list_length(q->jointree->fromlist) - n_aggs + 1;
2292 foreach (lc2, q->targetList) {
2293 TargetEntry *te = lfirst(lc2);
2295 if (IsA(te->expr, Aggref) &&
2296 ((Aggref *)te->expr)->aggdistinct != NIL) {
2297 Var *v = makeNode(Var);
2298 v->varno = agg_idx++;
2302 te->expr = (Expr*)v;
2336 if (IsA(node, Aggref)) {
2337 Aggref *ar_v = (Aggref *)node;
2355 Const *typ_const = (Const *)lsecond(prov_agg->args);
2356 Oid target_type = DatumGetObjectId(typ_const->constvalue);
2357 CoercionPathType pathtype;
2360 pathtype = find_coercion_pathway(target_type,
2362 COERCION_EXPLICIT, &castfuncid);
2363 if (pathtype == COERCION_PATH_FUNC && OidIsValid(castfuncid)) {
2364 FuncExpr *cast = makeNode(FuncExpr);
2365 cast->funcid = castfuncid;
2366 cast->funcresulttype = target_type;
2367 cast->funcretset =
false;
2368 cast->funcvariadic =
false;
2369 cast->funcformat = COERCE_IMPLICIT_CAST;
2370 cast->args = list_make1(prov_agg);
2371 cast->location = -1;
2372 return (Node *)cast;
2375 provsql_error(
"no cast from agg_token to %s for arithmetic on aggregate",
2376 format_type_be(target_type));
2377 return (Node *)prov_agg;
2397 Form_pg_proc procForm;
2401 tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(parent_funcid));
2402 if (!HeapTupleIsValid(tp))
2404 procForm = (Form_pg_proc) GETSTRUCT(tp);
2408 Node *arg = lfirst(lc);
2410 if (i < procForm->pronargs && IsA(arg, FuncExpr) &&
2412 Oid formal_type = procForm->proargtypes.values[i];
2415 !IsPolymorphicType(formal_type)) {
2422 ReleaseSysCache(tp);
2450 if (IsA(result, OpExpr)) {
2451 OpExpr *op = (OpExpr *)result;
2454 }
else if (IsA(result, FuncExpr)) {
2455 FuncExpr *fe = (FuncExpr *)result;
2478 Query *q, List *prov_atts,
2485 QTW_DONT_COPY_QUERY | QTW_IGNORE_RT_SUBQUERIES);
2492 foreach(lc, q->targetList) {
2493 TargetEntry *te = (TargetEntry *)lfirst(lc);
2494 if (te->expr == NULL)
2497 if (IsA(te->expr, FuncExpr) &&
2516 TargetEntry *newte = makeNode(TargetEntry);
2517 bool inserted =
false;
2524 RangeTblEntry *rte = list_nth(q->rtable, ((Var *)
provenance)->varno - 1);
2525 newte->resorigtbl = rte->relid;
2526 newte->resorigcol = ((Var *)
provenance)->varattno;
2530 for (ListCell *cell = list_head(q->targetList); cell != NULL;) {
2531 TargetEntry *te = (TargetEntry *)lfirst(cell);
2538 newte->resno = resno;
2540 cell = list_nth_cell(q->targetList, resno);
2541 te = (TargetEntry *)lfirst(cell);
2548 cell =
my_lnext(q->targetList, cell);
2552 newte->resno = resno + 1;
2553 q->targetList = lappend(q->targetList, newte);
2581 if (IsA(node, Aggref))
2598 if (IsA(node, Aggref)) {
2611 if (IsA(node, FuncExpr)) {
2612 FuncExpr *f = (FuncExpr *)node;
2617 "applying an SQL aggregate on top of a ProvSQL-introduced "
2618 "aggregation is not supported: the inner provenance() would "
2619 "be substituted with an expression containing an aggregate, "
2620 "producing a nested same-level aggregate that PostgreSQL "
2621 "rejects. Evaluate the per-row provenance in a subquery "
2622 "and aggregate the resulting scalar outside, or drop the "
2623 "surrounding aggregate.");
2625 return (Node *)copyObject(context->
provsql);
2627 }
else if (IsA(node, RangeTblEntry) || IsA(node, RangeTblFunction)) {
2659 QTW_DONT_COPY_QUERY | QTW_IGNORE_RT_SUBQUERIES);
2676 Bitmapset *already_in_group_by = NULL;
2678 foreach (lc, q->groupClause) {
2679 SortGroupClause *sgc = (SortGroupClause *)lfirst(lc);
2680 already_in_group_by =
2681 bms_add_member(already_in_group_by, sgc->tleSortGroupRef);
2684 foreach (lc, q->distinctClause) {
2685 SortGroupClause *sgc = (SortGroupClause *)lfirst(lc);
2686 if (!bms_is_member(sgc->tleSortGroupRef, already_in_group_by)) {
2687 q->groupClause = lappend(q->groupClause, sgc);
2691 q->distinctClause = NULL;
2706 const Bitmapset *removed_sortgrouprefs) {
2707 List **lists[3] = {&q->groupClause, &q->distinctClause, &q->sortClause};
2710 for (i = 0; i < 3; ++i) {
2711 ListCell *cell, *prev;
2713 for (cell = list_head(*lists[i]), prev = NULL; cell != NULL;) {
2714 SortGroupClause *sgc = (SortGroupClause *)lfirst(cell);
2715 if (bms_is_member(sgc->tleSortGroupRef, removed_sortgrouprefs)) {
2721 cell = list_head(*lists[i]);
2743 SetOperationStmt *so = (SetOperationStmt *)q->setOperations;
2744 List **lists[3] = {&so->colTypes, &so->colTypmods, &so->colCollations};
2747 for (i = 0; i < 3; ++i) {
2748 ListCell *cell, *prev;
2751 for (cell = list_head(*lists[i]), prev = NULL, j = 0; cell != NULL; ++j) {
2758 cell = list_head(*lists[i]);
2784 Query *new_query = makeNode(Query);
2785 RangeTblEntry *rte = makeNode(RangeTblEntry);
2786 FromExpr *jointree = makeNode(FromExpr);
2787 RangeTblRef *rtr = makeNode(RangeTblRef);
2789 SetOperationStmt *stmt = (SetOperationStmt *)q->setOperations;
2792 int sortgroupref = 0;
2800 rte->rtekind = RTE_SUBQUERY;
2802 rte->eref = copyObject(((RangeTblEntry *)linitial(q->rtable))->eref);
2803 rte->inFromCl =
true;
2804#if PG_VERSION_NUM < 160000
2807 rte->requiredPerms = ACL_SELECT;
2811 jointree->fromlist = list_make1(rtr);
2813 new_query->commandType = CMD_SELECT;
2814 new_query->canSetTag =
true;
2815 new_query->rtable = list_make1(rte);
2816 new_query->jointree = jointree;
2817 new_query->targetList = copyObject(q->targetList);
2819 if (new_query->targetList) {
2820 foreach (lc, new_query->targetList) {
2821 TargetEntry *te = (TargetEntry *)lfirst(lc);
2822 SortGroupClause *sgc = makeNode(SortGroupClause);
2824 sgc->tleSortGroupRef = te->ressortgroupref = ++sortgroupref;
2826 get_sort_group_operators(exprType((Node *)te->expr),
false,
true,
false,
2827 &sgc->sortop, &sgc->eqop, NULL, &sgc->hashable);
2829 new_query->groupClause = lappend(new_query->groupClause, sgc);
2832 GroupingSet *gs = makeNode(GroupingSet);
2833 gs->kind = GROUPING_SET_EMPTY;
2836 new_query->groupingSets = list_make1(gs);
2860 if (IsA(node, FuncExpr)) {
2861 FuncExpr *f = (FuncExpr *)node;
2886 Bitmapset *group_refs = NULL;
2887 foreach (lc, q->groupClause) {
2888 SortGroupClause *sgc = (SortGroupClause *)lfirst(lc);
2889 group_refs = bms_add_member(group_refs, sgc->tleSortGroupRef);
2892 foreach (lc, q->targetList) {
2893 TargetEntry *te = (TargetEntry *)lfirst(lc);
2894 if (te->ressortgroupref > 0 &&
2895 bms_is_member(te->ressortgroupref, group_refs)) {
2897 (
void *)constants)) {
2901#if PG_VERSION_NUM >= 180000
2904 if(IsA(te->expr, Var)) {
2905 Var *v = (Var *) te->expr;
2906 RangeTblEntry *r = (RangeTblEntry *)list_nth(q->rtable, v->varno - 1);
2907 if(r->rtekind == RTE_GROUP)
2909 (
void *)constants)) {
2942 if (IsA(node, OpExpr)) {
2943 OpExpr *op = (OpExpr *)node;
2948 if (IsA(node, FuncExpr)) {
2949 FuncExpr *f = (FuncExpr *)node;
2954 if (IsA(node, SubLink)) {
2955 SubLink *sl = (SubLink *)node;
2961 if (IsA(node, Query))
2972 if (IsA(node, Query)) {
2973 Query *q = (Query *)node;
2979 foreach (rc, q->cteList) {
2980 CommonTableExpr *cte = (CommonTableExpr *)lfirst(rc);
3004 foreach (rc, q->rtable) {
3005 RangeTblEntry *r = (RangeTblEntry *)lfirst(rc);
3006 if (r->rtekind == RTE_RELATION) {
3008 AttrNumber attid = 1;
3010 foreach (lc, r->eref->colnames) {
3011 const char *v = strVal(lfirst(lc));
3020 }
else if (r->rtekind == RTE_FUNCTION) {
3022 AttrNumber attid = 1;
3024 foreach (lc, r->functions) {
3025 RangeTblFunction *func = (RangeTblFunction *)lfirst(lc);
3027 if (func->funccolcount == 1) {
3028 FuncExpr *expr = (FuncExpr *)func->funcexpr;
3030 !strcmp(get_rte_attribute_name(r, attid),
3036 attid += func->funccolcount;
3038 }
else if (r->rtekind == RTE_SUBQUERY && r->subquery != NULL) {
3083 if (IsA(node, Var)) {
3084 Var *v = (Var *) node;
3103 return expression_tree_walker(node,
aggtoken_walker, (
void*) constants);
3127 if (IsA(node, Var)) {
3128 Var *v = (Var *) node;
3133 if (IsA(node, FuncExpr)) {
3134 FuncExpr *fe = (FuncExpr *) node;
3157 (
void *) constants);
3180 SetOperationStmt *setOps = (SetOperationStmt *)q->setOperations;
3181 RangeTblEntry *rte = makeNode(RangeTblEntry);
3182 FromExpr *fe = makeNode(FromExpr);
3183 JoinExpr *je = makeNode(JoinExpr);
3184 BoolExpr *expr = makeNode(BoolExpr);
3188 if (!IsA(setOps->larg, RangeTblRef) || !IsA(setOps->rarg, RangeTblRef)) {
3192 expr->boolop = AND_EXPR;
3193 expr->location = -1;
3196 foreach (lc, q->targetList) {
3197 TargetEntry *te = (TargetEntry *)lfirst(lc);
3200 if (!IsA(te->expr, Var))
3203 v = (Var *)te->expr;
3206 OpExpr *oe = makeNode(OpExpr);
3208 Operator opInfo = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno));
3209 Form_pg_operator opform;
3210 Var *leftArg, *rightArg;
3212 if (!HeapTupleIsValid(opInfo))
3213 provsql_error(
"could not find operator with OID %u to compare variables of type %u",
3216 opform = (Form_pg_operator)GETSTRUCT(opInfo);
3217 leftArg = makeNode(Var);
3218 rightArg = makeNode(Var);
3221 oe->opfuncid = opform->oprcode;
3222 oe->opresulttype = opform->oprresult;
3223 oe->opcollid = InvalidOid;
3224 oe->inputcollid = DEFAULT_COLLATION_OID;
3226 leftArg->varno = ((RangeTblRef *)setOps->larg)->rtindex;
3227 rightArg->varno = ((RangeTblRef *)setOps->rarg)->rtindex;
3228 leftArg->varattno = rightArg->varattno = attno;
3230#if PG_VERSION_NUM >= 130000
3231 leftArg->varnosyn = rightArg->varnosyn = 0;
3232 leftArg->varattnosyn = rightArg->varattnosyn = 0;
3234 leftArg->varnoold = leftArg->varno;
3235 rightArg->varnoold = rightArg->varno;
3236 leftArg->varoattno = rightArg->varoattno = attno;
3239 leftArg->vartype = rightArg->vartype = v->vartype;
3240 leftArg->varcollid = rightArg->varcollid = InvalidOid;
3241 leftArg->vartypmod = rightArg->vartypmod = -1;
3242 leftArg->location = rightArg->location = -1;
3244 oe->args = list_make2(leftArg, rightArg);
3246 expr->args = lappend(expr->args, oe);
3248 ReleaseSysCache(opInfo);
3262 RangeTblRef *larg_ref = (RangeTblRef *)setOps->larg;
3263 RangeTblRef *rarg_ref = (RangeTblRef *)setOps->rarg;
3264 RangeTblEntry *larg_rte =
3265 (RangeTblEntry *)list_nth(q->rtable, larg_ref->rtindex - 1);
3266 RangeTblEntry *rarg_rte =
3267 (RangeTblEntry *)list_nth(q->rtable, rarg_ref->rtindex - 1);
3268 List *aliasvars = NIL;
3269 List *leftcols = NIL;
3270 List *rightcols = NIL;
3271 List *colnames = NIL;
3276 foreach (lc_te, larg_rte->subquery->targetList) {
3277 TargetEntry *te = (TargetEntry *)lfirst(lc_te);
3282 aliasvars = lappend(aliasvars,
3283 makeVar(larg_ref->rtindex, colno,
3284 exprType((Node *)te->expr),
3285 exprTypmod((Node *)te->expr),
3286 exprCollation((Node *)te->expr),
3288 leftcols = lappend_int(leftcols, colno);
3289 rightcols = lappend_int(rightcols, 0);
3290 colnames = lappend(colnames,
3291 makeString(pstrdup(te->resname ? te->resname
3296 foreach (lc_te, rarg_rte->subquery->targetList) {
3297 TargetEntry *te = (TargetEntry *)lfirst(lc_te);
3302 aliasvars = lappend(aliasvars,
3303 makeVar(rarg_ref->rtindex, colno,
3304 exprType((Node *)te->expr),
3305 exprTypmod((Node *)te->expr),
3306 exprCollation((Node *)te->expr),
3308 leftcols = lappend_int(leftcols, 0);
3309 rightcols = lappend_int(rightcols, colno);
3310 colnames = lappend(colnames,
3311 makeString(pstrdup(te->resname ? te->resname
3317 rte->eref = makeAlias(
"unnamed_join", colnames);
3318 rte->joinaliasvars = aliasvars;
3319#if PG_VERSION_NUM >= 130000
3320 rte->joinleftcols = leftcols;
3321 rte->joinrightcols = rightcols;
3322 rte->joinmergedcols = 0;
3329 rte->rtekind = RTE_JOIN;
3330 rte->jointype = JOIN_LEFT;
3332 q->rtable = lappend(q->rtable, rte);
3334 je->jointype = JOIN_LEFT;
3336 je->larg = setOps->larg;
3337 je->rarg = setOps->rarg;
3338 je->quals = (Node *)expr;
3339 je->rtindex = list_length(q->rtable);
3341 fe->fromlist = list_make1(je);
3347 q->setOperations = 0;
3366 SetOperationStmt *stmt,
3368 if (stmt->op != SETOP_UNION) {
3371 if (IsA(stmt->larg, SetOperationStmt)) {
3374 if (IsA(stmt->rarg, SetOperationStmt)) {
3380 if (IsA(stmt->larg, RangeTblRef)) {
3381 Index rtindex = ((RangeTblRef *)stmt->larg)->rtindex;
3382 RangeTblEntry *rte = list_nth_node(RangeTblEntry, q->rtable, rtindex - 1);
3383 if (rte->rtekind == RTE_SUBQUERY && rte->subquery != NULL) {
3384 ListCell *lc_type = list_head(stmt->colTypes);
3385 ListCell *lc_te = list_head(rte->subquery->targetList);
3386 while (lc_type != NULL && lc_te != NULL) {
3387 TargetEntry *te = (TargetEntry *)lfirst(lc_te);
3391 lc_type =
my_lnext(stmt->colTypes, lc_type);
3392 lc_te =
my_lnext(rte->subquery->targetList, lc_te);
3397 stmt->colTypes = lappend_oid(stmt->colTypes, constants->
OID_TYPE_UUID);
3398 stmt->colTypmods = lappend_int(stmt->colTypmods, -1);
3399 stmt->colCollations = lappend_int(stmt->colCollations, 0);
3417 FuncExpr *
gate_zero = makeNode(FuncExpr);
3418 OpExpr *oe = makeNode(OpExpr);
3425 oe->opresulttype = BOOLOID;
3429 if (q->jointree->quals != NULL) {
3430 BoolExpr *be = makeNode(BoolExpr);
3432 be->boolop = AND_EXPR;
3433 be->args = list_make2(oe, q->jointree->quals);
3436 q->jointree->quals = (Node *)be;
3438 q->jointree->quals = (Node *)oe;
3455 havingQual = (Node*) expr;
3456 }
else if(IsA(havingQual, BoolExpr) && ((BoolExpr*)havingQual)->boolop==AND_EXPR) {
3457 BoolExpr *be = (BoolExpr*)havingQual;
3458 be->args = lappend(be->args, expr);
3459 }
else if(IsA(havingQual, OpExpr) || IsA(havingQual, BoolExpr)) {
3461 BoolExpr *be = makeNode(BoolExpr);
3462 be->boolop=AND_EXPR;
3464 be->args = list_make2(havingQual, expr);
3465 havingQual = (Node*) be;
3467 provsql_error(
"Unknown structure within Boolean expression");
3488 bool found_agg_token=
false;
3490 if(op->args->length != 2)
3493 for(
unsigned i=0; i<2; ++i) {
3494 Node *arg = lfirst(list_nth_cell(op->args, i));
3499 found_agg_token=
true;
3500 }
else if(IsA(arg, Const)) {
3501 }
else if(IsA(arg, FuncExpr)) {
3502 FuncExpr *fe = (FuncExpr*) arg;
3503 if(fe->funcformat != COERCE_IMPLICIT_CAST && fe->funcformat != COERCE_EXPLICIT_CAST) {
3507 if(fe->args->length != 1) {
3511 if(!IsA(lfirst(list_head(fe->args)), Const)) {
3521 return ok && found_agg_token;
3539 foreach (lc, be->args) {
3541 if(IsA(n, OpExpr)) {
3544 }
else if(IsA(n, BoolExpr)) {
3562 switch(expr->type) {
3568 provsql_error(
"Unknown structure within Boolean expression");
3606 foreach (l, q->rtable) {
3607 RangeTblEntry *r = (RangeTblEntry *)lfirst(l);
3614 columns[i] = (
int *)palloc(r->eref->colnames->length *
sizeof(
int));
3616 foreach (lc, r->eref->colnames) {
3619 columns[i][j] = ++(*nbcols);
3621 const char *v = strVal(lfirst(lc));
3623 if (strcmp(v,
"") && r->rtekind != RTE_JOIN) {
3627 columns[i][j] = ++(*nbcols);
3684 if (has_agg && has_rv)
3709 provsql_error(
"Complex selection on aggregation results not supported");
3712 provsql_error(
"WHERE clause mixes random_variable comparisons with "
3713 "other predicates inside the same Boolean expression; "
3714 "split the non-RV part into its own AND conjunct");
3717 provsql_error(
"WHERE clause mixes agg_token (HAVING-style) and "
3718 "random_variable (per-tuple) comparisons inside the "
3719 "same Boolean expression; this combination is not "
3761 List *rv_cmps = NIL;
3764 if (!q->jointree || !q->jointree->quals)
3767 quals = q->jointree->quals;
3773 if (!IsA(quals, BoolExpr) || ((BoolExpr *)quals)->boolop != AND_EXPR) {
3780 q->jointree->quals = NULL;
3783 rv_cmps = lappend(rv_cmps,
3786 q->jointree->quals = NULL;
3800 BoolExpr *be = (BoolExpr *)quals;
3801 ListCell *cell, *prev;
3803 for (cell = list_head(be->args), prev = NULL; cell != NULL;) {
3804 Expr *conjunct = (Expr *)lfirst(cell);
3816 cell = list_head(be->args);
3819 rv_cmps = lappend(rv_cmps,
3826 cell = list_head(be->args);
3839 if (be->args == NIL)
3840 q->jointree->quals = NULL;
3841 else if (list_length(be->args) == 1)
3842 q->jointree->quals = (Node *)linitial(be->args);
3865 if (v->varno < 1 || v->varno > list_length(ctx->
query->rtable))
3868 rte = list_nth_node(RangeTblEntry, ctx->
query->rtable, v->varno - 1);
3869 if (rte->rtekind != RTE_SUBQUERY || rte->subquery == NULL)
3872 if (v->varattno < 1 || v->varattno > list_length(rte->subquery->targetList))
3875 te = list_nth_node(TargetEntry, rte->subquery->targetList, v->varattno - 1);
3876 if (IsA(te->expr, FuncExpr)) {
3877 FuncExpr *f = (FuncExpr *)te->expr;
3879 Const *aggtype_const = (Const *)lsecond(f->args);
3880 return DatumGetObjectId(aggtype_const->constvalue);
3891 Var *v = (Var *)lfirst(lc);
3893 HeapTuple castTuple;
3895 if (!OidIsValid(target))
3898 castTuple = SearchSysCache2(CASTSOURCETARGET,
3900 ObjectIdGetDatum(target));
3901 if (HeapTupleIsValid(castTuple)) {
3902 Form_pg_cast castForm = (Form_pg_cast)GETSTRUCT(castTuple);
3903 if (OidIsValid(castForm->castfunc)) {
3904 FuncExpr *fc = makeNode(FuncExpr);
3905 fc->funcid = castForm->castfunc;
3906 fc->funcresulttype = target;
3907 fc->funcretset =
false;
3908 fc->funcvariadic =
false;
3909 fc->funcformat = COERCE_IMPLICIT_CAST;
3910 fc->funccollid = InvalidOid;
3911 fc->inputcollid = InvalidOid;
3912 fc->args = list_make1(v);
3916 ReleaseSysCache(castTuple);
3926 foreach (lc, args) {
3927 if (IsA(lfirst(lc), Var) &&
3949 if (IsA(node, OpExpr)) {
3951 return (Node *)node;
3953 if (IsA(node, WindowFunc)) {
3955 return (Node *)node;
3957 if (IsA(node, CoalesceExpr)) {
3959 return (Node *)node;
3961 if (IsA(node, MinMaxExpr)) {
3963 return (Node *)node;
3965 if (IsA(node, NullIfExpr)) {
3967 return (Node *)node;
3979 QTW_DONT_COPY_QUERY | QTW_IGNORE_RC_SUBQUERIES);
4002 FuncExpr *wrap = makeNode(FuncExpr);
4005 wrap->funcretset =
false;
4006 wrap->funcvariadic =
false;
4007 wrap->funcformat = COERCE_EXPLICIT_CALL;
4008 wrap->funccollid = InvalidOid;
4009 wrap->inputcollid = InvalidOid;
4010 wrap->args = list_make1(expr);
4011 wrap->location = -1;
4012 return (Expr *) wrap;
4044 bool **removed,
bool wrap_root) {
4046 bool has_union =
false;
4047 bool has_difference =
false;
4048 bool supported =
true;
4049 bool group_by_rewrite =
false;
4054 elog_node_display(NOTICE,
"ProvSQL: Before query rewriting", q,
true);
4056 if (q->rtable == NULL) {
4067 if (rv_cmps != NIL) {
4069 RangeTblEntry *values_rte;
4073 if (list_length(rv_cmps) == 1) {
4077 FuncExpr *times = makeNode(FuncExpr);
4078 ArrayExpr *array = makeNode(ArrayExpr);
4081 times->funcvariadic =
true;
4082 times->location = -1;
4085 array->elements = rv_cmps;
4086 array->location = -1;
4087 times->args = list_make1(array);
4100 values_rte = makeNode(RangeTblEntry);
4101 values_rte->rtekind = RTE_VALUES;
4102 values_rte->values_lists = list_make1(list_make1(
provenance));
4103 values_rte->coltypes = list_make1_oid(constants->
OID_TYPE_UUID);
4104 values_rte->coltypmods = list_make1_int(-1);
4105 values_rte->colcollations = list_make1_oid(InvalidOid);
4106 values_rte->eref = makeAlias(
4109 values_rte->inh =
false;
4110 values_rte->inFromCl =
true;
4111#if PG_VERSION_NUM < 160000
4112 values_rte->requiredPerms = 0;
4114 q->rtable = list_make1(values_rte);
4116 rtr = makeNode(RangeTblRef);
4118 if (q->jointree == NULL) {
4119 q->jointree = makeNode(FromExpr);
4121 q->jointree->fromlist = list_make1(rtr);
4123 v = makeVar(1, 1, constants->
OID_TYPE_UUID, -1, InvalidOid, 0);
4132 TargetEntry *te = makeTargetEntry(
4133 (Expr *)copyObject(v),
4134 list_length(q->targetList) + 1,
4137 q->targetList = lappend(q->targetList, te);
4149 Bitmapset *removed_sortgrouprefs = NULL;
4151 if (q->targetList) {
4152 removed_sortgrouprefs =
4154 if (removed_sortgrouprefs != NULL)
4156 if (q->setOperations)
4162 columns = (
int **)palloc(q->rtable->length *
sizeof(
int *));
4164 if (q->setOperations) {
4168 SetOperationStmt *stmt = (SetOperationStmt *)q->setOperations;
4174 foreach (lc_rte, q->rtable) {
4175 RangeTblEntry *rte = (RangeTblEntry *)lfirst(lc_rte);
4176 if (rte->rtekind == RTE_SUBQUERY && rte->subquery &&
4177 rte->subquery->hasAggs)
4179 "aggregate results not supported");
4189 return process_query(constants, rewritten, removed, wrap_root);
4214 if (prov_atts == NIL) {
4221 if (q->jointree && q->jointree->quals &&
4223 FuncExpr *one_expr = makeNode(FuncExpr);
4226 one_expr->args = NIL;
4227 one_expr->location = -1;
4228 prov_atts = list_make1(one_expr);
4234 if (q->hasSubLinks) {
4235 provsql_error(
"Subqueries (EXISTS, IN, scalar subquery) not supported");
4239 if (supported && q->distinctClause) {
4240 if (q->hasDistinctOn) {
4243 }
else if (q->hasAggs) {
4244 provsql_error(
"DISTINCT on aggregate results not supported");
4245 }
else if (list_length(q->distinctClause) < list_length(q->targetList)) {
4246 provsql_error(
"Inconsistent DISTINCT and GROUP BY clauses not "
4254 if (supported && q->setOperations) {
4255 SetOperationStmt *stmt = (SetOperationStmt *)q->setOperations;
4257 if (stmt->op == SETOP_UNION) {
4260 }
else if (stmt->op == SETOP_EXCEPT) {
4263 has_difference =
true;
4265 provsql_error(
"Set operations other than UNION and EXCEPT not "
4271 if (supported && q->groupClause &&
4273 group_by_rewrite =
true;
4276 if (supported && q->groupingSets) {
4277 if (q->groupClause || list_length(q->groupingSets) > 1 ||
4278 ((GroupingSet *)linitial(q->groupingSets))->kind !=
4279 GROUPING_SET_EMPTY) {
4280 provsql_error(
"GROUPING SETS, CUBE, and ROLLUP not supported");
4284 group_by_rewrite =
true;
4315 if (rv_cmps != NIL && !has_union && !has_difference) {
4316 prov_atts = list_concat(prov_atts, rv_cmps);
4325 constants, q, prov_atts,
4331 foreach (lc_sort, q->sortClause) {
4332 SortGroupClause *sort = (SortGroupClause *)lfirst(lc_sort);
4334 foreach (lc_te, q->targetList) {
4335 TargetEntry *te = (TargetEntry *)lfirst(lc_te);
4336 if (sort->tleSortGroupRef == te->ressortgroupref) {
4338 provsql_error(
"ORDER BY on the result of an aggregate function is "
4351 constants, q, prov_atts, q->hasAggs, group_by_rewrite,
4358 if (rv_cmps != NIL) {
4359 FuncExpr *times = makeNode(FuncExpr);
4360 ArrayExpr *array = makeNode(ArrayExpr);
4363 times->funcvariadic =
true;
4364 times->location = -1;
4367 array->elements = lcons(
provenance, rv_cmps);
4368 array->location = -1;
4369 times->args = list_make1(array);
4380 for (i = 0; i < q->rtable->length; ++i) {
4387 elog_node_display(NOTICE,
"ProvSQL: After query rewriting", q,
true);
4408 Index src_rteid = 0;
4409 RangeTblEntry *src_rte = NULL;
4410 RangeTblEntry *tgt_rte;
4411 AttrNumber provsql_attno = 0;
4412 TargetEntry *provsql_te = NULL;
4415 foreach (lc, q->rtable) {
4416 RangeTblEntry *r = (RangeTblEntry *)lfirst(lc);
4418 if (r->rtekind == RTE_SUBQUERY && r->subquery &&
4425 if (src_rte == NULL)
4429 tgt_rte = list_nth_node(RangeTblEntry, q->rtable, q->resultRelation - 1);
4430 if (tgt_rte->rtekind == RTE_RELATION) {
4431 AttrNumber attid = 1;
4432 foreach (lc, tgt_rte->eref->colnames) {
4434 get_atttype(tgt_rte->relid, attid) == constants->
OID_TYPE_UUID)
4435 provsql_attno = attid;
4440 if (provsql_attno == 0) {
4442 "tables: source provenance is not propagated "
4443 "to inserted rows");
4448 foreach (lc, q->targetList) {
4449 TargetEntry *te = (TargetEntry *)lfirst(lc);
4450 if (te->resno == provsql_attno &&
4457 if (provsql_te == NULL) {
4462 provsql_te = makeNode(TargetEntry);
4463 provsql_te->resno = provsql_attno;
4466 q->targetList = lappend(q->targetList, provsql_te);
4471 bool *removed = NULL;
4472 Query *new_subquery =
process_query(constants, src_rte->subquery, &removed,
false);
4473 AttrNumber src_provsql_attno = 0;
4475 if (new_subquery == NULL)
4478 src_rte->subquery = new_subquery;
4481 foreach (lc, new_subquery->targetList) {
4482 TargetEntry *te = (TargetEntry *)lfirst(lc);
4485 src_provsql_attno = te->resno;
4490 if (src_provsql_attno == 0)
4495 Var *v = makeNode(Var);
4496 v->varno = src_rteid;
4497 v->varattno = src_provsql_attno;
4500 v->varcollid = InvalidOid;
4502 provsql_te->expr = (Expr *)v;
4506 src_rte->eref->colnames = lappend(src_rte->eref->colnames,
4541#
if PG_VERSION_NUM >= 130000
4542 const char *query_string,
4545 ParamListInfo boundParams) {
4546 if (q->commandType == CMD_INSERT && q->rtable) {
4550 }
else if (q->commandType == CMD_SELECT) {
4570 && q->rtable != NIL) {
4578 bool *removed = NULL;
4582#if PG_VERSION_NUM >= 150000
4585 pg_get_querydef(q,
true));
4595 (
double)(clock() - begin) / CLOCKS_PER_SEC);
4597 if (new_query != NULL)
4600#if PG_VERSION_NUM >= 150000
4603 pg_get_querydef(q,
true));
4610#
if PG_VERSION_NUM >= 130000
4613 cursorOptions, boundParams);
4615 return standard_planner(q,
4616#
if PG_VERSION_NUM >= 130000
4619 cursorOptions, boundParams);
4640 standard_ExecutorStart(queryDesc, eflags);
4651#if PG_VERSION_NUM >= 130000
4657 standard_ExecutorEnd(queryDesc);
4673 standard_ExecutorEnd(queryDesc);
4742 CreateTableAsStmt *stmt;
4746 AttrNumber prov_resno = InvalidAttrNumber;
4747 Oid source_relid = InvalidOid;
4749 Bitmapset *ancestor_bms = NULL;
4756 if (parsetree == NULL || !IsA(parsetree, CreateTableAsStmt))
4758 stmt = (CreateTableAsStmt *) parsetree;
4759 if (stmt->query == NULL || !IsA(stmt->query, Query))
4761 qry = (Query *) stmt->query;
4762 if (qry->commandType != CMD_SELECT)
4775 foreach (lc, qry->targetList) {
4776 TargetEntry *te = (TargetEntry *) lfirst(lc);
4777 Node *e = (Node *) te->expr;
4780 AttrNumber prov_attno;
4784 while (e != NULL && IsA(e, RelabelType))
4785 e = (Node *) ((RelabelType *) e)->arg;
4786 if (e == NULL || !IsA(e, Var))
4789 if (v->varlevelsup != 0)
4791 if (v->varno < 1 || (
int) v->varno > list_length(qry->rtable))
4793 rte = (RangeTblEntry *) list_nth(qry->rtable, v->varno - 1);
4794 if (rte->rtekind != RTE_RELATION)
4797 if (prov_attno == InvalidAttrNumber || v->varattno != prov_attno)
4803 prov_resno = te->resno;
4804 source_relid = rte->relid;
4807 if (prov_resno == InvalidAttrNumber) {
4818 Oid src_relid = lfirst_oid(lc);
4822 for (uint16 i = 0; i < src_n; ++i)
4823 ancestor_bms = bms_add_member(ancestor_bms, (
int) src_ancestors[i]);
4825 ancestor_bms = bms_add_member(ancestor_bms, (
int) src_relid);
4832 while ((bms_member = bms_next_member(ancestor_bms, bms_member)) >= 0) {
4837 bms_free(ancestor_bms);
4840 cap->
ancestors[ancestor_n++] = (Oid) bms_member;
4842 bms_free(ancestor_bms);
4885 CreateTableAsStmt *stmt;
4887 AttrNumber prov_attno;
4889 uint16 eff_block_key_n = 0;
4892 Datum block_key_datum;
4893 Datum ancestors_datum;
4894 Datum *block_key_elems;
4895 Datum *ancestor_elems;
4896 ArrayType *block_key_arr;
4897 ArrayType *ancestors_arr;
4898 const char *nspname;
4899 const char *relname;
4900 StringInfoData trigger_sql;
4904 stmt = (CreateTableAsStmt *) parsetree;
4906 new_relid = RangeVarGetRelid(stmt->into->rel, NoLock,
true);
4907 if (new_relid == InvalidOid)
4915 if (prov_attno == InvalidAttrNumber)
4917 if (get_atttype(new_relid, prov_attno) != UUIDOID)
4934 TargetEntry *te = (TargetEntry *) lfirst(lc);
4935 Node *e = (Node *) te->expr;
4940 while (e != NULL && IsA(e, RelabelType))
4941 e = (Node *) ((RelabelType *) e)->arg;
4942 if (e == NULL || !IsA(e, Var))
4945 if (v->varlevelsup != 0)
4948 || (
int) v->varno > list_length(cap->
inner_query->rtable))
4950 rte = (RangeTblEntry *)
4952 if (rte->rtekind != RTE_RELATION)
4955 && v->varattno == src_attno) {
4960 eff_block_key[eff_block_key_n++] = te->resno;
4972 eff_block_key_n = 0;
4981 if (eff_block_key_n == 0) {
4982 block_key_arr = construct_empty_array(INT2OID);
4984 block_key_elems = palloc(eff_block_key_n *
sizeof(Datum));
4985 for (uint16 i = 0; i < eff_block_key_n; ++i)
4986 block_key_elems[i] = Int16GetDatum(eff_block_key[i]);
4987 block_key_arr = construct_array(block_key_elems, eff_block_key_n,
4988 INT2OID, 2,
true,
's');
4989 pfree(block_key_elems);
4991 block_key_datum = PointerGetDatum(block_key_arr);
4993 ObjectIdGetDatum(new_relid),
4998 ancestors_arr = construct_empty_array(OIDOID);
5000 ancestor_elems = palloc(cap->
ancestor_n *
sizeof(Datum));
5002 ancestor_elems[i] = ObjectIdGetDatum(cap->
ancestors[i]);
5003 ancestors_arr = construct_array(ancestor_elems, cap->
ancestor_n,
5004 OIDOID,
sizeof(Oid),
true,
'i');
5005 pfree(ancestor_elems);
5007 ancestors_datum = PointerGetDatum(ancestors_arr);
5009 ObjectIdGetDatum(new_relid),
5026#if PG_VERSION_NUM >= 140000
5027 if (stmt->objtype == OBJECT_MATVIEW)
5029 if (stmt->relkind == OBJECT_MATVIEW)
5033 nspname = get_namespace_name(get_rel_namespace(new_relid));
5034 relname = get_rel_name(new_relid);
5035 if (nspname == NULL || relname == NULL)
5037 initStringInfo(&trigger_sql);
5038 appendStringInfo(&trigger_sql,
5039 "CREATE TRIGGER provenance_guard "
5040 "BEFORE INSERT OR UPDATE OF provsql ON %s.%s "
5045 "FOR EACH ROW EXECUTE PROCEDURE provsql.provenance_guard()",
5046 quote_identifier(nspname), quote_identifier(relname));
5047 if (SPI_connect() != SPI_OK_CONNECT)
5049 if (SPI_exec(trigger_sql.data, 0) != SPI_OK_UTILITY)
5050 provsql_error(
"CTAS lineage hook: failed to install provenance_guard "
5051 "on %s.%s", nspname, relname);
5053 pfree(trigger_sql.data);
5058 const char *queryString,
5059#
if PG_VERSION_NUM >= 140000
5062 ProcessUtilityContext context,
5063 ParamListInfo params,
5064 QueryEnvironment *queryEnv,
5066#
if PG_VERSION_NUM >= 130000
5072 Node *parsetree = pstmt ? pstmt->utilityStmt : NULL;
5079#
if PG_VERSION_NUM >= 140000
5082 context, params, queryEnv, dest,
5083#
if PG_VERSION_NUM >= 130000
5090 standard_ProcessUtility(pstmt, queryString,
5091#
if PG_VERSION_NUM >= 140000
5094 context, params, queryEnv, dest,
5095#
if PG_VERSION_NUM >= 130000
5116 if (!process_shared_preload_libraries_in_progress)
5117 provsql_error(
"provsql needs to be added to the shared_preload_libraries "
5118 "configuration variable");
5120 DefineCustomBoolVariable(
"provsql.active",
5121 "Should ProvSQL track provenance?",
5122 "1 is standard ProvSQL behavior, 0 means provsql attributes will be dropped.",
5130 DefineCustomBoolVariable(
"provsql.where_provenance",
5131 "Should ProvSQL track where-provenance?",
5132 "1 turns where-provenance on, 0 off.",
5140 DefineCustomBoolVariable(
"provsql.update_provenance",
5141 "Should ProvSQL track update provenance?",
5142 "1 turns update provenance on, 0 off.",
5150 DefineCustomBoolVariable(
"provsql.aggtoken_text_as_uuid",
5151 "Output agg_token cells as the underlying UUID "
5152 "instead of \"value (*)\".",
5153 "Off by default for psql-friendly output. UI "
5154 "layers (notably ProvSQL Studio) flip this on "
5155 "per session so aggregate cells expose the "
5156 "circuit root UUID for click-through; the "
5157 "display value is recovered via "
5158 "provsql.agg_token_value_text(uuid).",
5166 DefineCustomIntVariable(
"provsql.verbose_level",
5167 "Level of verbosity for ProvSQL informational and debug messages",
5168 "0 for quiet (default), 1-9 for informational messages, 10-100 for debug information.",
5178 DefineCustomStringVariable(
"provsql.tool_search_path",
5179 "Directories prepended to PATH when ProvSQL spawns external tools.",
5180 "Colon-separated list of directories searched before the server's PATH "
5181 "when locating d4, c2d, minic2d, dsharp, weightmc, or graph-easy. "
5182 "Empty (default) means rely on the server's PATH alone.",
5190 DefineCustomBoolVariable(
"provsql.simplify_on_load",
5191 "Apply universal cmp-resolution passes when "
5192 "loading a provenance circuit.",
5193 "When on (default), every GenericCircuit returned "
5194 "by getGenericCircuit goes through RangeCheck "
5195 "(and any future universal pass): comparators "
5196 "decidable to certain Boolean values become "
5197 "Bernoulli gate_input gates with probability 0 "
5198 "or 1, transparent to every downstream consumer "
5199 "(semiring evaluators, MC, view_circuit, PROV "
5200 "export). Set off to inspect raw circuit "
5201 "structure (e.g. when debugging gate-creation "
5215 DefineCustomBoolVariable(
"provsql.hybrid_evaluation",
5216 "Run the hybrid-evaluator simplifier and "
5217 "island decomposer inside probability_evaluate. "
5219 "When on (default), probability_evaluate runs "
5220 "the HybridEvaluator peephole simplifier "
5221 "between RangeCheck and AnalyticEvaluator and "
5222 "the per-cmp MC island decomposer after "
5223 "AnalyticEvaluator. Off bypasses both and lets "
5224 "unresolved comparators fall through to "
5225 "whole-circuit MC. End users have no reason "
5226 "to flip this; it exists for developer A/B "
5227 "testing against the unfolded path and as a "
5228 "bisection knob if a closure rule turns out "
5229 "to be unsound on some workload.",
5233 GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,
5246 DefineCustomBoolVariable(
"provsql.cmp_probability_evaluation",
5247 "Run closed-form / analytic probability "
5248 "evaluators for gate_cmps inside "
5249 "probability_evaluate. Debug only.",
5250 "When on (default), probability_evaluate "
5251 "runs pre-passes that recognise specific "
5252 "gate_cmp shapes (currently HAVING COUNT(*) "
5253 "op C over distinct gate_input leaves) and "
5254 "replace each cmp with a Bernoulli "
5255 "gate_input carrying the closed-form "
5256 "probability, bypassing the DNF that "
5257 "provsql_having's enumerate_valid_worlds "
5258 "would otherwise emit. Off forces the cmp "
5259 "to fall through to that enumeration path. "
5260 "Future MIN / MAX / SUM probability "
5261 "evaluators will gate on the same flag. "
5262 "End users have no reason to flip this; it "
5263 "exists for developer A/B testing and as a "
5264 "bisection escape valve.",
5268 GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,
5272 DefineCustomBoolVariable(
"provsql.boolean_provenance",
5273 "Opt-in safe-query optimisation for hierarchical conjunctive queries.",
5274 "When on, the planner rewrites self-join-free "
5275 "hierarchical conjunctive queries (and independent "
5276 "UCQs) over TID/BID tables to a read-once form "
5277 "whose probability is computable in linear time "
5278 "via the existing BooleanCircuit independent "
5279 "evaluator. The rewrite preserves the Boolean "
5280 "polynomial of the existential lineage, so any "
5281 "evaluation that factors through a homomorphism "
5282 "from Boolean functions remains sound (notably "
5283 "probability and Shapley / Banzhaf). The "
5284 "resulting root gate is tagged; semiring "
5285 "evaluators are individually marked at compile "
5286 "time as compatible or not with this rewrite, "
5287 "and incompatible evaluators refuse to run on a "
5288 "tagged circuit. Off by default because the "
5289 "rewrite changes the multiset of result rows and "
5290 "is therefore unsound for per-row provenance "
5291 "interrogations and aggregation queries.",
5299 DefineCustomBoolVariable(
"provsql.classify_top_level",
5300 "Emit a NOTICE classifying each top-level SELECT.",
5301 "When on, every top-level SELECT that "
5302 "touches a relation triggers a NOTICE of "
5303 "the form `ProvSQL: query result is "
5304 "<KIND> (sources: ...)` where <KIND> is "
5305 "TID, BID, or OPAQUE under the existing "
5306 "provsql_table_kind taxonomy and the "
5307 "sources list names the provenance-"
5308 "tracked base relations the query touches. "
5309 "Read-only : the classifier does not "
5310 "rewrite the query. Studio reads the "
5311 "NOTICE to label query results with their "
5320 DefineCustomIntVariable(
"provsql.monte_carlo_seed",
5321 "Seed for the Monte Carlo sampler.",
5322 "-1 (default) seeds from std::random_device for "
5323 "non-deterministic sampling. Any other value "
5324 "(including 0) is used as a literal seed for "
5325 "std::mt19937_64, making "
5326 "probability_evaluate(..., 'monte-carlo', n) "
5327 "reproducible across runs and across the Bernoulli "
5328 "and continuous (gate_rv) sampling paths.",
5338 DefineCustomIntVariable(
"provsql.rv_mc_samples",
5339 "Default sample count for analytical-evaluator MC fallbacks.",
5340 "Used when an analytical evaluator (Expectation, "
5341 "future hybrid evaluator, etc.) cannot decompose a "
5342 "sub-circuit and needs to fall back to Monte Carlo. "
5343 "Default 10000. Set to 0 to disable the fallback "
5344 "entirely: callers raise an exception rather than "
5345 "sampling, which is useful when only analytical "
5346 "answers are acceptable. Unrelated to "
5347 "probability_evaluate(..., 'monte-carlo', n) where "
5348 "the sample count is an explicit argument.",
5360 EmitWarningsOnPlaceholders(
"provsql");
5367#if (PG_VERSION_NUM >= 150000)
#define PROVSQL_TABLE_INFO_MAX_BLOCK_KEY
Cap on the number of block-key columns recorded per relation.
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.
bool provsql_classify_top_level
Backing storage for the provsql.classify_top_level GUC.
void provsql_classify_emit_notice(const ProvSQLClassification *c)
Render the result of provsql_classify_query as a NOTICE.
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.
List * list_insert_nth(List *list, int pos, void *datum)
Insert datum at position pos in list (PG < 13 backport).
PostgreSQL cross-version compatibility shims for ProvSQL.
#define F_SUM_INT4
OID of sum(int4) aggregate function (pre-PG 14).
static List * my_list_delete_cell(List *list, ListCell *cell, ListCell *prev)
Version-agnostic wrapper around list_delete_cell().
static ListCell * my_lnext(const List *l, const ListCell *c)
Version-agnostic wrapper around lnext().
#define F_COUNT_
OID of count() aggregate function (pre-PG 14).
#define F_COUNT_ANY
OID of count(*) / count(any) aggregate function (pre-PG 14).
Datum provenance(PG_FUNCTION_ARGS)
Error stub for provsql.provenance() on untracked tables.
static void transform_distinct_into_group_by(Query *q)
Convert a SELECT DISTINCT into an equivalent GROUP BY.
static const char * provsql_ctas_kind_label(provsql_table_kind k)
Map provsql_table_kind to its textual label (set_table_info accepts text).
static void remove_provenance_attribute_groupref(Query *q, const Bitmapset *removed_sortgrouprefs)
Remove sort/group references that belonged to removed provenance columns.
static qual_class classify_qual(Expr *expr, const constants_t *constants)
Classify expr along the qual_class axis.
static FuncExpr * having_Expr_to_provenance_cmp(Expr *expr, const constants_t *constants, bool negated)
Dispatch a HAVING sub-expression to the appropriate converter.
static void maybe_cast_agg_token_args(List *args, Oid parent_funcid, const constants_t *constants)
Cast provenance_aggregate arguments of an operator or function when the formal parameter type require...
Datum set_ancestors(PG_FUNCTION_ARGS)
PostgreSQL-callable wrapper for setTableAncestry() over the IPC pipe.
static bool transform_except_into_join(const constants_t *constants, Query *q)
Rewrite an EXCEPT query into a LEFT JOIN with monus provenance.
bool provsql_where_provenance
Global variable that indicates if where-provenance support has been activated through the provsql....
static bool has_provenance_walker(Node *node, void *data)
static bool has_rv_or_provenance_call(Node *node, void *data)
Tree walker that detects any provenance-bearing relation or provenance() call.
int provsql_verbose
Verbosity level; controlled by the provsql.verbose_level GUC.
static bool aggtoken_walker(Node *node, void *data)
Tree walker that detects any Var of type agg_token.
static bool check_selection_on_aggregate(OpExpr *op, const constants_t *constants)
Check whether op is a supported comparison on an aggregate result.
void _PG_init(void)
Extension initialization – called once when the shared library is loaded.
bool provsql_simplify_on_load
Run universal cmp-resolution passes when getGenericCircuit returns; controlled by the provsql....
static FuncExpr * having_BoolExpr_to_provenance(BoolExpr *be, const constants_t *constants, bool negated)
Convert a Boolean combination of HAVING comparisons into a provenance_times / provenance_plus gate ex...
static bool is_target_agg_var(Node *node, aggregation_type_mutator_context *context)
Check if a Var matches the target aggregate column.
static bool has_aggtoken(Node *node, const constants_t *constants)
Return true if node contains a Var of type agg_token.
static int rv_cmp_index(const constants_t *constants, Oid funcoid)
Test whether funcoid is one of the random_variable_* comparison procedures, and if so return its Comp...
static void replace_provenance_function_by_expression(const constants_t *constants, Query *q, Expr *provsql)
Replace every explicit provenance() call in q with provsql.
static Node * reduce_varattno_mutator(Node *node, void *ctx)
Tree-mutator callback that adjusts Var attribute numbers.
static void provsql_ProcessUtility_capture(Node *parsetree, ProvSQLCtasCapture *cap)
Decide whether parsetree is a CTAS that should trigger the ancestry hook, and if so populate cap with...
static List * get_provenance_attributes(const constants_t *constants, Query *q)
Collect all provenance Var nodes reachable from q's range table.
static bool check_expr_on_rv(Expr *expr, const constants_t *constants)
Test whether expr is a Boolean combination of only random_variable comparisons (no other leaves allow...
PG_MODULE_MAGIC
Required PostgreSQL extension magic block.
static void insert_agg_token_casts(const constants_t *constants, Query *q)
Walk query and insert agg_token casts where needed.
Datum set_table_info(PG_FUNCTION_ARGS)
Forward declaration of the C SQL entry points.
static Query * build_inner_for_distinct_key(Query *q, Expr *key_expr, List *groupby_tes)
Build the inner GROUP-BY subquery for one AGG(DISTINCT key).
static Expr * wrap_random_variable_uuid(Node *operand, const constants_t *constants)
Wrap an expression returning random_variable in a binary-coercible cast to uuid.
int provsql_rv_mc_samples
Default sample count for analytical-evaluator MC fallbacks; 0 disables fallback (callers raise instea...
static Query * process_query(const constants_t *constants, Query *q, bool **removed, bool wrap_root)
Rewrite a single SELECT query to carry provenance.
static Node * add_to_havingQual(Node *havingQual, Expr *expr)
Append expr to havingQual with an AND, creating one if needed.
static void fix_type_of_aggregation_result(const constants_t *constants, Query *q, Index rteid, List *targetList)
Retypes aggregation-result Vars in q from UUID to agg_token.
static Expr * wrap_in_assume_boolean(const constants_t *constants, Expr *expr)
Wrap expr in a provsql.assume_boolean FuncExpr.
static void provsql_executor_end(QueryDesc *queryDesc)
static void process_set_operation_union(const constants_t *constants, SetOperationStmt *stmt, Query *q)
Recursively annotate a UNION tree with the provenance UUID type.
static bool having_lift_walker(Node *node, void *data)
Walker for needs_having_lift: detect any operand shape that the HAVING-lift rewriter (having_OpExpr_t...
static Node * wrap_agg_token_with_cast(FuncExpr *prov_agg, const constants_t *constants)
Wrap a provenance_aggregate FuncExpr with a cast to the original aggregate return type.
static void replace_aggregations_by_provenance_aggregate(const constants_t *constants, Query *q, List *prov_atts, semiring_operation op)
Replace every Aggref in q with a provenance-aware aggregate.
static Var * make_provenance_attribute(const constants_t *constants, Query *q, RangeTblEntry *r, Index relid, AttrNumber attid)
Build a Var node that references the provenance column of a relation.
static void provsql_executor_start(QueryDesc *queryDesc, int eflags)
static void remove_provenance_attribute_setoperations(Query *q, bool *removed)
Strip the provenance column's type info from a set-operation node.
static void add_to_select(Query *q, Expr *provenance)
Append the provenance expression to q's target list.
static void inline_ctes(Query *q)
Inline all CTE references in q as subqueries.
void _PG_fini(void)
Extension teardown — restores the planner and shmem hooks.
static Node * provenance_mutator(Node *node, void *ctx)
Tree-mutator that replaces provenance() calls with the actual provenance expression.
static Node * aggregation_type_mutator(Node *node, void *ctx)
Tree-mutator that retypes a specific Var to agg_token.
int provsql_monte_carlo_seed
Seed for the Monte Carlo sampler; -1 means non-deterministic (std::random_device); controlled by the ...
static void provsql_ProcessUtility_apply(Node *parsetree, ProvSQLCtasCapture *cap)
Apply cap to the freshly-created relation stmt->into->rel.
static List * migrate_probabilistic_quals(const constants_t *constants, Query *q)
Unified WHERE classifier – routes each top-level conjunct to the right evaluation site in a single pa...
static void cast_agg_token_in_list(ListCell *lc, insert_agg_token_casts_context *ctx)
Wrap an agg_token Var in a cast to its original type, in place.
bool provsql_cmp_probability_evaluation
Run closed-form / analytic probability evaluators for gate_cmps inside probability_evaluate (currentl...
static Node * cast_agg_token_mutator(Node *node, void *ctx)
Tree-mutator that casts provenance_aggregate results back to the original aggregate return type where...
char * provsql_tool_search_path
Colon-separated directory list prepended to PATH when invoking external tools (d4,...
static Query * rewrite_agg_distinct(Query *q, const constants_t *constants)
Rewrite every AGG(DISTINCT key) in q using independent subqueries.
bool provsql_interrupted
Global variable that becomes true if this particular backend received an interrupt signal.
static Expr * combine_prov_atts(const constants_t *constants, List *prov_atts, semiring_operation op)
Build the per-row provenance token for an aggregate rewrite.
bool provsql_boolean_provenance
Opt-in safe-query optimisation: when true, rewrites hierarchical conjunctive queries to a read-once f...
static FuncExpr * rv_BoolExpr_to_provenance(BoolExpr *be, const constants_t *constants, bool negated)
Convert a Boolean combination of RV comparisons into a provenance_times / provenance_plus expression.
static void inline_ctes_in_rtable(List *rtable, List *cteList)
Inline CTE references as subqueries within a query.
static Expr * add_eq_from_Quals_to_Expr(const constants_t *constants, Node *quals, Expr *result, int **columns)
Walk a join-condition or WHERE quals node and add eq gates for every equality it contains.
static Query * rewrite_non_all_into_external_group_by(Query *q)
Wrap a non-ALL set operation in an outer GROUP BY query.
static PlannedStmt * provsql_planner(Query *q, int cursorOptions, ParamListInfo boundParams)
static ExecutorStart_hook_type prev_ExecutorStart
static bool provsql_active
true while ProvSQL query rewriting is enabled
static bool provenance_function_in_group_by(const constants_t *constants, Query *q)
Check whether a provenance() call appears in the GROUP BY list.
static void provsql_ProcessUtility(PlannedStmt *pstmt, const char *queryString, ProcessUtilityContext context, ParamListInfo params, QueryEnvironment *queryEnv, DestReceiver *dest, char *completionTag)
static Bitmapset * remove_provenance_attributes_select(const constants_t *constants, Query *q, bool **removed)
Strip provenance UUID columns from q's SELECT list.
static Expr * make_aggregation_expression(const constants_t *constants, Aggref *agg_ref, List *prov_atts, semiring_operation op)
Build the provenance expression for a single aggregate function.
static Expr * add_eq_from_OpExpr_to_Expr(const constants_t *constants, OpExpr *fromOpExpr, Expr *toExpr, int **columns)
Wrap toExpr in a provenance_eq gate if fromOpExpr is an equality between two tracked columns.
static FuncExpr * rv_Expr_to_provenance(Expr *expr, const constants_t *constants, bool negated)
Dispatch a WHERE sub-expression to the appropriate RV converter.
static planner_hook_type prev_planner
Previous planner hook (chained).
static bool check_boolexpr_on_aggregate(BoolExpr *be, const constants_t *constants)
Check whether every leaf of a Boolean expression is a supported comparison on an aggregate result.
static void process_insert_select(const constants_t *constants, Query *q)
Propagate provenance through INSERT ... SELECT.
static bool expr_contains_rv_cmp(Node *node, const constants_t *constants)
Test whether an Expr (sub-)tree contains any RV comparison.
static Oid get_agg_token_orig_type(Var *v, insert_agg_token_casts_context *ctx)
Look up the original aggregate return type for an agg_token Var.
static void cast_agg_token_args(List *args, insert_agg_token_casts_context *ctx)
Wrap any agg_token Vars in an argument list.
static Query * build_outer_for_distinct_key(TargetEntry *orig_agg_te, Query *inner, int n_gb, const constants_t *constants)
Wrap inner in an outer query that applies the original aggregate.
static int provsql_executor_depth
PostgreSQL planner hook — entry point for provenance rewriting.
static ProcessUtility_hook_type prev_ProcessUtility
static void build_column_map(Query *q, int **columns, int *nbcols)
Build the per-RTE column-numbering map used by where-provenance.
static Node * insert_agg_token_casts_mutator(Node *node, void *data)
Insert agg_token casts for Vars used in expressions.
bool provsql_hybrid_evaluation
Run the hybrid-evaluator simplifier inside probability_evaluate; controlled by the provsql....
static bool provsql_update_provenance
true when provenance tracking for DML is enabled
static bool check_expr_on_aggregate(Expr *expr, const constants_t *constants)
Top-level dispatcher for supported WHERE-on-aggregate patterns.
static bool provenance_function_walker(Node *node, void *data)
Tree walker that returns true if any provenance() call is found.
semiring_operation
Semiring operation used to combine provenance tokens.
@ SR_PLUS
Semiring addition (UNION, SELECT DISTINCT).
@ SR_TIMES
Semiring multiplication (JOIN, Cartesian product).
@ SR_MONUS
Semiring monus / set difference (EXCEPT).
static bool needs_having_lift(Node *havingQual, const constants_t *constants)
Return true if havingQual contains anything the HAVING-lift path needs to handle (an agg_token Var or...
static Node * aggregation_mutator(Node *node, void *ctx)
Tree-mutator that replaces Aggrefs with provenance-aware aggregates.
static bool expr_contains_aggref_walker(Node *node, void *context)
expression_tree_walker predicate: returns true on the first Aggref it encounters.
static ExecutorEnd_hook_type prev_ExecutorEnd
bool provsql_aggtoken_text_as_uuid
When true, agg_token::text emits the underlying provenance UUID instead of "value (*)".
static Expr * make_provenance_expression(const constants_t *constants, Query *q, List *prov_atts, bool aggregation, bool group_by_rewrite, semiring_operation op, int **columns, int nbcols, bool wrap_assumed_boolean)
Build the combined provenance expression to be added to the SELECT list.
static FuncExpr * having_OpExpr_to_provenance_cmp(OpExpr *opExpr, const constants_t *constants, bool negated)
Convert a comparison OpExpr on aggregate results into a provenance_cmp gate expression.
static void add_select_non_zero(const constants_t *constants, Query *q, Expr *provsql)
Add a WHERE condition filtering out zero-provenance tuples.
static void reduce_varattno_by_offset(List *targetList, Index varno, int *offset)
Adjust Var attribute numbers in targetList after columns are removed.
qual_class
Categorisation of a top-level WHERE conjunct.
@ QUAL_MIXED_RV_DET
random_variable mixed with non-RV leaves; error
@ QUAL_PURE_AGG
pure agg_token expression; route to HAVING
@ QUAL_DETERMINISTIC
no probabilistic value; stays in WHERE
@ QUAL_MIXED_AGG_DET
agg_token mixed with non-agg leaves; error
@ QUAL_MIXED_AGG_RV
agg_token and random_variable in the same expr; error
@ QUAL_PURE_RV
pure random_variable expression; lift to provenance
static FuncExpr * rv_OpExpr_to_provenance_cmp(OpExpr *opExpr, const constants_t *constants, bool negated)
Convert a single RV-comparison OpExpr into a provenance_cmp() FuncExpr returning UUID.
static Expr * make_rv_aggregate_expression(const constants_t *constants, Aggref *agg_ref, List *prov_atts, semiring_operation op)
Inline rewrite of an RV-returning aggregate into the same aggregate over provenance-wrapped per-row a...
static void error_for_mixed_qual(qual_class c)
Raise the user-facing error appropriate to a mixed c.
static bool has_provenance(const constants_t *constants, Query *q)
Return true if q involves any provenance-bearing relation or contains an explicit provenance() call.
#define provsql_error(fmt,...)
Report a fatal ProvSQL error and abort the current transaction.
#define provsql_warning(fmt,...)
Emit a ProvSQL warning message (execution continues).
#define provsql_notice(fmt,...)
Emit a ProvSQL informational notice (execution continues).
void RegisterProvSQLMMapWorker(void)
Register the ProvSQL mmap background worker with PostgreSQL.
Background worker and IPC primitives for mmap-backed circuit storage.
void provsql_shmem_request(void)
Request shared memory from PostgreSQL (PG ≥ 15).
shmem_startup_hook_type prev_shmem_startup
Saved pointer to the previous shmem_startup_hook, for chaining.
void provsql_shmem_startup(void)
Initialise the ProvSQL shared-memory segment.
Shared-memory segment and inter-process pipe management.
shmem_request_hook_type prev_shmem_request
Saved pointer to the previous shmem_request_hook (PG ≥ 15), for chaining.
Oid find_equality_operator(Oid ltypeId, Oid rtypeId)
Find the equality operator OID for two given types.
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.
constants_t get_constants(bool failure_if_not_possible)
Retrieve the cached OID constants for the current database.
Core types, constants, and utilities shared across ProvSQL.
#define PROVSQL_COLUMN_NAME
Canonical name of the per-row provenance column installed by add_provenance / repair_key.
Query * try_safe_query_rewrite(const constants_t *constants, Query *q)
Top-level entry point for the safe-query rewrite.
Public surface of the safe-query (hierarchical-CQ) rewriter.
void strip_group_rte_pg18(Query *q)
PG 18 helper: strip the synthetic RTE_GROUP entry from q in place, resolving every grouped Var back t...
Result of provsql_classify_query.
State captured by the pre-execution pass for the post-execution one.
provsql_table_kind inherited_kind
bool fire
true when the post-pass should run
uint16 source_block_key_n
Oid ancestors[PROVSQL_TABLE_INFO_MAX_ANCESTORS]
AttrNumber source_block_key[PROVSQL_TABLE_INFO_MAX_BLOCK_KEY]
Query * inner_query
cloned for safety; freed by pfree on completion
Oid source_relid
Single source whose block_key we want to align (BID only).
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.
Context for the aggregation_mutator tree walker.
semiring_operation op
Semiring operation for combining tokens.
const constants_t * constants
Extension OID cache.
List * prov_atts
List of provenance Var nodes.
Context for the aggregation_type_mutator tree walker.
const constants_t * constants
Extension OID cache.
Index varattno
Attribute number of the aggregate column.
Index varno
Range-table entry index of the aggregate var.
Structure to store the value of various constants.
Oid OID_FUNCTION_PROVENANCE_EQ
OID of the provenance_eq FUNCTION.
Oid OID_FUNCTION_PROVENANCE_AGGREGATE
OID of the provenance_aggregate FUNCTION.
Oid OID_FUNCTION_PROVENANCE_SEMIMOD
OID of the provenance_semimod FUNCTION.
Oid OID_FUNCTION_PROVENANCE
OID of the provenance FUNCTION.
Oid OID_FUNCTION_AGG_TOKEN_UUID
OID of the agg_token_uuid FUNCTION.
Oid OID_FUNCTION_RV_AGGREGATE_SEMIMOD
OID of rv_aggregate_semimod helper (uuid, rv -> rv) used to wrap each per-row argument of an RV-retur...
Oid OID_FUNCTION_GATE_ZERO
OID of the provenance_zero FUNCTION.
Oid OID_FUNCTION_PROVENANCE_PROJECT
OID of the provenance_project FUNCTION.
Oid OID_TYPE_AGG_TOKEN
OID of the agg_token TYPE.
Oid OID_FUNCTION_ARRAY_AGG
OID of the array_agg FUNCTION.
Oid OID_TYPE_INT
OID of the INT TYPE.
Oid OID_FUNCTION_PROVENANCE_PLUS
OID of the provenance_plus FUNCTION.
Oid OID_OPERATOR_NOT_EQUAL_UUID
OID of the <> operator on UUIDs FUNCTION.
Oid OID_TYPE_UUID
OID of the uuid TYPE.
bool ok
true if constants were loaded
Oid OID_TYPE_INT_ARRAY
OID of the INT[] TYPE.
Oid OID_FUNCTION_PROVENANCE_DELTA
OID of the provenance_delta FUNCTION.
Oid OID_FUNCTION_ASSUME_BOOLEAN
OID of provsql.assume_boolean(uuid)->uuid.
Oid OID_FUNCTION_PROVENANCE_TIMES
OID of the provenance_times FUNCTION.
Oid OID_FUNCTION_PROVENANCE_MONUS
OID of the provenance_monus FUNCTION.
Oid OID_FUNCTION_NOT_EQUAL_UUID
OID of the = operator on UUIDs FUNCTION.
Oid OID_FUNCTION_GATE_ONE
OID of the provenance_one FUNCTION.
Oid OID_TYPE_UUID_ARRAY
OID of the uuid[] TYPE.
Oid OID_TYPE_RANDOM_VARIABLE
OID of the random_variable TYPE.
Oid OID_FUNCTION_PROVENANCE_CMP
OID of the provenance_cmp FUNCTION.
Oid OID_FUNCTION_RV_CMP[6]
OIDs of the random_variable_{eq,ne,le,lt,ge,gt} comparison procedure functions, indexed by the Compar...
Context for the insert_agg_token_casts_mutator.
const constants_t * constants
Extension OID cache.
Query * query
Outer query (to look up subquery RTEs).
Context for the provenance_mutator tree walker.
bool provsql_has_aggref
true when provsql contains an Aggref (set once by replace_provenance_function_by_expression)....
bool inside_aggref
true while descending the argument tree of an Aggref node.
const constants_t * constants
Extension OID cache.
Expr * provsql
Provenance expression to substitute for provenance() calls.
Context for the reduce_varattno_mutator tree walker.
Index varno
Range-table entry whose attribute numbers are being adjusted.
int * offset
Per-attribute cumulative shift to apply.