25#include "access/htup_details.h"
26#include "access/sysattr.h"
27#include "catalog/pg_aggregate.h"
28#include "catalog/pg_collation.h"
29#include "catalog/pg_operator.h"
30#include "catalog/pg_type.h"
31#include "nodes/makefuncs.h"
32#include "nodes/nodeFuncs.h"
33#include "nodes/print.h"
34#include "optimizer/planner.h"
35#include "parser/parse_oper.h"
36#include "parser/parsetree.h"
37#include "storage/lwlock.h"
38#include "storage/shmem.h"
39#include "utils/fmgroids.h"
41#include "utils/lsyscache.h"
42#include "utils/ruleutils.h"
43#include "utils/syscache.h"
50#if PG_VERSION_NUM < 100000
51#error "ProvSQL requires PostgreSQL version 10 or later"
97 RangeTblEntry *r, Index relid,
99 Var *v = makeNode(Var);
104#if PG_VERSION_NUM >= 130000
106 v->varattnosyn = attid;
109 v->varoattno = attid;
113 v->varcollid = InvalidOid;
117#if PG_VERSION_NUM >= 160000
118 if (r->perminfoindex != 0) {
119 RTEPermissionInfo *rpi =
120 list_nth_node(RTEPermissionInfo, q->rteperminfos, r->perminfoindex - 1);
121 rpi->selectedCols = bms_add_member(
122 rpi->selectedCols, attid - FirstLowInvalidHeapAttributeNumber);
125 r->selectedCols = bms_add_member(r->selectedCols,
126 attid - FirstLowInvalidHeapAttributeNumber);
153 if (IsA(node, Var)) {
154 Var *v = (Var *)node;
156 if (v->varno == context->
varno) {
157 v->varattno += context->
offset[v->varattno - 1];
182 foreach (lc, targetList) {
183 Node *te = lfirst(lc);
207 if (IsA(node, Var)) {
208 Var *v = (Var *)node;
210 if (v->varno == context->
varno && v->varattno == context->
varattno) {
232 Query *q, Index rteid,
238 foreach (lc, targetList) {
239 TargetEntry *te = (TargetEntry *)lfirst(lc);
240 if (IsA(te->expr, FuncExpr)) {
241 FuncExpr *f = (FuncExpr *)te->expr;
244 context.
varno = rteid;
247 QTW_DONT_COPY_QUERY | QTW_IGNORE_RC_SUBQUERIES);
274 List *prov_atts = NIL;
276 for(Index rteid = 1; rteid <= q->rtable->length; ++rteid) {
277 RangeTblEntry *r = list_nth_node(RangeTblEntry, q->rtable, rteid-1);
279 if (r->rtekind == RTE_RELATION) {
281 AttrNumber attid = 1;
283 foreach (lc, r->eref->colnames) {
284 const char *v = strVal(lfirst(lc));
295 }
else if (r->rtekind == RTE_SUBQUERY) {
296 bool *inner_removed = NULL;
297 int old_targetlist_length =
298 r->subquery->targetList ? r->subquery->targetList->length : 0;
299 Query *new_subquery =
301 if (new_subquery != NULL) {
303 int *offset = (
int *)palloc(old_targetlist_length *
sizeof(
int));
304 unsigned varattnoprovsql;
305 ListCell *cell, *prev;
307 r->subquery = new_subquery;
309 if (inner_removed != NULL) {
310 for (cell = list_head(r->eref->colnames), prev = NULL;
312 if (inner_removed[i]) {
316 cell =
my_lnext(r->eref->colnames, prev);
318 cell = list_head(r->eref->colnames);
321 cell =
my_lnext(r->eref->colnames, cell);
325 for (i = 0; i < old_targetlist_length; ++i) {
327 (i == 0 ? 0 : offset[i - 1]) - (inner_removed[i] ? 1 : 0);
334 for (cell = list_head(new_subquery->targetList); cell != NULL;
335 cell =
my_lnext(new_subquery->targetList, cell)) {
336 TargetEntry *te = (TargetEntry *)lfirst(cell);
343 r->eref->colnames =
list_insert_nth(r->eref->colnames, varattnoprovsql-1,
347 constants, q, r, rteid, varattnoprovsql));
350 r->subquery->targetList);
352 }
else if (r->rtekind == RTE_JOIN) {
353 if (r->jointype == JOIN_INNER || r->jointype == JOIN_LEFT ||
354 r->jointype == JOIN_FULL || r->jointype == JOIN_RIGHT) {
363 }
else if (r->rtekind == RTE_FUNCTION) {
365 AttrNumber attid = 1;
367 foreach (lc, r->functions) {
368 RangeTblFunction *func = (RangeTblFunction *)lfirst(lc);
370 if (func->funccolcount == 1) {
371 FuncExpr *expr = (FuncExpr *)func->funcexpr;
375 constants, q, r, rteid, attid));
379 "attributes not supported by provsql");
382 attid += func->funccolcount;
384 }
else if (r->rtekind == RTE_VALUES) {
386#if PG_VERSION_NUM >= 180000
387 }
else if (r->rtekind == RTE_GROUP) {
424 Bitmapset *ressortgrouprefs = NULL;
425 ListCell *cell, *prev;
426 *removed = (
bool *)palloc(q->targetList->length *
sizeof(
bool));
428 for (cell = list_head(q->targetList), prev = NULL; cell != NULL;) {
429 TargetEntry *rt = (TargetEntry *)lfirst(cell);
430 (*removed)[i] =
false;
432 if (rt->expr->type == T_Var) {
433 Var *v = (Var *)rt->expr;
439 colname = rt->resname;
443 RangeTblEntry *r = (RangeTblEntry *)list_nth(q->rtable, v->varno - 1);
444 colname = strVal(list_nth(r->eref->colnames, v->varattno - 1));
450 (*removed)[i] =
true;
453 if (rt->ressortgroupref > 0)
455 bms_add_member(ressortgrouprefs, rt->ressortgroupref);
462 cell =
my_lnext(q->targetList, prev);
464 cell = list_head(q->targetList);
467 rt->resno -= nbRemoved;
469 cell =
my_lnext(q->targetList, cell);
475 return ressortgrouprefs;
514 OpExpr *fromOpExpr, Expr *toExpr,
524 if (
my_lnext(fromOpExpr->args, list_head(fromOpExpr->args))) {
526 if (IsA(linitial(fromOpExpr->args), Var)) {
527 v1 = linitial(fromOpExpr->args);
528 }
else if (IsA(linitial(fromOpExpr->args), RelabelType)) {
530 RelabelType *rt1 = linitial(fromOpExpr->args);
531 if (IsA(rt1->arg, Var)) {
532 v1 = (Var *)rt1->arg;
537 first_arg = Int16GetDatum(columns[v1->varno - 1][v1->varattno - 1]);
539 if (IsA(lsecond(fromOpExpr->args), Var)) {
540 v2 = lsecond(fromOpExpr->args);
541 }
else if (IsA(lsecond(fromOpExpr->args), RelabelType)) {
543 RelabelType *rt2 = lsecond(fromOpExpr->args);
544 if (IsA(rt2->arg, Var)) {
545 v2 = (Var *)rt2->arg;
550 second_arg = Int16GetDatum(columns[v2->varno - 1][v2->varattno - 1]);
552 fc = makeNode(FuncExpr);
554 fc->funcvariadic =
false;
558 c1 = makeConst(constants->
OID_TYPE_INT, -1, InvalidOid,
sizeof(int16),
559 first_arg,
false,
true);
561 c2 = makeConst(constants->
OID_TYPE_INT, -1, InvalidOid,
sizeof(int16),
562 second_arg,
false,
true);
564 fc->args = list_make3(toExpr, c1, c2);
586 Node *quals, Expr *result,
593 if (IsA(quals, OpExpr)) {
594 oe = (OpExpr *)quals;
597 else if (IsA(quals, BoolExpr)) {
598 BoolExpr *be = (BoolExpr *)quals;
600 if (be->boolop == OR_EXPR || be->boolop == NOT_EXPR) {
602 "clause are not supported by provsql");
605 foreach (lc2, be->args) {
606 if (IsA(lfirst(lc2), OpExpr)) {
607 oe = (OpExpr *)lfirst(lc2);
637 Aggref *agg_ref, List *prov_atts,
640 FuncExpr *expr, *expr_s;
641 Aggref *agg = makeNode(Aggref);
642 FuncExpr *plus = makeNode(FuncExpr);
643 TargetEntry *te_inner = makeNode(TargetEntry);
644 Const *fn = makeNode(Const);
645 Const *typ = makeNode(Const);
648 result = linitial(prov_atts);
650 Oid aggregation_function = agg_ref->aggfnoid;
652 if (
my_lnext(prov_atts, list_head(prov_atts)) == NULL)
653 expr = linitial(prov_atts);
655 expr = makeNode(FuncExpr);
657 ArrayExpr *array = makeNode(ArrayExpr);
660 expr->funcvariadic =
true;
664 array->elements = prov_atts;
665 array->location = -1;
667 expr->args = list_make1(array);
670 expr->args = prov_atts;
677 expr_s = makeNode(FuncExpr);
682 if (aggregation_function ==
F_COUNT_ ||
685 Const *one = makeConst(constants->
OID_TYPE_INT, -1, InvalidOid,
686 sizeof(int32), Int32GetDatum(1),
false,
true);
687 expr_s->args = list_make2(one, expr);
691 list_make2(((TargetEntry *)linitial(agg_ref->args))->expr, expr);
694 expr_s->location = -1;
698 te_inner->expr = (Expr *)expr_s;
701 agg->args = list_make1(te_inner);
702 agg->aggkind = AGGKIND_NORMAL;
704#if PG_VERSION_NUM >= 140000
705 agg->aggno = agg->aggtransno = -1;
713 fn = makeConst(constants->
OID_TYPE_INT, -1, InvalidOid,
sizeof(int32),
714 Int32GetDatum(aggregation_function),
false,
true);
716 typ = makeConst(constants->
OID_TYPE_INT, -1, InvalidOid,
sizeof(int32),
717 Int32GetDatum(agg_ref->aggtype),
false,
true);
720 plus->args = list_make4(fn, typ, agg_ref, agg);
723 result = (Expr *)plus;
758 Oid opno = opExpr->opno;
760 for (
unsigned i = 0; i < 2; ++i) {
761 Node *node = (Node *)lfirst(list_nth_cell(opExpr->args, i));
763 if (IsA(node, FuncExpr)) {
764 FuncExpr *fe = (FuncExpr *)node;
765 if (fe->funcformat == COERCE_IMPLICIT_CAST ||
766 fe->funcformat == COERCE_EXPLICIT_CAST) {
767 if (fe->args->length == 1)
768 node = lfirst(list_head(fe->args));
772 if (IsA(node, FuncExpr)) {
773 FuncExpr *fe = (FuncExpr *)node;
776 FuncExpr *castToUUID = makeNode(FuncExpr);
780 castToUUID->args = list_make1(fe);
781 castToUUID->location = -1;
783 arguments[i] = (Node *)castToUUID;
787 }
else if (IsA(node, Var)) {
788 Var *v = (Var *)node;
792 FuncExpr *castToUUID = makeNode(FuncExpr);
796 castToUUID->args = list_make1(v);
797 castToUUID->location = -1;
799 arguments[i] = (Node *)castToUUID;
803 }
else if (IsA(node, Const)) {
804 Const *literal = (Const *)node;
805 FuncExpr *oneExpr, *semimodExpr;
808 oneExpr = makeNode(FuncExpr);
812 oneExpr->location = -1;
815 semimodExpr = makeNode(FuncExpr);
818 semimodExpr->args = list_make2((Expr *)literal, (Expr *)oneExpr);
819 semimodExpr->location = -1;
821 arguments[i] = (Node *)semimodExpr;
828 opno = get_negator(opno);
833 oid = makeConst(constants->
OID_TYPE_INT, -1, InvalidOid,
sizeof(int32),
834 Int32GetDatum(opno),
false,
true);
836 cmpExpr = makeNode(FuncExpr);
839 cmpExpr->args = list_make3(arguments[0], oid, arguments[1]);
840 cmpExpr->location = opExpr->location;
859 if(be->boolop == NOT_EXPR) {
860 Expr *expr = (Expr *) lfirst(list_head(be->args));
866 ArrayExpr *array = makeNode(ArrayExpr);
870 array->location = -1;
872 result = makeNode(FuncExpr);
874 result->funcvariadic =
true;
875 result->location = be->location;
876 result->args = list_make1(array);
878 if ((be->boolop == AND_EXPR && !negated) || (be->boolop == OR_EXPR && negated))
880 else if ((be->boolop == AND_EXPR && negated) || (be->boolop == OR_EXPR && !negated))
885 foreach (lc, be->args) {
886 Expr *expr = (Expr *)lfirst(lc);
910 if (IsA(expr, BoolExpr))
912 else if (IsA(expr, OpExpr))
915 provsql_error(
"Unknown structure within Boolean expression");
949 List *prov_atts,
bool aggregation,
950 bool group_by_rewrite,
957 result = linitial(prov_atts);
959 if (
my_lnext(prov_atts, list_head(prov_atts)) == NULL) {
960 result = linitial(prov_atts);
962 FuncExpr *expr = makeNode(FuncExpr);
964 ArrayExpr *array = makeNode(ArrayExpr);
967 expr->funcvariadic =
true;
971 array->elements = prov_atts;
972 array->location = -1;
974 expr->args = list_make1(array);
977 expr->args = prov_atts;
982 result = (Expr *)expr;
985 if (group_by_rewrite || aggregation) {
986 Aggref *agg = makeNode(Aggref);
987 FuncExpr *plus = makeNode(FuncExpr);
988 TargetEntry *te_inner = makeNode(TargetEntry);
993 te_inner->expr = (Expr *)result;
997 agg->args = list_make1(te_inner);
998 agg->aggkind = AGGKIND_NORMAL;
1000#if PG_VERSION_NUM >= 140000
1001 agg->aggno = agg->aggtransno = -1;
1004 agg->aggargtypes = list_make1_oid(constants->
OID_TYPE_UUID);
1007 plus->args = list_make1(agg);
1009 plus->location = -1;
1011 result = (Expr *)plus;
1014 if (aggregation && !q->havingQual) {
1015 FuncExpr *deltaExpr = makeNode(FuncExpr);
1019 deltaExpr->args = list_make1(result);
1021 deltaExpr->location = -1;
1023 result = (Expr *)deltaExpr;
1026 if (q->havingQual) {
1028 q->havingQual = NULL;
1037 foreach (lc, q->jointree->fromlist) {
1038 if (IsA(lfirst(lc), JoinExpr)) {
1039 JoinExpr *je = (JoinExpr *)lfirst(lc);
1051 ArrayExpr *array = makeNode(ArrayExpr);
1052 FuncExpr *fe = makeNode(FuncExpr);
1053 bool projection =
false;
1057 fe->funcvariadic =
true;
1063 array->elements = NIL;
1064 array->location = -1;
1066 foreach (lc_v, q->targetList) {
1067 TargetEntry *te_v = (TargetEntry *)lfirst(lc_v);
1068 if (IsA(te_v->expr, Var)) {
1069 Var *vte_v = (Var *)te_v->expr;
1070 RangeTblEntry *rte_v =
1071 (RangeTblEntry *)lfirst(list_nth_cell(q->rtable, vte_v->varno - 1));
1073#if PG_VERSION_NUM >= 180000
1074 if (rte_v->rtekind == RTE_GROUP) {
1075 Expr *ge = lfirst(list_nth_cell(rte_v->groupexprs, vte_v->varattno - 1));
1077 Var *v = (Var *) ge;
1078 value_v = columns[v->varno - 1][v->varattno - 1];
1080 Const *ce = makeConst(constants->
OID_TYPE_INT, -1, InvalidOid,
1081 sizeof(int32), Int32GetDatum(0),
false,
true);
1083 array->elements = lappend(array->elements, ce);
1088 if (rte_v->rtekind != RTE_JOIN) {
1089 value_v = columns[vte_v->varno - 1][vte_v->varattno - 1];
1091 Var *jav_v = (Var *)lfirst(
1092 list_nth_cell(rte_v->joinaliasvars, vte_v->varattno - 1));
1093 value_v = columns[jav_v->varno - 1][jav_v->varattno - 1];
1099 makeConst(constants->
OID_TYPE_INT, -1, InvalidOid,
sizeof(int32),
1100 Int32GetDatum(value_v),
false,
true);
1102 array->elements = lappend(array->elements, ce);
1104 if (value_v != ++nb_column)
1111 Const *ce = makeConst(constants->
OID_TYPE_INT, -1, InvalidOid,
1112 sizeof(int32), Int32GetDatum(0),
false,
true);
1114 array->elements = lappend(array->elements, ce);
1119 if (nb_column != nbcols)
1123 fe->args = list_make2(result, array);
1124 result = (Expr *)fe;
1138#if PG_VERSION_NUM >= 180000
1140 Index group_rtindex;
1142} resolve_group_rte_ctx;
1145resolve_group_rte_vars_mutator(Node *node, resolve_group_rte_ctx *ctx) {
1148 if (IsA(node, Var)) {
1149 Var *v = (Var *)node;
1150 if (v->varno == ctx->group_rtindex) {
1151 Node *resolved = copyObject(list_nth(ctx->groupexprs, v->varattno - 1));
1157 if (IsA(resolved, Var))
1158 ((Var *)resolved)->varnullingrels = NULL;
1162 return expression_tree_mutator(node, resolve_group_rte_vars_mutator,
1183 List *groupby_tes) {
1188 int resno = 1, sgref = 1;
1190 inner = copyObject(q);
1192 inner->hasAggs =
false;
1193 inner->sortClause = NIL;
1194 inner->limitCount = NULL;
1195 inner->limitOffset = NULL;
1196 inner->distinctClause = NIL;
1197 inner->hasDistinctOn =
false;
1198 inner->havingQual = NULL;
1202 TargetEntry *kte = makeNode(TargetEntry);
1203 SortGroupClause *sgc = makeNode(SortGroupClause);
1205 kte->expr = copyObject(key_expr);
1206 kte->resno = resno++;
1207 kte->resname =
"key";
1208 sgc->tleSortGroupRef = kte->ressortgroupref = sgref++;
1209 get_sort_group_operators(exprType((Node *)kte->expr),
true,
true,
false,
1210 &sgc->sortop, &sgc->eqop, NULL, &sgc->hashable);
1211 new_gc = list_make1(sgc);
1212 new_tl = list_make1(kte);
1216 foreach (lc, groupby_tes) {
1217 TargetEntry *gyte = copyObject((TargetEntry *)lfirst(lc));
1218 SortGroupClause *sgc = makeNode(SortGroupClause);
1220 gyte->resno = resno++;
1221 gyte->resjunk =
false;
1222 sgc->tleSortGroupRef = gyte->ressortgroupref = sgref++;
1223 get_sort_group_operators(exprType((Node *)gyte->expr),
true,
true,
false,
1224 &sgc->sortop, &sgc->eqop, NULL, &sgc->hashable);
1225 new_gc = lappend(new_gc, sgc);
1226 new_tl = lappend(new_tl, gyte);
1229 inner->targetList = new_tl;
1230 inner->groupClause = new_gc;
1253 Query *inner,
int n_gb,
1255 Query *outer = makeNode(Query);
1256 RangeTblEntry *rte = makeNode(RangeTblEntry);
1257 Alias *alias = makeNode(Alias), *eref = makeNode(Alias);
1258 RangeTblRef *rtr = makeNode(RangeTblRef);
1259 FromExpr *jt = makeNode(FromExpr);
1260 List *new_tl = NIL, *new_gc = NIL;
1262 int resno = 1, sgref = 1;
1263 int inner_len = list_length(inner->targetList);
1267 alias->aliasname = eref->aliasname =
"d";
1268 eref->colnames = NIL;
1269 foreach (lc, inner->targetList) {
1270 TargetEntry *te = lfirst(lc);
1271 eref->colnames = lappend(eref->colnames,
1272 makeString(te->resname ? pstrdup(te->resname) :
""));
1276 rte->rtekind = RTE_SUBQUERY;
1277 rte->subquery = inner;
1278 rte->inFromCl =
true;
1279#if PG_VERSION_NUM < 160000
1280 rte->requiredPerms = ACL_SELECT;
1284 jt->fromlist = list_make1(rtr);
1286 outer->commandType = CMD_SELECT;
1287 outer->canSetTag =
true;
1288 outer->rtable = list_make1(rte);
1289 outer->jointree = jt;
1290 outer->hasAggs =
true;
1294 TargetEntry *agg_te = copyObject(orig_agg_te);
1295 Aggref *ar = (Aggref *)agg_te->expr;
1296 Var *key_var = makeNode(Var);
1297 TargetEntry *arg_te = makeNode(TargetEntry);
1300 key_var->varattno = 1;
1301 key_var->vartype = linitial_oid(ar->aggargtypes);
1302 key_var->varcollid = exprCollation((Node *)((TargetEntry *)linitial(ar->args))->expr);
1303 key_var->vartypmod = -1;
1304 key_var->location = -1;
1306 arg_te->expr = (Expr *)key_var;
1308 ar->args = list_make1(arg_te);
1309 ar->aggdistinct = NIL;
1310 agg_te->resno = resno++;
1311 new_tl = list_make1(agg_te);
1315 for (attno = inner_len - n_gb + 1; attno <= inner_len; attno++) {
1316 TargetEntry *inner_te = list_nth(inner->targetList, attno - 1);
1317 Var *gb_var = makeNode(Var);
1318 TargetEntry *gb_te = makeNode(TargetEntry);
1319 SortGroupClause *sgc = makeNode(SortGroupClause);
1322 gb_var->varattno = attno;
1323 gb_var->vartype = exprType((Node *)inner_te->expr);
1324 gb_var->varcollid = exprCollation((Node *)inner_te->expr);
1325 gb_var->vartypmod = -1;
1326 gb_var->location = -1;
1328 gb_te->resno = resno++;
1329 gb_te->expr = (Expr *)gb_var;
1330 gb_te->resname = inner_te->resname;
1332 sgc->tleSortGroupRef = gb_te->ressortgroupref = sgref++;
1333 sgc->nulls_first =
false;
1334 get_sort_group_operators(gb_var->vartype,
true,
true,
false,
1335 &sgc->sortop, &sgc->eqop, NULL, &sgc->hashable);
1336 new_gc = lappend(new_gc, sgc);
1337 new_tl = lappend(new_tl, gb_te);
1340 outer->targetList = new_tl;
1341 outer->groupClause = new_gc;
1361 List *distinct_agg_tes = NIL;
1362 List *groupby_tes = NIL;
1365#if PG_VERSION_NUM >= 180000
1379 if (q->hasGroupRTE) {
1380 resolve_group_rte_ctx grp_ctx;
1386 foreach (lc2, q->rtable) {
1387 RangeTblEntry *r = (RangeTblEntry *)lfirst(lc2);
1388 if (r->rtekind == RTE_GROUP) {
1389 grp_ctx.group_rtindex = idx;
1390 grp_ctx.groupexprs = r->groupexprs;
1400 q->rtable = list_truncate(q->rtable, rte_len);
1401 q->hasGroupRTE =
false;
1405 foreach (lc2, q->targetList) {
1406 TargetEntry *te = (TargetEntry *)lfirst(lc2);
1407 te->expr = (Expr *)resolve_group_rte_vars_mutator(
1408 (Node *)te->expr, &grp_ctx);
1410 if (q->jointree && q->jointree->quals)
1411 q->jointree->quals = resolve_group_rte_vars_mutator(
1412 q->jointree->quals, &grp_ctx);
1419 foreach (lc, q->targetList) {
1420 TargetEntry *te = lfirst(lc);
1421 if (IsA(te->expr, Aggref)) {
1422 Aggref *ar = (Aggref *)te->expr;
1423 if (list_length(ar->aggdistinct) > 0)
1424 distinct_agg_tes = lappend(distinct_agg_tes, te);
1427 TargetEntry *te_copy = copyObject(te);
1428 te_copy->resjunk =
false;
1429 groupby_tes = lappend(groupby_tes, te_copy);
1433 if (distinct_agg_tes == NIL)
1437 int n_aggs = list_length(distinct_agg_tes);
1438 int n_gb = list_length(groupby_tes);
1439 List *outer_queries = NIL;
1456 foreach (lc, distinct_agg_tes) {
1457 TargetEntry *agg_te = lfirst(lc);
1458 Aggref *ar = (Aggref *)agg_te->expr;
1459 if(list_length(ar->args) != 1)
1460 provsql_error(
"AGG(DISTINCT) with more than one argument is not supported");
1462 Expr *key_expr = (Expr *)((TargetEntry *)linitial(ar->args))->expr;
1465 outer_queries = lappend(outer_queries, outer);
1472 foreach (lc, outer_queries) {
1473 Query *oq = lfirst(lc);
1474 RangeTblEntry *rte = makeNode(RangeTblEntry);
1475 Alias *alias = makeNode(Alias), *eref = makeNode(Alias);
1479 snprintf(buf,
sizeof(buf),
"d%d", i + 1);
1480 alias->aliasname = eref->aliasname = pstrdup(buf);
1481 eref->colnames = NIL;
1482 foreach (lc2, oq->targetList) {
1483 TargetEntry *te = lfirst(lc2);
1484 eref->colnames = lappend(eref->colnames,
1485 makeString(te->resname ? pstrdup(te->resname) :
""));
1489 rte->rtekind = RTE_SUBQUERY;
1491 rte->inFromCl =
true;
1492#if PG_VERSION_NUM < 160000
1493 rte->requiredPerms = ACL_SELECT;
1495 q->rtable = lappend(q->rtable, rte);
1502 FromExpr *jt = q->jointree;
1503 List *from_list = jt->fromlist;
1504 unsigned fll = list_length(from_list);
1505 List *where_args = NIL;
1507 for (i = fll+1; i <= fll+n_aggs; i++) {
1508 RangeTblRef *rtr = makeNode(RangeTblRef);
1513 from_list = lappend(from_list, rtr);
1516 foreach(lc2, groupby_tes) {
1517 TargetEntry *gb_te = lfirst(lc2);
1518 int gb_attno = ++j + 1;
1519 Oid ytype = exprType((Node *)gb_te->expr);
1521 Operator opInfo = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno));
1522 Form_pg_operator opform;
1523 OpExpr *oe = makeNode(OpExpr);
1524 Expr *le = copyObject(gb_te->expr);
1525 Var *rv = makeNode(Var);
1526 Oid collation=exprCollation((Node*) le);
1528 if (!HeapTupleIsValid(opInfo))
1529 provsql_error(
"could not find equality operator for type %u",
1531 opform = (Form_pg_operator)GETSTRUCT(opInfo);
1534 oe->opfuncid = opform->oprcode;
1535 oe->opresulttype = opform->oprresult;
1536 oe->opcollid = InvalidOid;
1537 oe->inputcollid = collation;
1539 ReleaseSysCache(opInfo);
1541 rv->varno = i; rv->varattno = gb_attno;
1542 rv->vartype = ytype; rv->varcollid = collation;
1543 rv->vartypmod = -1; rv->location = -1;
1545 oe->args = list_make2(le, rv);
1546 where_args = lappend(where_args, oe);
1550 if (list_length(where_args) == 0) {
1552 }
else if (list_length(where_args) == 1) {
1553 jt->quals = linitial(where_args);
1555 BoolExpr *be = makeNode(BoolExpr);
1556 be->boolop = AND_EXPR;
1557 be->args = where_args;
1559 jt->quals = (Node *)be;
1566 int agg_idx = list_length(q->jointree->fromlist) - n_aggs + 1;
1569 foreach (lc2, q->targetList) {
1570 TargetEntry *te = lfirst(lc2);
1572 if (IsA(te->expr, Aggref) &&
1573 ((Aggref *)te->expr)->aggdistinct != NIL) {
1574 Var *v = makeNode(Var);
1575 v->varno = agg_idx++;
1579 te->expr = (Expr*)v;
1611 if (IsA(node, Aggref)) {
1612 Aggref *ar_v = (Aggref *)node;
1635 Query *q, List *prov_atts,
1641 QTW_DONT_COPY_QUERY | QTW_IGNORE_RT_SUBQUERIES);
1655 TargetEntry *newte = makeNode(TargetEntry);
1656 bool inserted =
false;
1663 RangeTblEntry *rte = list_nth(q->rtable, ((Var *)
provenance)->varno - 1);
1664 newte->resorigtbl = rte->relid;
1665 newte->resorigcol = ((Var *)
provenance)->varattno;
1669 for (ListCell *cell = list_head(q->targetList); cell != NULL;) {
1670 TargetEntry *te = (TargetEntry *)lfirst(cell);
1677 newte->resno = resno;
1679 cell = list_nth_cell(q->targetList, resno);
1680 te = (TargetEntry *)lfirst(cell);
1687 cell =
my_lnext(q->targetList, cell);
1691 newte->resno = resno + 1;
1692 q->targetList = lappend(q->targetList, newte);
1717 if (IsA(node, FuncExpr)) {
1718 FuncExpr *f = (FuncExpr *)node;
1721 return (Node *)copyObject(context->
provsql);
1723 }
else if (IsA(node, RangeTblEntry) || IsA(node, RangeTblFunction)) {
1745 Query *q, Expr *provsql) {
1749 QTW_DONT_COPY_QUERY | QTW_IGNORE_RT_SUBQUERIES);
1766 Bitmapset *already_in_group_by = NULL;
1768 foreach (lc, q->groupClause) {
1769 SortGroupClause *sgc = (SortGroupClause *)lfirst(lc);
1770 already_in_group_by =
1771 bms_add_member(already_in_group_by, sgc->tleSortGroupRef);
1774 foreach (lc, q->distinctClause) {
1775 SortGroupClause *sgc = (SortGroupClause *)lfirst(lc);
1776 if (!bms_is_member(sgc->tleSortGroupRef, already_in_group_by)) {
1777 q->groupClause = lappend(q->groupClause, sgc);
1781 q->distinctClause = NULL;
1796 const Bitmapset *removed_sortgrouprefs) {
1797 List **lists[3] = {&q->groupClause, &q->distinctClause, &q->sortClause};
1800 for (i = 0; i < 3; ++i) {
1801 ListCell *cell, *prev;
1803 for (cell = list_head(*lists[i]), prev = NULL; cell != NULL;) {
1804 SortGroupClause *sgc = (SortGroupClause *)lfirst(cell);
1805 if (bms_is_member(sgc->tleSortGroupRef, removed_sortgrouprefs)) {
1811 cell = list_head(*lists[i]);
1833 SetOperationStmt *so = (SetOperationStmt *)q->setOperations;
1834 List **lists[3] = {&so->colTypes, &so->colTypmods, &so->colCollations};
1837 for (i = 0; i < 3; ++i) {
1838 ListCell *cell, *prev;
1841 for (cell = list_head(*lists[i]), prev = NULL, j = 0; cell != NULL; ++j) {
1848 cell = list_head(*lists[i]);
1874 Query *new_query = makeNode(Query);
1875 RangeTblEntry *rte = makeNode(RangeTblEntry);
1876 FromExpr *jointree = makeNode(FromExpr);
1877 RangeTblRef *rtr = makeNode(RangeTblRef);
1879 SetOperationStmt *stmt = (SetOperationStmt *)q->setOperations;
1882 int sortgroupref = 0;
1890 rte->rtekind = RTE_SUBQUERY;
1892 rte->eref = copyObject(((RangeTblEntry *)linitial(q->rtable))->eref);
1893 rte->inFromCl =
true;
1894#if PG_VERSION_NUM < 160000
1897 rte->requiredPerms = ACL_SELECT;
1901 jointree->fromlist = list_make1(rtr);
1903 new_query->commandType = CMD_SELECT;
1904 new_query->canSetTag =
true;
1905 new_query->rtable = list_make1(rte);
1906 new_query->jointree = jointree;
1907 new_query->targetList = copyObject(q->targetList);
1909 if (new_query->targetList) {
1910 foreach (lc, new_query->targetList) {
1911 TargetEntry *te = (TargetEntry *)lfirst(lc);
1912 SortGroupClause *sgc = makeNode(SortGroupClause);
1914 sgc->tleSortGroupRef = te->ressortgroupref = ++sortgroupref;
1916 get_sort_group_operators(exprType((Node *)te->expr),
false,
true,
false,
1917 &sgc->sortop, &sgc->eqop, NULL, &sgc->hashable);
1919 new_query->groupClause = lappend(new_query->groupClause, sgc);
1922 GroupingSet *gs = makeNode(GroupingSet);
1923 gs->kind = GROUPING_SET_EMPTY;
1926 new_query->groupingSets = list_make1(gs);
1950 if (IsA(node, FuncExpr)) {
1951 FuncExpr *f = (FuncExpr *)node;
1973 foreach (lc, q->targetList) {
1974 TargetEntry *te = (TargetEntry *)lfirst(lc);
1975 if (te->ressortgroupref > 0) {
1977 (
void *)constants)) {
1981#if PG_VERSION_NUM >= 180000
1984 if(IsA(te->expr, Var)) {
1985 Var *v = (Var *) te->expr;
1986 RangeTblEntry *r = (RangeTblEntry *)list_nth(q->rtable, v->varno - 1);
1987 if(r->rtekind == RTE_GROUP)
1989 (
void *)constants)) {
2011 if (IsA(node, Query)) {
2012 Query *q = (Query *)node;
2018 foreach (rc, q->rtable) {
2019 RangeTblEntry *r = (RangeTblEntry *)lfirst(rc);
2020 if (r->rtekind == RTE_RELATION) {
2022 AttrNumber attid = 1;
2024 foreach (lc, r->eref->colnames) {
2025 const char *v = strVal(lfirst(lc));
2034 }
else if (r->rtekind == RTE_FUNCTION) {
2036 AttrNumber attid = 1;
2038 foreach (lc, r->functions) {
2039 RangeTblFunction *func = (RangeTblFunction *)lfirst(lc);
2041 if (func->funccolcount == 1) {
2042 FuncExpr *expr = (FuncExpr *)func->funcexpr;
2044 !strcmp(get_rte_attribute_name(r, attid),
2050 attid += func->funccolcount;
2084 if (IsA(node, Var)) {
2085 Var *v = (Var *) node;
2090 return expression_tree_walker(node,
aggtoken_walker, (
void*) constants);
2104 return expression_tree_walker(node,
aggtoken_walker, (
void*) constants);
2127 SetOperationStmt *setOps = (SetOperationStmt *)q->setOperations;
2128 RangeTblEntry *rte = makeNode(RangeTblEntry);
2129 FromExpr *fe = makeNode(FromExpr);
2130 JoinExpr *je = makeNode(JoinExpr);
2131 BoolExpr *expr = makeNode(BoolExpr);
2135 if (!IsA(setOps->larg, RangeTblRef) || !IsA(setOps->rarg, RangeTblRef)) {
2139 expr->boolop = AND_EXPR;
2140 expr->location = -1;
2143 foreach (lc, q->targetList) {
2144 TargetEntry *te = (TargetEntry *)lfirst(lc);
2147 if (!IsA(te->expr, Var))
2150 v = (Var *)te->expr;
2153 OpExpr *oe = makeNode(OpExpr);
2155 Operator opInfo = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno));
2156 Form_pg_operator opform;
2157 Var *leftArg, *rightArg;
2159 if (!HeapTupleIsValid(opInfo))
2160 provsql_error(
"could not find operator with OID %u to compare variables of type %u",
2163 opform = (Form_pg_operator)GETSTRUCT(opInfo);
2164 leftArg = makeNode(Var);
2165 rightArg = makeNode(Var);
2168 oe->opfuncid = opform->oprcode;
2169 oe->opresulttype = opform->oprresult;
2170 oe->opcollid = InvalidOid;
2171 oe->inputcollid = DEFAULT_COLLATION_OID;
2173 leftArg->varno = ((RangeTblRef *)setOps->larg)->rtindex;
2174 rightArg->varno = ((RangeTblRef *)setOps->rarg)->rtindex;
2175 leftArg->varattno = rightArg->varattno = attno;
2177#if PG_VERSION_NUM >= 130000
2178 leftArg->varnosyn = rightArg->varnosyn = 0;
2179 leftArg->varattnosyn = rightArg->varattnosyn = 0;
2181 leftArg->varnoold = leftArg->varno;
2182 rightArg->varnoold = rightArg->varno;
2183 leftArg->varoattno = rightArg->varoattno = attno;
2186 leftArg->vartype = rightArg->vartype = v->vartype;
2187 leftArg->varcollid = rightArg->varcollid = InvalidOid;
2188 leftArg->vartypmod = rightArg->vartypmod = -1;
2189 leftArg->location = rightArg->location = -1;
2191 oe->args = list_make2(leftArg, rightArg);
2193 expr->args = lappend(expr->args, oe);
2195 ReleaseSysCache(opInfo);
2203 rte->joinaliasvars = NULL;
2205 rte->rtekind = RTE_JOIN;
2206 rte->jointype = JOIN_LEFT;
2208 q->rtable = lappend(q->rtable, rte);
2210 je->jointype = JOIN_LEFT;
2212 je->larg = setOps->larg;
2213 je->rarg = setOps->rarg;
2214 je->quals = (Node *)expr;
2215 je->rtindex = list_length(q->rtable);
2217 fe->fromlist = list_make1(je);
2223 q->setOperations = 0;
2241 SetOperationStmt *stmt) {
2242 if (stmt->op != SETOP_UNION) {
2245 if (IsA(stmt->larg, SetOperationStmt)) {
2248 if (IsA(stmt->rarg, SetOperationStmt)) {
2251 stmt->colTypes = lappend_oid(stmt->colTypes, constants->
OID_TYPE_UUID);
2252 stmt->colTypmods = lappend_int(stmt->colTypmods, -1);
2253 stmt->colCollations = lappend_int(stmt->colCollations, 0);
2271 FuncExpr *
gate_zero = makeNode(FuncExpr);
2272 OpExpr *oe = makeNode(OpExpr);
2279 oe->opresulttype = BOOLOID;
2280 oe->args = list_make2(provsql,
gate_zero);
2283 if (q->jointree->quals != NULL) {
2284 BoolExpr *be = makeNode(BoolExpr);
2286 be->boolop = AND_EXPR;
2287 be->args = list_make2(oe, q->jointree->quals);
2290 q->jointree->quals = (Node *)be;
2292 q->jointree->quals = (Node *)oe;
2309 havingQual = (Node*) expr;
2310 }
else if(IsA(havingQual, BoolExpr) && ((BoolExpr*)havingQual)->boolop==AND_EXPR) {
2311 BoolExpr *be = (BoolExpr*)havingQual;
2312 be->args = lappend(be->args, expr);
2313 }
else if(IsA(havingQual, OpExpr) || IsA(havingQual, BoolExpr)) {
2315 BoolExpr *be = makeNode(BoolExpr);
2316 be->boolop=AND_EXPR;
2318 be->args = list_make2(havingQual, expr);
2319 havingQual = (Node*) be;
2321 provsql_error(
"Unknown structure within Boolean expression");
2342 bool found_agg_token=
false;
2344 if(op->args->length != 2)
2347 for(
unsigned i=0; i<2; ++i) {
2348 Node *arg = lfirst(list_nth_cell(op->args, i));
2353 found_agg_token=
true;
2354 }
else if(IsA(arg, Const)) {
2355 }
else if(IsA(arg, FuncExpr)) {
2356 FuncExpr *fe = (FuncExpr*) arg;
2357 if(fe->funcformat != COERCE_IMPLICIT_CAST && fe->funcformat != COERCE_EXPLICIT_CAST) {
2361 if(fe->args->length != 1) {
2365 if(!IsA(lfirst(list_head(fe->args)), Const)) {
2375 return ok && found_agg_token;
2393 foreach (lc, be->args) {
2395 if(IsA(n, OpExpr)) {
2398 }
else if(IsA(n, BoolExpr)) {
2416 switch(expr->type) {
2422 provsql_error(
"Unknown structure within Boolean expression");
2450 foreach (l, q->rtable) {
2451 RangeTblEntry *r = (RangeTblEntry *)lfirst(l);
2458 columns[i] = (
int *)palloc(r->eref->colnames->length *
sizeof(
int));
2460 foreach (lc, r->eref->colnames) {
2463 columns[i][j] = ++(*nbcols);
2465 const char *v = strVal(lfirst(lc));
2467 if (strcmp(v,
"") && r->rtekind != RTE_JOIN) {
2471 columns[i][j] = ++(*nbcols);
2501 if (!q->jointree || !q->jointree->quals)
2518 q->jointree->quals = NULL;
2519 }
else if (IsA(q->jointree->quals, BoolExpr)) {
2520 BoolExpr *be = (BoolExpr *)q->jointree->quals;
2521 if (be->boolop == AND_EXPR) {
2523 ListCell *cell, *prev;
2524 for (cell = list_head(be->args), prev = NULL; cell != NULL;) {
2526 Expr *expr = (Expr *)lfirst(cell);
2533 cell = list_head(be->args);
2537 provsql_error(
"Complex selection on aggregation results not supported");
2545 provsql_error(
"Complex selection on aggregation results not supported");
2548 provsql_error(
"Unknown structure within Boolean expression");
2580 bool has_union =
false;
2581 bool has_difference =
false;
2582 bool supported =
true;
2583 bool group_by_rewrite =
false;
2588 elog_node_display(NOTICE,
"Before ProvSQL query rewriting", q,
true);
2590 if (q->rtable == NULL) {
2596 Bitmapset *removed_sortgrouprefs = NULL;
2598 if (q->targetList) {
2599 removed_sortgrouprefs =
2601 if (removed_sortgrouprefs != NULL)
2603 if (q->setOperations)
2609 columns = (
int **)palloc(q->rtable->length *
sizeof(
int *));
2611 if (q->setOperations) {
2615 SetOperationStmt *stmt = (SetOperationStmt *)q->setOperations;
2632 if (prov_atts == NIL)
2635 if (q->hasSubLinks) {
2636 provsql_error(
"Subqueries in WHERE clause not supported by provsql");
2640 if (supported && q->distinctClause) {
2641 if (q->hasDistinctOn) {
2644 }
else if (list_length(q->distinctClause) < list_length(q->targetList)) {
2645 provsql_error(
"Inconsistent DISTINCT and GROUP BY clauses not "
2646 "supported by provsql");
2653 if (supported && q->setOperations) {
2654 SetOperationStmt *stmt = (SetOperationStmt *)q->setOperations;
2656 if (stmt->op == SETOP_UNION) {
2659 }
else if (stmt->op == SETOP_EXCEPT) {
2662 has_difference =
true;
2664 provsql_error(
"Set operations other than UNION and EXCEPT not "
2665 "supported by provsql");
2670 if (supported && q->groupClause &&
2672 group_by_rewrite =
true;
2675 if (supported && q->groupingSets) {
2676 if (q->groupClause || list_length(q->groupingSets) > 1 ||
2677 ((GroupingSet *)linitial(q->groupingSets))->kind !=
2678 GROUPING_SET_EMPTY) {
2679 provsql_error(
"GROUPING SETS, CUBE, and ROLLUP not supported by provsql");
2683 group_by_rewrite =
true;
2698 constants, q, prov_atts,
2704 foreach (lc_sort, q->sortClause) {
2705 SortGroupClause *sort = (SortGroupClause *)lfirst(lc_sort);
2707 foreach (lc_te, q->targetList) {
2708 TargetEntry *te = (TargetEntry *)lfirst(lc_te);
2709 if (sort->tleSortGroupRef == te->ressortgroupref) {
2711 provsql_error(
"ORDER BY on the result of an aggregate function is "
2712 "not supported by ProvSQL");
2723 constants, q, prov_atts, q->hasAggs, group_by_rewrite,
2734 for (i = 0; i < q->rtable->length; ++i) {
2741 elog_node_display(NOTICE,
"After ProvSQL query rewriting", q,
true);
2764#
if PG_VERSION_NUM >= 130000
2765 const char *query_string,
2768 ParamListInfo boundParams) {
2769 if (q->commandType == CMD_SELECT && q->rtable) {
2773 bool *removed = NULL;
2777#if PG_VERSION_NUM >= 150000
2779 provsql_notice(
"Main query before ProvSQL query rewriting:\n%s\n",
2780 pg_get_querydef(q,
true));
2790 (
double)(clock() - begin) / CLOCKS_PER_SEC);
2792#if PG_VERSION_NUM >= 150000
2794 provsql_notice(
"Main query after ProvSQL query rewriting:\n%s\n",
2795 pg_get_querydef(q,
true));
2798 if (new_query != NULL)
2805#
if PG_VERSION_NUM >= 130000
2808 cursorOptions, boundParams);
2810 return standard_planner(q,
2811#
if PG_VERSION_NUM >= 130000
2814 cursorOptions, boundParams);
2827 if (!process_shared_preload_libraries_in_progress)
2828 provsql_error(
"provsql needs to be added to the shared_preload_libraries "
2829 "configuration variable");
2831 DefineCustomBoolVariable(
"provsql.active",
2832 "Should ProvSQL track provenance?",
2833 "1 is standard ProvSQL behavior, 0 means provsql attributes will be dropped.",
2841 DefineCustomBoolVariable(
"provsql.where_provenance",
2842 "Should ProvSQL track where-provenance?",
2843 "1 turns where-provenance on, 0 off.",
2851 DefineCustomBoolVariable(
"provsql.update_provenance",
2852 "Should ProvSQL track update provenance?",
2853 "1 turns update provenance on, 0 off.",
2861 DefineCustomIntVariable(
"provsql.verbose_level",
2862 "Level of verbosity for ProvSQL informational and debug messages",
2863 "0 for quiet (default), 1-9 for informational messages, 10-100 for debug information.",
2875 EmitWarningsOnPlaceholders(
"provsql");
2879#if (PG_VERSION_NUM >= 150000)
2880 prev_shmem_request = shmem_request_hook;
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 void remove_provenance_attribute_groupref(Query *q, const Bitmapset *removed_sortgrouprefs)
Remove sort/group references that belonged to removed provenance columns.
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 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)
Tree walker that detects any provenance-bearing relation or provenance() call.
static Node * aggregation_type_mutator(Node *node, aggregation_type_mutator_context *context)
Tree-mutator that retyps a specific Var to agg_token.
int provsql_verbose
Verbosity level; controlled by the provsql.verbose_level GUC.
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.
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 has_aggtoken(Node *node, const constants_t *constants)
Return true if node contains a Var of type agg_token.
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 List * get_provenance_attributes(const constants_t *constants, Query *q)
Collect all provenance Var nodes reachable from q's range table.
PG_MODULE_MAGIC
Required PostgreSQL extension magic block.
static void migrate_aggtoken_quals_to_having(const constants_t *constants, Query *q)
Move WHERE conditions on aggregate results (agg_token) to HAVING.
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 Query * process_query(const constants_t *constants, Query *q, bool **removed)
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 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 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.
void _PG_fini(void)
Extension teardown — restores the planner and shmem hooks.
static Node * provenance_mutator(Node *node, provenance_mutator_context *context)
Tree-mutator that replaces provenance() calls with the actual provenance expression.
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 * 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)
PostgreSQL planner hook — entry point for provenance rewriting.
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 bool aggtoken_walker(Node *node, const constants_t *constants)
Tree walker that detects any Var of type agg_token.
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 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 Node * aggregation_mutator(Node *node, aggregation_mutator_context *context)
Tree-mutator that replaces Aggrefs with provenance-aware aggregates.
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 Node * reduce_varattno_mutator(Node *node, reduce_varattno_mutator_context *context)
Tree-mutator callback that adjusts Var attribute numbers.
static void build_column_map(Query *q, int **columns, int *nbcols)
Build the per-RTE column-numbering map used by where-provenance.
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 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)
Build the combined provenance expression to be added to the SELECT list.
static const char * PROVSQL_COLUMN_NAME
Name of the provenance column added to tracked tables.
static void process_set_operation_union(const constants_t *constants, SetOperationStmt *stmt)
Recursively annotate a UNION tree with the provenance UUID type.
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.
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_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.
Oid find_equality_operator(Oid ltypeId, Oid rtypeId)
Find the equality operator OID for two given types.
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.
@ gate_zero
Semiring zero.
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_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_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_FUNCTION_PROVENANCE_CMP
OID of the provenance_cmp FUNCTION.
Context for the provenance_mutator tree walker.
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.