ProvSQL C/C++ API
Adding support for provenance and uncertainty management to PostgreSQL databases
Loading...
Searching...
No Matches
qual_classify.c
Go to the documentation of this file.
1/**
2 * @file qual_classify.c
3 * @brief Implementation of the shared predicate-tree classification
4 * helpers. See @c qual_classify.h.
5 */
6#include "postgres.h"
7#include "nodes/parsenodes.h"
8#include "nodes/pg_list.h"
9#include "nodes/bitmapset.h"
10#include "nodes/makefuncs.h"
11#include "nodes/nodeFuncs.h"
12#if PG_VERSION_NUM >= 120000
13#include "optimizer/optimizer.h"
14#else
15#include "optimizer/clauses.h" /* contain_volatile_functions (PG <12) */
16#endif
17
18#include "qual_classify.h"
19#include "provsql_utils.h"
20
21bool qc_collect_vars_walker(Node *node, qc_vars_ctx *ctx)
22{
23 ListCell *lc;
24 if (node == NULL)
25 return false;
26 if (IsA(node, Var)) {
27 Var *v = (Var *) node;
28 if (v->varlevelsup != 0)
29 return false;
30 foreach (lc, ctx->vars) {
31 Var *existing = (Var *) lfirst(lc);
32 if (existing->varno == v->varno && existing->varattno == v->varattno)
33 return false;
34 }
35 ctx->vars = lappend(ctx->vars, v);
36 return false;
37 }
38 return expression_tree_walker(node, qc_collect_vars_walker, ctx);
39}
40
41int qc_var_index(List *vars, Index varno, AttrNumber varattno)
42{
43 ListCell *lc;
44 int i = 0;
45 foreach (lc, vars) {
46 Var *v = (Var *) lfirst(lc);
47 if (v->varno == varno && v->varattno == varattno)
48 return i;
49 i++;
50 }
51 return -1;
52}
53
54bool qc_is_var_eq(Expr *qual, Var **l, Var **r)
55{
56 OpExpr *op;
57 Node *ln, *rn;
58 Var *lv, *rv;
59 Oid expected;
60
61 if (!IsA(qual, OpExpr))
62 return false;
63 op = (OpExpr *) qual;
64 if (list_length(op->args) != 2)
65 return false;
66 ln = (Node *) linitial(op->args);
67 rn = (Node *) lsecond(op->args);
68 while (IsA(ln, RelabelType))
69 ln = (Node *) ((RelabelType *) ln)->arg;
70 while (IsA(rn, RelabelType))
71 rn = (Node *) ((RelabelType *) rn)->arg;
72 if (!IsA(ln, Var) || !IsA(rn, Var))
73 return false;
74 lv = (Var *) ln;
75 rv = (Var *) rn;
76 if (lv->varlevelsup != 0 || rv->varlevelsup != 0)
77 return false;
78 expected = find_equality_operator(lv->vartype, rv->vartype);
79 if (expected == InvalidOid || op->opno != expected)
80 return false;
81 *l = lv;
82 *r = rv;
83 return true;
84}
85
86bool qc_is_var_const_eq(Expr *qual, Var **var, Const **konst)
87{
88 OpExpr *op;
89 Node *ln, *rn;
90 Var *v;
91 Const *k;
92 Oid expected;
93
94 if (!IsA(qual, OpExpr))
95 return false;
96 op = (OpExpr *) qual;
97 if (list_length(op->args) != 2)
98 return false;
99 ln = (Node *) linitial(op->args);
100 rn = (Node *) lsecond(op->args);
101 while (IsA(ln, RelabelType))
102 ln = (Node *) ((RelabelType *) ln)->arg;
103 while (IsA(rn, RelabelType))
104 rn = (Node *) ((RelabelType *) rn)->arg;
105 if (IsA(ln, Var) && IsA(rn, Const)) {
106 v = (Var *) ln;
107 k = (Const *) rn;
108 } else if (IsA(ln, Const) && IsA(rn, Var)) {
109 k = (Const *) ln;
110 v = (Var *) rn;
111 } else {
112 return false;
113 }
114 if (v->varlevelsup != 0)
115 return false;
116 if (k->constisnull)
117 return false;
118 expected = find_equality_operator(v->vartype, k->consttype);
119 if (expected == InvalidOid || op->opno != expected)
120 return false;
121 *var = v;
122 *konst = k;
123 return true;
124}
125
126void qc_collect_equalities(Node *quals, List **out)
127{
128 if (quals == NULL)
129 return;
130 if (IsA(quals, List)) {
131 ListCell *lc;
132 foreach (lc, (List *) quals)
133 qc_collect_equalities((Node *) lfirst(lc), out);
134 return;
135 }
136 if (IsA(quals, BoolExpr)) {
137 BoolExpr *be = (BoolExpr *) quals;
138 if (be->boolop == AND_EXPR) {
139 ListCell *lc;
140 foreach (lc, be->args)
141 qc_collect_equalities((Node *) lfirst(lc), out);
142 }
143 return;
144 }
145 {
146 Var *l, *r;
147 if (qc_is_var_eq((Expr *) quals, &l, &r)) {
148 *out = lappend(*out, l);
149 *out = lappend(*out, r);
150 }
151 }
152}
153
155{
156 if (node == NULL)
157 return false;
158 if (IsA(node, Var)) {
159 Var *v = (Var *) node;
160 if (v->varlevelsup == 0 && (int) v->varno >= 1)
161 ctx->varnos = bms_add_member(ctx->varnos, (int) v->varno);
162 return false;
163 }
164 return expression_tree_walker(node, qc_collect_varnos_walker, ctx);
165}
166
167void qc_flatten_and(Node *n, List **out)
168{
169 if (n == NULL)
170 return;
171 if (IsA(n, List)) {
172 ListCell *lc;
173 foreach (lc, (List *) n)
174 qc_flatten_and((Node *) lfirst(lc), out);
175 return;
176 }
177 if (IsA(n, BoolExpr) && ((BoolExpr *) n)->boolop == AND_EXPR) {
178 ListCell *lc;
179 foreach (lc, ((BoolExpr *) n)->args)
180 qc_flatten_and((Node *) lfirst(lc), out);
181 return;
182 }
183 *out = lappend(*out, n);
184}
185
186void qc_split_quals(Node *quals, int natoms,
187 List **per_atom_out, Node **out_residual)
188{
189 List *conjuncts = NIL;
190 List *residual = NIL;
191 ListCell *lc;
192 int i;
193
194 for (i = 0; i < natoms; i++)
195 per_atom_out[i] = NIL;
196
197 qc_flatten_and(quals, &conjuncts);
198
199 foreach (lc, conjuncts) {
200 Node *qual = (Node *) lfirst(lc);
201 qc_varnos_ctx vctx = { NULL };
202 int nmembers;
203
204 if (contain_volatile_functions(qual)) {
205 residual = lappend(residual, qual);
206 continue;
207 }
208
209 qc_collect_varnos_walker(qual, &vctx);
210 nmembers = bms_num_members(vctx.varnos);
211 if (nmembers == 1) {
212 int v = bms_singleton_member(vctx.varnos);
213 if (v >= 1 && v <= natoms) {
214 per_atom_out[v - 1] = lappend(per_atom_out[v - 1], qual);
215 bms_free(vctx.varnos);
216 continue;
217 }
218 }
219 bms_free(vctx.varnos);
220 residual = lappend(residual, qual);
221 }
222
223 if (residual == NIL)
224 *out_residual = NULL;
225 else if (list_length(residual) == 1)
226 *out_residual = (Node *) linitial(residual);
227 else
228 *out_residual = (Node *) makeBoolExpr(AND_EXPR, residual, -1);
229}
Oid find_equality_operator(Oid ltypeId, Oid rtypeId)
Find the equality operator OID for two given types.
Core types, constants, and utilities shared across ProvSQL.
void qc_flatten_and(Node *n, List **out)
Flatten the top-level AND tree of a qual into a flat list of leaf conjuncts (a bare List is an implic...
void qc_split_quals(Node *quals, int natoms, List **per_atom_out, Node **out_residual)
Partition top-level conjuncts into atom-local selections and the cross-atom residual.
int qc_var_index(List *vars, Index varno, AttrNumber varattno)
Position of a Var inside vars (matched on (varno, varattno)); -1 if absent.
void qc_collect_equalities(Node *quals, List **out)
Walk quals as an AND tree, appending each Var=Var equijoin's two Vars (left, right) to *out.
bool qc_collect_vars_walker(Node *node, qc_vars_ctx *ctx)
Tree walker that collects every distinct base-level Var node (varlevelsup == 0), deduplicated by (var...
bool qc_collect_varnos_walker(Node *node, qc_varnos_ctx *ctx)
Collect the distinct base-level varno values referenced by a sub-tree (used to tell a single-relation...
bool qc_is_var_eq(Expr *qual, Var **l, Var **r)
Recognise a conjunct equating two base Vars (the canonical equality for the operand types,...
bool qc_is_var_const_eq(Expr *qual, Var **var, Const **konst)
Recognise a conjunct of shape Var=Const (either order, through RelabelType casts; non-NULL literal,...
Predicate-tree classification helpers shared by the query rewriters (the safe-query rewrite and the j...
Walker context for qc_collect_varnos_walker.
Bitmapset * varnos
Set of varno values seen in base-level Vars.
Walker context for qc_collect_vars_walker.
List * vars
Deduplicated list of distinct base-level Var nodes.