60#include "access/htup_details.h"
61#include "access/xact.h"
62#include "catalog/pg_type.h"
63#include "executor/spi.h"
64#include "utils/array.h"
65#include "utils/builtins.h"
66#include "utils/resowner.h"
67#include "utils/uuid.h"
101#include <unordered_map>
102#include <unordered_set>
108class MobiusDecline :
public std::runtime_error {
110 explicit MobiusDecline(
const std::string &w) : std::runtime_error(w) {}
117 bool operator==(
const Term &o)
const {
return isVar==o.isVar && v==o.v; }
123 std::vector<Term> args;
126using Disjunct = std::vector<MAtom>;
127using Sentence = std::vector<Disjunct>;
133 int n_components = 0;
134 int n_cnf_conjuncts = 0;
135 int lattice_size = 0;
136 int lattice_collapsed = 0;
139 bool cancelled_hard =
false;
142 double probability = 0.0;
155bool homExists(
const Disjunct &p,
const Disjunct &q,
156 std::size_t ai, std::map<long,Term> &asg)
160 const MAtom &pa = p[ai];
161 for(
const MAtom &qa : q) {
162 if(qa.rel != pa.rel || qa.args.size() != pa.args.size())
164 std::vector<long> undo;
166 for(std::size_t k=0; k<pa.args.size(); ++k) {
167 const Term &pt = pa.args[k];
168 const Term &qt = qa.args[k];
170 if(qt.isVar || qt.v != pt.v) { ok=
false;
break; }
172 auto it = asg.find(pt.v);
173 if(it == asg.end()) { asg[pt.v]=qt; undo.push_back(pt.v); }
174 else if(!(it->second == qt)) { ok=
false;
break; }
177 if(ok && homExists(p, q, ai+1, asg))
179 for(
long u : undo) asg.erase(u);
185bool hom(
const Disjunct &p,
const Disjunct &q)
187 std::map<long,Term> asg;
188 return homExists(p, q, 0, asg);
195bool equiv(
const Disjunct &p,
const Disjunct &q)
197 return hom(p,q) && hom(q,p);
206 explicit UF(
int n) : p(n) {
for(
int i=0;i<n;++i) p[i]=i; }
207 int find(
int x){
while(p[x]!=x){ p[x]=p[p[x]]; x=p[x]; }
return x; }
208 void uni(
int a,
int b){ p[find(a)]=find(b); }
216std::vector<Disjunct> componentsOf(
const Disjunct &d)
218 const int n =
static_cast<int>(d.size());
220 std::map<long, int> firstAtomOfVar;
222 for(
const auto &t : d[i].args)
224 auto it = firstAtomOfVar.find(t.v);
225 if(it==firstAtomOfVar.end()) firstAtomOfVar[t.v]=i;
226 else uf.uni(it->second, i);
228 std::map<int, Disjunct> byroot;
230 byroot[uf.find(i)].push_back(d[i]);
231 std::vector<Disjunct> out;
232 for(
auto &kv : byroot) out.push_back(std::move(kv.second));
237bool ground(
const MAtom &a)
239 for(
const auto &t : a.args)
if(t.isVar)
return false;
250 std::map<std::pair<unsigned, std::vector<long>>, std::string> tok;
251 std::set<std::pair<unsigned, std::vector<long>>> present;
253 std::map<std::pair<unsigned,int>, std::set<long>> domain;
255 bool isPresent(
unsigned rel,
const std::vector<long> &el)
const {
256 return present.count({rel, el}) > 0;
258 const std::string *token(
unsigned rel,
const std::vector<long> &el)
const {
259 auto it = tok.find({rel, el});
260 return it==tok.end() ? nullptr : &it->second;
268std::string canonDisjunct(
const Disjunct &d)
274 std::vector<std::string> atomsigRaw;
275 for(
const auto &a : d) {
276 std::string sg =
"r" + std::to_string(a.rel) +
"(";
277 for(
const auto &t : a.args) {
278 if(t.isVar) sg +=
"v";
else sg +=
"c"+std::to_string(t.v);
282 atomsigRaw.push_back(sg);
286 std::vector<int> order(d.size());
287 for(std::size_t i=0;i<d.size();++i) order[i]=
static_cast<int>(i);
288 std::sort(order.begin(), order.end(),
289 [&](
int a,
int b){ return atomsigRaw[a]<atomsigRaw[b]; });
290 std::map<long,int> ren;
292 for(
int idx : order) {
293 const MAtom &a = d[idx];
294 out +=
"r"+std::to_string(a.rel)+
"(";
295 for(
const auto &t : a.args) {
297 auto it = ren.find(t.v);
298 int id = (it==ren.end()) ? (ren[t.v]=
static_cast<int>(ren.size())) : it->second;
299 out +=
"v"+std::to_string(
id);
300 }
else out +=
"c"+std::to_string(t.v);
308std::string canonSentence(
const Sentence &s)
310 std::vector<std::string> parts;
311 for(
const auto &d : s) parts.push_back(canonDisjunct(d));
312 std::sort(parts.begin(), parts.end());
313 parts.erase(std::unique(parts.begin(), parts.end()), parts.end());
315 for(
const auto &p : parts){ out += p; out +=
"|"; }
334using FactPattern = std::pair<unsigned, std::map<int,long>>;
335using Footprint = std::set<FactPattern>;
337void addToFootprint(
const Disjunct &d, Footprint &fp)
339 for(
const auto &a : d) {
340 std::map<int,long> c;
341 for(std::size_t i=0;i<a.args.size();++i)
342 if(!a.args[i].isVar) c[
static_cast<int>(i)] = a.args[i].v;
343 fp.insert({a.rel, std::move(c)});
347Footprint footprintOf(
const Disjunct &d)
349 Footprint fp; addToFootprint(d, fp);
return fp;
353bool patternsDisjoint(
const FactPattern &x,
const FactPattern &y)
355 if(x.first != y.first)
357 for(
const auto &kv : x.second) {
358 auto it = y.second.find(kv.first);
359 if(it != y.second.end() && it->second != kv.second)
365bool disjointFootprints(
const Footprint &a,
const Footprint &b)
367 for(
const auto &x : a)
368 for(
const auto &y : b)
369 if(!patternsDisjoint(x,y))
return false;
405const long SHARD_FREE = std::numeric_limits<long>::min();
411 std::vector<int> rep;
412 std::vector<long> cst;
413 bool operator<(
const ShardSig &o)
const {
414 if(rep != o.rep)
return rep < o.rep;
424 std::vector<int> freepos;
429bool atomNeedsShards(
const MAtom &a)
431 for(std::size_t i=0;i<a.args.size();++i) {
432 if(!a.args[i].isVar)
return true;
433 for(std::size_t j=0;j<i;++j)
434 if(a.args[j] == a.args[i])
return true;
439ShardSig shardSigOf(
const std::vector<long> &el,
const std::set<long> &consts)
441 const int k =
static_cast<int>(el.size());
444 sg.cst.assign(k, SHARD_FREE);
445 for(
int i=0;i<k;++i) {
447 for(
int j=0;j<i;++j)
if(el[j]==el[i]) { r = sg.rep[j];
break; }
451 if(sg.rep[i]==i && consts.count(el[i])) sg.cst[i] = el[i];
457bool shardCompatible(
const MAtom &a,
const Shard &sh)
459 const int k =
static_cast<int>(a.args.size());
460 if(
static_cast<int>(sh.sig.rep.size()) != k)
462 for(
int p=0;p<k;++p) {
463 const int r = sh.sig.rep[p];
464 const Term &tp = a.args[p], &tr = a.args[r];
465 if(!tp.isVar && !tr.isVar && tp.v != tr.v)
467 if(sh.sig.cst[r] != SHARD_FREE) {
468 if(!tp.isVar && tp.v != sh.sig.cst[r])
return false;
469 }
else if(!tp.isVar) {
474 for(
int q=p+1;q<k;++q)
475 if(sh.sig.rep[p]!=sh.sig.rep[q] && a.args[p]==a.args[q])
485 bool unify(
const Term &x,
const Term &y) {
486 if(!x.isVar && !y.isVar)
return x.v == y.v;
487 if(!x.isVar)
return bind(y.v, x.v);
488 if(!y.isVar)
return bind(x.v, y.v);
489 const long rx = find(x.v), ry = find(y.v);
490 if(rx == ry)
return true;
491 auto cx = bound.find(rx), cy = bound.find(ry);
492 if(cx!=bound.end() && cy!=bound.end() && cx->second != cy->second)
495 if(cy != bound.end()) {
496 const long v = cy->second;
503 Term apply(
const Term &t) {
504 if(!t.isVar)
return t;
505 const long r = find(t.v);
506 auto it = bound.find(r);
508 if(it != bound.end()) { o.isVar=
false; o.v=it->second; }
509 else { o.isVar=
true; o.v=r; }
513 std::map<long,long> parent;
514 std::map<long,long> bound;
516 auto it = parent.find(v);
517 if(it == parent.end()) { parent[v]=v;
return v; }
518 if(it->second == v)
return v;
519 const long r = find(it->second);
523 bool bind(
long var,
long c) {
524 const long r = find(var);
525 auto it = bound.find(r);
526 if(it != bound.end())
return it->second == c;
536const std::size_t SHARD_MAX_DISJUNCTS = 256;
548bool normalizeShardsPass(
const Sentence &s,
const FactIndex &fi,
549 const std::set<unsigned> &need,
550 const std::set<long> &consts,
unsigned maxRel,
551 Sentence &sOut, FactIndex &fiOut,
552 std::set<unsigned> &missing)
561 std::map<unsigned, std::map<ShardSig, Shard>> shards;
562 unsigned next = maxRel + 1;
564 for(
const auto &kv : fi.tok) {
565 const unsigned rel = kv.first.first;
566 const std::vector<long> &el = kv.first.second;
568 std::vector<long> proj = el;
569 if(need.count(rel)) {
570 const ShardSig sg = shardSigOf(el, consts);
571 auto &byrel = shards[rel];
572 auto sit = byrel.find(sg);
573 if(sit == byrel.end()) {
577 for(std::size_t i=0;i<el.size();++i)
578 if(sg.rep[i]==
static_cast<int>(i) && sg.cst[i]==SHARD_FREE)
579 sh.freepos.push_back(
static_cast<int>(i));
580 sit = byrel.emplace(sg, sh).first;
582 sym = sit->second.sym;
584 for(
int p : sit->second.freepos) proj.push_back(el[p]);
586 fiOut.tok[{sym, proj}] = kv.second;
587 fiOut.present.insert({sym, proj});
588 for(std::size_t i=0;i<proj.size();++i)
589 fiOut.domain[{sym,
static_cast<int>(i)}].insert(proj[i]);
595 std::set<std::string> emitted;
596 for(
const Disjunct &d : s) {
597 std::vector<std::vector<const Shard *>> opts;
598 unsigned long prod = 1;
600 for(
const MAtom &a : d) {
601 std::vector<const Shard *> o;
602 if(!need.count(a.rel)) {
603 o.push_back(
nullptr);
605 auto rit = shards.find(a.rel);
606 if(rit != shards.end())
607 for(
const auto &kv : rit->second)
608 if(shardCompatible(a, kv.second)) o.push_back(&kv.second);
610 if(o.empty()) { dead =
true;
break; }
612 if(prod > SHARD_MAX_DISJUNCTS)
613 throw MobiusDecline(
"Möbius: ranking/shattering expansion cap exceeded");
614 opts.push_back(std::move(o));
619 std::vector<const Shard *> choice(opts.size(),
nullptr);
620 std::function<void(std::size_t)> expand = [&](std::size_t ai) {
621 if(ai < opts.size()) {
622 for(
const Shard *sh : opts[ai]) { choice[ai]=sh; expand(ai+1); }
627 for(std::size_t i=0;i<d.size();++i) {
628 const Shard *sh = choice[i];
629 if(sh ==
nullptr)
continue;
630 for(std::size_t p=0;p<d[i].args.size();++p) {
631 const int r = sh->sig.rep[p];
632 if(r !=
static_cast<int>(p) && !sub.unify(d[i].args[p], d[i].args[r]))
634 if(sh->sig.cst[r] != SHARD_FREE) {
635 Term c; c.isVar=
false; c.v=sh->sig.cst[r];
636 if(!sub.unify(d[i].args[p], c))
return;
644 for(std::size_t i=0;i<d.size();++i) {
645 const Shard *sh = choice[i];
646 std::vector<Term> ap;
647 ap.reserve(d[i].args.size());
648 for(
const Term &t : d[i].args) ap.push_back(sub.apply(t));
652 na.args = std::move(ap);
653 if(atomNeedsShards(na))
654 missing.insert(na.rel);
656 for(std::size_t p=0;p<ap.size();++p)
657 for(std::size_t q=p+1;q<ap.size();++q)
658 if(sh->sig.rep[p]!=sh->sig.rep[q] && ap[p]==ap[q])
return;
659 for(
int p : sh->freepos)
660 if(!ap[p].isVar)
return;
662 for(
int p : sh->freepos) na.args.push_back(ap[p]);
665 for(
const MAtom &prev : nd)
666 if(prev.rel==na.rel && prev.args==na.args) { dup=
true;
break; }
667 if(!dup) nd.push_back(std::move(na));
669 if(nd.empty())
return;
670 if(sOut.size() >= SHARD_MAX_DISJUNCTS)
671 throw MobiusDecline(
"Möbius: ranking/shattering expansion cap exceeded");
672 sOut.push_back(std::move(nd));
681 for(
const Disjunct &d : sOut)
682 if(emitted.insert(canonDisjunct(d)).second) uniq.push_back(d);
683 sOut = std::move(uniq);
693 for(
const auto &d : s)
694 for(
const auto &a : d)
695 for(
const auto &t : a.args)
696 if(t.isVar) nextvar = std::max(nextvar, t.v + 1);
697 for(Disjunct &d : sOut) {
698 std::map<long,long> ren;
700 for(Term &t : a.args)
702 auto it = ren.find(t.v);
703 if(it == ren.end()) it = ren.emplace(t.v, nextvar++).first;
719bool normalizeShards(
const Sentence &s,
const FactIndex &fi,
720 Sentence &sOut, FactIndex &fiOut)
725 std::set<unsigned> need;
726 std::set<long> consts;
727 std::set<unsigned> rels;
729 for(
const auto &d : s)
730 for(
const auto &a : d) {
731 maxRel = std::max(maxRel, a.rel);
733 if(atomNeedsShards(a)) need.insert(a.rel);
734 for(
const auto &t : a.args)
if(!t.isVar) consts.insert(t.v);
742 for(
const auto &kv : fi.tok)
743 maxRel = std::max(maxRel, kv.first.first);
747 for(std::size_t round=0; round<=rels.size(); ++round) {
748 std::set<unsigned> missing;
749 const bool done = normalizeShardsPass(s, fi, need, consts, maxRel,
750 sOut, fiOut, missing);
753 need.insert(missing.begin(), missing.end());
755 throw MobiusDecline(
"Möbius: ranking/shattering did not converge");
762class MobiusCompiler {
764 MobiusCompiler(
const FactIndex &fi, MobiusStats &st) : fi(fi), st(st) {
768 if(!OidIsValid(times_oid) || !OidIsValid(plus_oid))
769 provsql_error(
"ucq_mobius: provenance_times / provenance_plus unavailable");
779 pg_uuid_t compileTop(
const Sentence &s,
const std::string &lineage =
"") {
780 pending_lineage = lineage;
781 lineage_consumed =
false;
782 pg_uuid_t root = compile(s,
true);
783 if(!lineage_consumed && !(lineage.empty() && isMobiusGate(root)))
802 root = mkMobius({root}, {1}, lineage);
809 Oid times_oid = InvalidOid;
810 Oid plus_oid = InvalidOid;
811 std::unordered_map<std::string, std::string> memo;
812 std::unordered_set<std::string> created;
813 std::string pending_lineage;
814 bool lineage_consumed =
false;
824 pg_uuid_t callProvenance(
bool isAnd,
const std::vector<pg_uuid_t> &ch) {
825 std::vector<Datum> datums;
826 datums.reserve(ch.size());
827 for(
const auto &u : ch)
828 datums.push_back(UUIDPGetDatum(
const_cast<pg_uuid_t *
>(&u)));
829 ArrayType *arr = construct_array(datums.empty()?
nullptr:datums.data(),
830 static_cast<int>(datums.size()),
832 Datum res = OidFunctionCall1(isAnd ? times_oid : plus_oid,
833 PointerGetDatum(arr));
841 throw MobiusDecline(
"Möbius: data-cost cap (provsql.mobius_max_gates) "
842 "exceeded -- the |D|^k recursion is too large");
843 return *DatumGetUUIDP(res);
847 pg_uuid_t mkConst(
bool one) {
return callProvenance(one, {}); }
853 bool isMobiusGate(
const pg_uuid_t &u)
const {
861 pg_uuid_t mkBool(
bool isAnd, std::vector<pg_uuid_t> ch) {
862 std::vector<std::string> texts;
863 texts.reserve(ch.size());
864 for(
const auto &c : ch) texts.push_back(
uuid2string(c));
865 std::sort(texts.begin(), texts.end());
866 texts.erase(std::unique(texts.begin(), texts.end()), texts.end());
867 std::vector<pg_uuid_t> uniq;
868 uniq.reserve(texts.size());
869 for(
const auto &t : texts) uniq.push_back(
string2uuid(t));
870 return callProvenance(isAnd, uniq);
880 pg_uuid_t mkMobius(
const std::vector<pg_uuid_t> &children,
881 const std::vector<long> &coeffs,
882 const std::string &lineage =
"") {
883 std::map<std::string,long> bycoeff;
884 std::vector<std::string> order;
885 for(std::size_t i=0;i<children.size();++i) {
887 if(!bycoeff.count(u)) order.push_back(u);
888 bycoeff[u] += coeffs[i];
890 std::vector<pg_uuid_t> ch;
891 std::string extra, name =
"mobius[";
892 if(!lineage.empty()) {
894 extra +=
"L:" + lineage +
" ";
895 name +=
"L:" + lineage +
",";
897 for(
const std::string &u : order) {
898 if(bycoeff[u]==0)
continue;
900 extra += u +
":" + std::to_string(bycoeff[u]) +
" ";
901 name += u +
":" + std::to_string(bycoeff[u]) +
",";
904 if(ch.empty() || (!lineage.empty() && ch.size()==1))
905 return mkConst(
false);
909 static_cast<unsigned>(ch.size()),
919 pg_uuid_t compile(
const Sentence &sentence,
bool top=
false);
925 bool findSeparator(
const Sentence &s,
926 std::set<std::pair<unsigned,int>> &occ,
927 std::vector<std::set<long>> &classVarsPerDisj);
930 pg_uuid_t mobiusStep(
const Sentence &s,
bool top);
935bool MobiusCompiler::findSeparator(
const Sentence &s,
936 std::set<std::pair<unsigned,int>> &occOut,
937 std::vector<std::set<long>> &classVarsPerDisj)
940 std::map<long,int> id;
941 auto vid = [&](
long v)->
int{
942 auto it=
id.find(v);
if(it!=
id.end())
return it->second;
943 int n=
static_cast<int>(
id.size());
id[v]=n;
return n;
945 for(
const auto &d : s)
for(
const auto &a : d)
for(
const auto &t : a.args)
946 if(t.isVar) vid(t.v);
947 if(
id.empty())
return false;
948 UF uf(
static_cast<int>(
id.size()));
949 std::map<std::pair<unsigned,int>,
long> firstAtPos;
950 for(
const auto &d : s)
for(
const auto &a : d)
951 for(std::size_t k=0;k<a.args.size();++k) {
952 if(!a.args[k].isVar)
continue;
953 auto key = std::make_pair(a.rel,
static_cast<int>(k));
954 auto it = firstAtPos.find(key);
955 if(it==firstAtPos.end()) firstAtPos[key]=a.args[k].v;
956 else uf.uni(vid(it->second), vid(a.args[k].v));
960 std::vector<long> idToVar(
id.size());
961 for(
auto &kv :
id) idToVar[kv.second]=kv.first;
964 for(
int i=0;i<static_cast<int>(
id.size());++i) roots.insert(uf.find(i));
966 for(
int root : roots) {
968 std::vector<std::set<long>> cvars(s.size());
969 for(std::size_t di=0; di<s.size() && covers; ++di) {
970 const Disjunct &d = s[di];
971 for(
const auto &a : d) {
973 for(
const auto &t : a.args)
974 if(t.isVar && uf.find(vid(t.v))==root) {
975 atomHas=
true; cvars[di].insert(t.v);
977 if(!atomHas){ covers=
false;
break; }
980 if(!covers)
continue;
988 for(
const auto &cv : cvars)
if(cv.size() != 1) { single =
false;
break; }
989 if(!single)
continue;
993 std::set<std::pair<unsigned,int>> occ;
994 for(
const auto &d : s)
for(
const auto &a : d)
995 for(std::size_t k=0;k<a.args.size();++k)
996 if(a.args[k].isVar && uf.find(vid(a.args[k].v))==root)
997 occ.insert({a.rel,
static_cast<int>(k)});
1005 std::set<unsigned> rels;
1007 for(
const auto &rp : occ)
1008 if(!rels.insert(rp.first).second) { onePos =
false;
break; }
1009 if(!onePos)
continue;
1011 occOut = std::move(occ);
1012 classVarsPerDisj = std::move(cvars);
1020pg_uuid_t MobiusCompiler::mobiusStep(
const Sentence &s,
bool top)
1024 std::vector<Disjunct> literals;
1025 auto litId = [&](
const Disjunct &c)->
int{
1026 for(std::size_t i=0;i<literals.size();++i)
1027 if(equiv(literals[i], c))
return static_cast<int>(i);
1028 literals.push_back(c);
1029 return static_cast<int>(literals.size()-1);
1031 std::vector<std::set<int>> terms;
1032 for(
const auto &d : s) {
1034 for(
const auto &c : componentsOf(d)) t.insert(litId(c));
1035 terms.push_back(std::move(t));
1037 if(top) st.n_components =
static_cast<int>(literals.size());
1041 const int L =
static_cast<int>(literals.size());
1042 std::vector<std::vector<char>> imp(L, std::vector<char>(L, 0));
1043 for(
int i=0;i<L;++i)
for(
int j=0;j<L;++j)
1044 imp[i][j] = hom(literals[j], literals[i]) ? 1 : 0;
1048 auto minimiseDisj = [&](std::set<int> in)->std::vector<int>{
1049 std::vector<int> v(in.begin(), in.end());
1050 std::vector<char> keep(v.size(),1);
1051 for(std::size_t a=0;a<v.size();++a)
for(std::size_t b=0;b<v.size();++b)
1052 if(a!=b && keep[b] && imp[v[a]][v[b]]) { keep[a]=0;
break; }
1053 std::vector<int> out;
1054 for(std::size_t a=0;a<v.size();++a)
if(keep[a]) out.push_back(v[a]);
1055 std::sort(out.begin(), out.end());
1061 std::set<std::vector<int>> clauseSet;
1062 std::vector<std::set<int>> clauses;
1064 std::vector<int> pick(terms.size(), 0);
1066 unsigned long prod = 1;
1067 for(
const auto &t : terms) prod *= std::max<std::size_t>(1, t.size());
1069 throw MobiusDecline(
"Möbius: DNF->CNF transversal count too large");
1070 std::function<void(std::size_t,std::set<int>&)> gen =
1071 [&](std::size_t ti, std::set<int> &acc){
1072 if(ti==terms.size()){
1073 std::vector<int> mn = minimiseDisj(acc);
1074 if(!mn.empty()) clauseSet.insert(mn);
1077 for(
int lit : terms[ti]) {
1078 bool fresh = acc.insert(lit).second;
1080 if(fresh) acc.erase(lit);
1083 { std::set<int> acc; gen(0, acc); }
1088 std::vector<std::vector<int>> cl(clauseSet.begin(), clauseSet.end());
1089 auto disjImplies = [&](
const std::vector<int>&A,
const std::vector<int>&B){
1090 for(
int a : A){
bool ok=
false;
for(
int b : B)
if(imp[a][b]){ ok=
true;
break; }
1091 if(!ok)
return false; }
1094 std::vector<char> keepCl(cl.size(),1);
1095 for(std::size_t a=0;a<cl.size();++a)
for(std::size_t b=0;b<cl.size();++b)
1096 if(a!=b && keepCl[a] && cl[a]!=cl[b] && disjImplies(cl[a],cl[b]))
1098 std::vector<std::vector<int>> M;
1099 for(std::size_t a=0;a<cl.size();++a)
if(keepCl[a]) M.push_back(cl[a]);
1101 const int m =
static_cast<int>(M.size());
1102 if(top) { st.n_cnf_conjuncts = m; st.lattice_size = (m<=30)?(1<<m):0; }
1111 for(
int lid : M[0]) el.push_back(literals[lid]);
1112 if(canonSentence(el) != canonSentence(s))
1113 return compile(el, top);
1116 throw MobiusDecline(
"Möbius: no inclusion-exclusion structure (M<2); "
1117 "sentence has no separator and does not decompose");
1119 throw MobiusDecline(
"Möbius: CNF conjunct count exceeds the cap "
1120 "(provsql.mobius_max_cnf)");
1125 std::map<std::vector<int>,
long> coeff;
1126 for(
unsigned mask=1; mask < (1u<<m); ++mask) {
1128 for(
int i=0;i<m;++i)
if(mask & (1u<<i))
1129 lits.insert(M[i].begin(), M[i].end());
1130 std::vector<int> key = minimiseDisj(lits);
1131 const int pc = __builtin_popcount(mask);
1132 coeff[key] += (pc & 1) ? 1 : -1;
1134 if(top) st.lattice_collapsed =
static_cast<int>(coeff.size());
1138 std::vector<pg_uuid_t> children;
1139 std::vector<long> coeffs;
1141 bool cancelledHard =
false;
1142 for(
const auto &kv : coeff) {
1143 if(kv.second == 0) {
1149 if(!cancelledHard) {
1151 for(
int lid : kv.first) el.push_back(literals[lid]);
1152 std::set<std::pair<unsigned,int>> occ;
1153 std::vector<std::set<long>> cv;
1155 if(el.size() >= 2 && !findSeparator(el, occ, cv)) {
1157 UF uf(
static_cast<int>(el.size()));
1158 std::map<unsigned,int> firstRelDisj;
1159 for(std::size_t i=0;i<el.size();++i)
1160 for(
const auto &a : el[i])
1161 {
auto it=firstRelDisj.find(a.rel);
1162 if(it==firstRelDisj.end()) firstRelDisj[a.rel]=
static_cast<int>(i);
1163 else uf.uni(it->second,
static_cast<int>(i)); }
1164 std::set<int> r;
for(std::size_t i=0;i<el.size();++i) r.insert(uf.find(
static_cast<int>(i)));
1165 if(r.size()==1) cancelledHard =
true;
1171 for(
int lid : kv.first) el.push_back(literals[lid]);
1172 children.push_back(compile(el,
false));
1173 coeffs.push_back(kv.second);
1175 if(top) { st.n_cancelled = cancelled; st.cancelled_hard = cancelledHard;
1176 st.n_nonzero =
static_cast<int>(children.size()); }
1178 if(children.empty())
1179 return mkConst(
false);
1183 if(top && !pending_lineage.empty()) {
1184 lineage_consumed =
true;
1185 return mkMobius(children, coeffs, pending_lineage);
1187 return mkMobius(children, coeffs);
1192pg_uuid_t MobiusCompiler::compile(
const Sentence &sentence0,
bool top)
1194 CHECK_FOR_INTERRUPTS();
1201 for(
const Disjunct &d0 : sentence0) {
1203 for(
const MAtom &a : d0) {
1205 std::vector<long> el;
1206 for(
const auto &t : a.args) el.push_back(t.v);
1207 if(!fi.isPresent(a.rel, el)) { dead=
true;
break; }
1210 if(!dead && !d0.empty()) s.push_back(d0);
1213 return mkConst(
false);
1218 auto isGround = [&](
const Disjunct &d){
1219 for(
const auto &a : d)
if(!ground(a))
return false;
1228 auto tokenAnd = [&](
const Disjunct &d)->pg_uuid_t {
1229 std::vector<pg_uuid_t> toks;
1230 for(
const auto &a : d) {
1231 std::vector<long> el;
for(
const auto &t : a.args) el.push_back(t.v);
1232 const std::string *tk = fi.token(a.rel, el);
1233 if(tk==
nullptr)
return mkConst(
false);
1234 if(tk->empty())
continue;
1237 return mkBool(
true, toks);
1241 const std::string key = canonSentence(s);
1243 auto it = memo.find(key);
1244 if(it!=memo.end()) { ++st.memo_hits;
return string2uuid(it->second); }
1254 const int n =
static_cast<int>(s.size());
1256 std::vector<Footprint> fps;
1258 for(
const auto &d : s) fps.push_back(footprintOf(d));
1259 for(
int i=0;i<n;++i)
1260 for(
int j=i+1;j<n;++j)
1261 if(!disjointFootprints(fps[i], fps[j])) uf.uni(i, j);
1262 std::map<int, Sentence> groups;
1263 for(
int i=0;i<n;++i) groups[uf.find(i)].push_back(s[i]);
1264 if(groups.size() > 1) {
1265 std::vector<pg_uuid_t> ch;
1266 for(
auto &kv : groups) ch.push_back(compile(kv.second,
false));
1267 result = mkBool(
false, ch);
1276 if(isGround(s[0])) {
1277 result = tokenAnd(s[0]);
1288 std::vector<Disjunct> comps = componentsOf(s[0]);
1289 if(comps.size() > 1) {
1290 std::vector<Footprint> fps;
1291 fps.reserve(comps.size());
1292 for(
const auto &c : comps) fps.push_back(footprintOf(c));
1294 for(std::size_t i=0;i<comps.size() && indep;++i)
1295 for(std::size_t j=i+1;j<comps.size();++j)
1296 if(!disjointFootprints(fps[i], fps[j])) { indep=
false;
break; }
1298 std::vector<pg_uuid_t> ch;
1299 for(
auto &c : comps) { Sentence one{c}; ch.push_back(compile(one,
false)); }
1300 result = mkBool(
true, ch);
1311 std::set<std::pair<unsigned,int>> occ;
1312 std::vector<std::set<long>> classVars;
1313 if(findSeparator(s, occ, classVars)) {
1316 for(
const auto &rp : occ) {
1317 auto it = fi.domain.find(rp);
1318 if(it!=fi.domain.end()) dom.insert(it->second.begin(), it->second.end());
1320 std::vector<pg_uuid_t> ch;
1324 sub.reserve(s.size());
1325 for(std::size_t di=0; di<s.size(); ++di) {
1327 for(
const MAtom &at : s[di]) {
1329 for(
auto &t : na.args)
1330 if(t.isVar && classVars[di].count(t.v)) { t.isVar=
false; t.v=a; }
1331 nd.push_back(std::move(na));
1333 sub.push_back(std::move(nd));
1335 ch.push_back(compile(sub,
false));
1337 result = mkBool(
false, ch);
1350 if(s.size()==1 && componentsOf(s[0]).size() < 2)
1351 throw MobiusDecline(
"Möbius: non-hierarchical single CQ (no separator); "
1352 "the query is #P-hard");
1353 result = mobiusStep(s, top);
1362pg_uuid_t compileNormalized(
const Sentence &s,
const FactIndex &fi,
1363 MobiusStats &st,
const std::string &lineage =
"")
1367 const bool norm = normalizeShards(s, fi, sn, fin);
1369 provsql_notice(
"ucq_mobius: ranking/shattering: %d disjuncts -> %d: %s",
1370 static_cast<int>(s.size()),
static_cast<int>(sn.size()),
1371 canonSentence(sn).c_str());
1372 MobiusCompiler mc(norm ? fin : fi, st);
1373 return mc.compileTop(norm ? sn : s, lineage);
1380int checkedLen(ArrayType *a,
const char *what) {
1381 if(a==NULL)
return 0;
1382 if(ARR_NDIM(a)>1)
provsql_error(
"ucq_mobius: %s must be 1-D", what);
1383 if(ARR_HASNULL(a))
provsql_error(
"ucq_mobius: %s must not contain NULLs", what);
1384 return ARR_NDIM(a)==0 ? 0 : ARR_DIMS(a)[0];
1386const int32 *intArr(FunctionCallInfo fcinfo,
int n,
const char *what,
int &len){
1387 ArrayType *a = PG_ARGISNULL(n)?NULL:PG_GETARG_ARRAYTYPE_P(n);
1388 len = checkedLen(a, what);
1389 return (a==NULL||len==0)?NULL:(
const int32*)ARR_DATA_PTR(a);
1396Sentence buildSentenceArrays(
const int32 *d_nvars,
int n_disj,
1397 const int32 *a_disj,
const int32 *a_rel,
1398 const int32 *a_vars,
int n_av,
1399 const int32 *a_arity,
int n_ad,
1400 std::vector<long> &base)
1402 if(n_disj==0)
provsql_error(
"ucq_mobius: the UCQ has no disjuncts");
1403 base.assign(n_disj, 0);
1405 for(
int d=0; d<n_disj; ++d){ base[d]=acc; acc += d_nvars[d] + 1; }
1409 for(
int i=0;i<n_ad;++i){
1410 const int d=a_disj[i];
1411 if(d<0||d>=n_disj)
provsql_error(
"ucq_mobius: atom disjunct out of range");
1412 const int ar=a_arity[i];
1413 if(ar<0||voff+ar>n_av)
1414 provsql_error(
"ucq_mobius: atom_vars shorter than arities");
1415 MAtom at; at.rel=
static_cast<unsigned>(a_rel[i]);
1416 for(
int k=0;k<ar;++k){
1417 Term t; t.isVar=
true; t.v = base[d] + a_vars[voff+k];
1418 at.args.push_back(t);
1421 s[d].push_back(std::move(at));
1428FactIndex buildFactIndexArrays(
const int32 *f_rel,
int n_fr,
1429 const int32 *f_elems,
int n_fe,
1430 const int32 *f_arity,
1431 const pg_uuid_t *tok)
1446 std::map<std::string, std::pair<unsigned, std::vector<long>>> token_owner;
1448 for(
int i=0;i<n_fr;++i){
1449 const int ar=f_arity[i];
1450 if(ar<0||eoff+ar>n_fe)
1451 provsql_error(
"ucq_mobius: fact_elems shorter than arities");
1452 unsigned rel=
static_cast<unsigned>(f_rel[i]);
1453 std::vector<long> el;
1454 for(
int k=0;k<ar;++k){
1455 long e=
static_cast<long>(f_elems[eoff+k]);
1457 fi.domain[{rel,k}].insert(e);
1460 fi.present.insert({rel, el});
1462 for(
int b=0;b<16;++b)
if(tok[i].data[b]!=0) nil=
false;
1464 pg_uuid_t tk = tok[i];
1465 const Datum gt = DirectFunctionCall1(
get_gate_type, UUIDPGetDatum(&tk));
1466 if(
static_cast<Oid
>(DatumGetInt32(gt)) != input_oid)
1467 throw MobiusDecline(
"non-TID input: a fact token is not a bare "
1468 "gate_input (correlated / derived lineage)");
1479 const std::string newtok = nil ? std::string() :
uuid2string(tok[i]);
1480 auto existing = fi.tok.find({rel, el});
1481 if(existing != fi.tok.end() && existing->second != newtok)
1482 throw MobiusDecline(
"bag multiplicity: two distinct tuples share an "
1483 "element tuple (non-reduced data) -- defer to "
1484 "joint-width, which keeps the multiplicity");
1494 if(!newtok.empty()) {
1495 auto own = token_owner.find(newtok);
1496 if(own != token_owner.end() && own->second != std::make_pair(rel, el))
1497 throw MobiusDecline(
"self-join overlap: one tuple feeds two fact slots "
1498 "(non-disjoint self-join) -- defer to joint-width");
1499 token_owner[newtok] = {rel, el};
1501 fi.tok[{rel, el}] = newtok;
1507Sentence decodeQuery(FunctionCallInfo fcinfo)
1509 int n_disj,n_ad,n_ar,n_av,n_aa;
1510 const int32 *d_nvars = intArr(fcinfo,0,
"disjunct_nvars",n_disj);
1511 const int32 *a_disj = intArr(fcinfo,1,
"atom_disjunct",n_ad);
1512 const int32 *a_rel = intArr(fcinfo,2,
"atom_rel",n_ar);
1513 const int32 *a_vars = intArr(fcinfo,3,
"atom_vars",n_av);
1514 const int32 *a_arity = intArr(fcinfo,4,
"atom_arity",n_aa);
1515 if(n_ad!=n_ar || n_ad!=n_aa)
1516 provsql_error(
"ucq_mobius: atom arrays must have the same length");
1517 std::vector<long> base;
1518 return buildSentenceArrays(d_nvars,n_disj,a_disj,a_rel,a_vars,n_av,
1523FactIndex decodeFacts(FunctionCallInfo fcinfo)
1525 int n_fr,n_fe,n_fa,n_ft;
1526 const int32 *f_rel = intArr(fcinfo,5,
"fact_rel",n_fr);
1527 const int32 *f_elems = intArr(fcinfo,6,
"fact_elems",n_fe);
1528 const int32 *f_arity = intArr(fcinfo,7,
"fact_arity",n_fa);
1529 ArrayType *toks = PG_ARGISNULL(8)?NULL:PG_GETARG_ARRAYTYPE_P(8);
1530 n_ft = checkedLen(toks,
"fact_tokens");
1531 if(n_fr!=n_fa || n_fr!=n_ft)
1532 provsql_error(
"ucq_mobius: fact arrays must have the same length");
1533 const pg_uuid_t *tok = (toks&&n_ft)?(
const pg_uuid_t*)ARR_DATA_PTR(toks):NULL;
1534 return buildFactIndexArrays(f_rel,n_fr,f_elems,n_fe,f_arity,tok);
1547struct MobAnswerCache {
1550 std::vector<long> base;
1551 std::vector<int> d_nvars;
1553 std::map<std::string,long> val_to_id;
1554 std::map<std::string,std::string> tokcache;
1557void mobAnswerCacheDelete(
void *arg) {
delete reinterpret_cast<MobAnswerCache*
>(arg); }
1559std::string mobHeadKey(
const std::vector<std::string> &vals)
1562 for(
const auto &v : vals){ k += v; k.push_back(
'\x1f'); }
1568bool mobGather(Datum descriptor, MobAnswerCache *c)
1571 Oid argt[1] = { JSONBOID };
1572 Datum argv[1] = { descriptor };
1573 char argn[1] = {
' ' };
1574 const int rc = SPI_execute_with_args(
1575 "SELECT * FROM provsql.ucq_joint_gather($1)", 1, argt, argv, argn,
true, 1);
1576 if(rc != SPI_OK_SELECT || SPI_processed != 1) { SPI_finish();
return false; }
1580 TupleDesc td = SPI_tuptable->tupdesc;
1581 HeapTuple row = SPI_tuptable->vals[0];
1583 auto ia = [&](
int col,
int &n)->
const int32*{
1584 Datum d = SPI_getbinval(row, td, col, &isnull);
1585 if(isnull){ n=0;
return nullptr; }
1586 ArrayType *a = DatumGetArrayTypeP(d);
1587 n = ArrayGetNItems(ARR_NDIM(a), ARR_DIMS(a));
1588 return (
const int32*) ARR_DATA_PTR(a);
1590 int n_dnv,n_adisj,n_arel,n_avars,n_aarity,n_frel,n_felems,n_farity;
1591 const int32 *dnv = ia(1,n_dnv);
1592 const int32 *adisj = ia(2,n_adisj);
1593 const int32 *arel = ia(3,n_arel);
1594 const int32 *avars = ia(4,n_avars);
1595 const int32 *aarity = ia(5,n_aarity);
1596 const int32 *frel = ia(6,n_frel);
1597 const int32 *felems = ia(7,n_felems);
1598 const int32 *farity = ia(8,n_farity);
1599 Datum dtok = SPI_getbinval(row, td, 9, &isnull);
1600 ArrayType *atok = isnull ? nullptr : DatumGetArrayTypeP(dtok);
1601 const int n_ftok = atok ? ArrayGetNItems(ARR_NDIM(atok), ARR_DIMS(atok)) : 0;
1602 const pg_uuid_t *ftok = atok ? (
const pg_uuid_t*) ARR_DATA_PTR(atok) : nullptr;
1603 if(n_frel != n_farity || n_frel != n_ftok)
1604 throw MobiusDecline(
"ucq_mobius: fact arrays length mismatch");
1606 c->sentence = buildSentenceArrays(dnv,n_dnv,adisj,arel,avars,n_avars,
1607 aarity,n_adisj,c->base);
1608 c->d_nvars.assign(dnv, dnv+n_dnv);
1609 c->fi = buildFactIndexArrays(frel,n_frel,felems,n_felems,farity,ftok);
1612 Datum dval = SPI_getbinval(row, td, 10, &isnull);
1614 ArrayType *aval = DatumGetArrayTypeP(dval);
1615 Datum *elems;
bool *nulls;
int nval;
1616 deconstruct_array(aval, TEXTOID, -1,
false,
TYPALIGN_INT,
1617 &elems, &nulls, &nval);
1618 for(
int i=0;i<nval;++i)
1620 c->val_to_id[TextDatumGetCString(elems[i])] = i;
1651 Sentence s = decodeQuery(fcinfo);
1652 FactIndex fi = decodeFacts(fcinfo);
1653 std::string lineage;
1654 if(!PG_ARGISNULL(9))
1657 pg_uuid_t root = compileNormalized(s, fi, st, lineage);
1660 PG_RETURN_UUID_P(u);
1661 }
catch(
const MobiusDecline &e) {
1663 }
catch(
const std::exception &e) {
1679 Sentence s = decodeQuery(fcinfo);
1680 FactIndex fi = decodeFacts(fcinfo);
1682 pg_uuid_t root = compileNormalized(s, fi, st);
1686 if(get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
1687 provsql_error(
"ucq_mobius_compile_stats: expected composite return type");
1688 tupdesc = BlessTupleDesc(tupdesc);
1690 bool nulls[9] = {
false,
false,
false,
false,
false,
false,
false,
false,
false};
1691 values[0] = Float8GetDatum(st.probability);
1692 values[1] = Int32GetDatum(st.n_components);
1693 values[2] = Int32GetDatum(st.n_cnf_conjuncts);
1694 values[3] = Int32GetDatum(st.lattice_collapsed);
1695 values[4] = Int32GetDatum(st.n_nonzero);
1696 values[5] = Int32GetDatum(st.n_cancelled);
1697 values[6] = BoolGetDatum(st.cancelled_hard);
1698 values[7] = Int64GetDatum(
static_cast<int64
>(st.dd_size));
1699 values[8] = Int64GetDatum(
static_cast<int64
>(st.memo_hits));
1700 PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls)));
1701 }
catch(
const std::exception &e) {
1704 provsql_error(
"ucq_mobius_compile_stats: unknown exception");
1723 MobAnswerCache *
cache =
1724 reinterpret_cast<MobAnswerCache*
>(fcinfo->flinfo->fn_extra);
1726 if(
cache ==
nullptr) {
1727 MemoryContext fnctx = fcinfo->flinfo->fn_mcxt;
1728 cache =
new MobAnswerCache();
1729 MemoryContextCallback *cb = (MemoryContextCallback*)
1730 MemoryContextAllocZero(fnctx,
sizeof(MemoryContextCallback));
1731 cb->func = mobAnswerCacheDelete;
1733 MemoryContextRegisterResetCallback(fnctx, cb);
1734 fcinfo->flinfo->fn_extra =
cache;
1736 if(!PG_ARGISNULL(0)) {
1738 MemoryContext oldcxt = CurrentMemoryContext;
1739 ResourceOwner oldowner = CurrentResourceOwner;
1740 BeginInternalSubTransaction(NULL);
1743 cache->ready = mobGather(PG_GETARG_DATUM(0),
cache);
1744 ReleaseCurrentSubTransaction();
1745 MemoryContextSwitchTo(oldcxt);
1746 CurrentResourceOwner = oldowner;
1750 MemoryContextSwitchTo(oldcxt);
1751 RollbackAndReleaseCurrentSubTransaction();
1752 MemoryContextSwitchTo(oldcxt);
1753 CurrentResourceOwner = oldowner;
1755 cache->ready =
false;
1762 if(
cache->ready && !PG_ARGISNULL(1) && !PG_ARGISNULL(2)) {
1763 std::vector<int> head_vars;
1765 ArrayType *a = PG_GETARG_ARRAYTYPE_P(1);
1766 const int32 *d = (
const int32*) ARR_DATA_PTR(a);
1767 const int n = ArrayGetNItems(ARR_NDIM(a), ARR_DIMS(a));
1768 for(
int i=0;i<n;++i) head_vars.push_back(d[i]);
1770 std::vector<std::string> head_vals;
1772 ArrayType *a = PG_GETARG_ARRAYTYPE_P(2);
1773 Datum *elems;
bool *nulls;
int n;
1774 deconstruct_array(a, TEXTOID, -1,
false,
TYPALIGN_INT, &elems, &nulls, &n);
1775 for(
int i=0;i<n;++i)
1776 head_vals.push_back(nulls[i] ? std::string() : TextDatumGetCString(elems[i]));
1779 if(head_vars.size() == head_vals.size()) {
1780 const std::string key = mobHeadKey(head_vals);
1781 auto it =
cache->tokcache.find(key);
1782 if(it !=
cache->tokcache.end()) {
1785 PG_RETURN_UUID_P(u);
1790 Sentence s =
cache->sentence;
1791 bool resolved =
true;
1792 for(std::size_t h=0; h<head_vars.size() && resolved; ++h) {
1793 auto vit =
cache->val_to_id.find(head_vals[h]);
1794 if(vit ==
cache->val_to_id.end()) { resolved =
false;
break; }
1795 const long val = vit->second;
1796 const int hv = head_vars[h];
1797 for(std::size_t d=0; d<s.size(); ++d) {
1798 const long gid =
cache->base[d] + hv;
1799 for(MAtom &at : s[d])
1800 for(Term &t : at.args)
1801 if(t.isVar && t.v == gid) { t.isVar=
false; t.v=val; }
1808 std::string lineage;
1809 if(!PG_ARGISNULL(3))
1816 PG_RETURN_UUID_P(u);
1818 }
catch(
const std::exception &e) {
1831 PG_RETURN_DATUM(PG_GETARG_DATUM(3));
pg_uuid_t provsqlUuidV5(const std::string &name)
RFC 4122 version-5 UUID in the ProvSQL namespace.
Content-addressed materialisation of a certified d-D into the mmap provenance store.
static CircuitCache cache
Process-local singleton circuit gate cache.
bool operator<(gate_t t, std::vector< gate_t >::size_type u)
Compare a gate_t against a std::vector size type.
Fix macro conflicts between PostgreSQL headers and the C++ STL/Boost.
PostgreSQL cross-version compatibility shims for ProvSQL.
#define TYPALIGN_INT
Alignment codes for the array routines (construct_array / deconstruct_array).
Datum ucq_mobius_materialize_tracked(PG_FUNCTION_ARGS)
Materialise the safe-UCQ Möbius circuit and return its root token.
Datum ucq_mobius_provenance_answer(PG_FUNCTION_ARGS)
Per-answer Möbius provenance (the planner-substituted entry point for a non-Boolean UCQ with free hea...
Datum get_gate_type(PG_FUNCTION_ARGS)
int provsql_mobius_max_gates
Data-cost cap of the Möbius route: it declines (falling through to joint-width / the ladder) once its...
Datum ucq_mobius_compile_stats(PG_FUNCTION_ARGS)
Compile the Möbius circuit and return the lattice statistics plus the probability (the demonstrabilit...
Shared declaration for the Möbius-route probability sweep.
double mobius_probability_of(pg_uuid_t token)
Probability of a Möbius-route token (a gate_mobius-rooted circuit, or any Boolean island beneath one)...
int provsql_verbose
Verbosity level; controlled by the provsql.verbose_level GUC.
int provsql_mobius_max_cnf
Query-cost cap of the Möbius route: it declines when a sentence's CNF has more than this many conjunc...
#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 provsql_internal_set_extra(const pg_uuid_t *token, const char *str)
Internal entry point behind set_extra(): worker IPC only.
void provsql_internal_create_gate(const pg_uuid_t *token, gate_type type, unsigned nb_children, const pg_uuid_t *children_data)
Internal entry point behind create_gate(): cache + worker IPC.
Background worker and IPC primitives for mmap-backed circuit storage.
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_mobius
Signed Möbius combination: a MEASURE-only gate carrying one integer coefficient per child (in extra,...
pg_uuid_t string2uuid(const string &source)
Parse a UUID string into a pg_uuid_t.
string uuid2string(pg_uuid_t uuid)
Format a pg_uuid_t as a std::string.
bool operator==(const pg_uuid_t &u, const pg_uuid_t &v)
Test two pg_uuid_t values for equality.
C++ utility functions for UUID manipulation.
Oid GATE_TYPE_TO_OID[nb_gate_types]
Array of the OID of each provenance_gate ENUM value.
Oid OID_FUNCTION_PROVENANCE_PLUS
OID of the provenance_plus FUNCTION.
Oid OID_FUNCTION_PROVENANCE_TIMES
OID of the provenance_times FUNCTION.