From 8dcb9699e5c31bfb4f05429ea62cb37cc51eb1bd Mon Sep 17 00:00:00 2001 From: Esteban Zimanyi Date: Wed, 15 Jul 2026 19:51:42 +0200 Subject: [PATCH] Generate aggregate identity from the @csqlaggfn tag Read @csqlaggfn from meos/src so each transition/combine/final function records the SQL aggregate role it implements in a new `sqlAgg` catalog field. This gives an aggregate member its own identity (setUnionTransition, spanUnionFinal) distinct from the identically named binary set/span union function, so a binding can reconstruct the standard PostgreSQL aggregate model rather than guess from name suffixes. The tag is one hop: the #Name() references are the SQL aggregate-role names directly, with no wrapper indirection to resolve. A member shared by two aggregates (spanset_union_finalfn) carries a list of names, recorded verbatim. --- parser/sqlfn.py | 53 +++++++++++++++++++++++++++++++++++++++++++++++++ run.py | 10 ++++++++-- 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/parser/sqlfn.py b/parser/sqlfn.py index 27692a4..07e1037 100644 --- a/parser/sqlfn.py +++ b/parser/sqlfn.py @@ -25,6 +25,14 @@ _CSQLFN = re.compile(r"@csqlfn\b") _CSQLFN_REF = re.compile(r"#(\w+)\s*\(\)") _CSQLFN_END = re.compile(r"@\w|\*/") +# @csqlaggfn names the SQL AGGREGATE(s) a transition/combine/final function +# implements, following the standard PostgreSQL aggregate model +# (Transition / Combine / Final). Unlike @csqlfn — which points at a PG +# wrapper that then carries the @sqlfn (two hops) — @csqlaggfn is ONE hop: the +# #Name() reference IS the SQL aggregate-role name (#setUnionTransition()). A member +# shared by two aggregates (the spanset union finalfn) carries both, so collect all +# references (reusing _CSQLFN_REF / _CSQLFN_END, the same value grammar). +_CSQLAGGFN = re.compile(r"@csqlaggfn\b") # After the doxygen close, the MEOS-C definition. The return type may sit on its # own line (`bool\nleft_tpcbox_tpcbox(`) OR on the same line as the name # (`bool tpcbox_eq(const TPCBox *box1, ...)`, the one-line predicate style). Match @@ -185,6 +193,32 @@ def _meos_to_mdb(meos_src): return out +def _meos_agg_names(meos_src): + """MEOS-C aggregate function name -> ordered list of SQL aggregate-role names + (from @csqlaggfn). One hop: the #Name() references ARE the SQL names + (#setUnionTransition()), so there is no wrapper indirection to resolve — unlike + _meos_to_mdb, whose #Wrapper() references need a second _mdb_to_sql hop. A member + shared by two aggregates (spanset_union_finalfn) carries several names.""" + out = {} + for cf in Path(meos_src).rglob("*.c"): + text = cf.read_text(errors="ignore") + for m in _CSQLAGGFN.finditer(text): + tail = text[m.end():] + end = _CSQLFN_END.search(tail) + value = tail[:end.start()] if end else tail + names = _CSQLFN_REF.findall(value) + if not names: + continue + fm = _FNDEF.search(text, m.end()) + if not fm: + continue + lst = out.setdefault(fm.group(1), []) + for nm in names: + if nm not in lst: + lst.append(nm) + return out + + def _mdb_to_sql(mdb_src): """MobilityDB-C wrapper name -> ordered list of (sqlfn, sqlop). @@ -284,6 +318,25 @@ def attach_sqlfn_map(idl, meos_src, mdb_src, sql_src=None): return idl, n, multi +def attach_aggfn_map(idl, meos_src): + """Attach `sqlAgg` — the SQL aggregate-role name(s) each aggregate function + implements, read faithfully from @csqlaggfn in meos/src. This gives an + aggregate member its own catalog identity (setUnionTransition, spanUnionFinal) + distinct from the identically named binary set/span union FUNCTION, and lets a + binding reconstruct the standard PostgreSQL aggregate model (a with + its Transition / Combine / Final members) instead of guessing from name + suffixes. A member shared by two aggregates (spanset_union_finalfn) carries a + list. Faithful reader: the name is recorded verbatim, no derivation.""" + a2n = _meos_agg_names(meos_src) + n = 0 + for f in idl["functions"]: + names = a2n.get(f["name"]) + if names: + f["sqlAgg"] = names + n += 1 + return idl, n + + # MEOS-C ever/always spatial-relationship functions are named _...; their # @csqlfn must point at the matching _... wrapper. A copy-paste @csqlfn in # meos/src (e.g. eintersects_tgeo_geo tagged #Aintersects_tgeo_geo) silently flips the diff --git a/run.py b/run.py index b0a60fb..ac607d5 100644 --- a/run.py +++ b/run.py @@ -13,8 +13,8 @@ from parser.nullable import merge_nullable from parser.outparam import merge_outparams from parser.enrich import enrich_idl -from parser.sqlfn import (attach_sqlfn_map, lint_ea_sqlfn, lint_positional_sqlfn, - lint_sqlfn_case_collisions) +from parser.sqlfn import (attach_sqlfn_map, attach_aggfn_map, lint_ea_sqlfn, + lint_positional_sqlfn, lint_sqlfn_case_collisions) from parser.doxygroup import attach_groups from parser.extractors import find_unlisted_foreign_structs from parser.object_model import attach_object_model, find_mobilitydb_src @@ -132,6 +132,12 @@ def main(): if MEOS_SRC.exists() and MDB_SRC.exists(): idl, nsql, sqlfn_multi = attach_sqlfn_map(idl, MEOS_SRC, MDB_SRC, SQL_SRC) print(f"[4/4] Attached {nsql} @sqlfn SQL names", file=sys.stderr) + # Aggregate identity: @csqlaggfn names the SQL aggregate (setUnion / + # spanUnion / spansetUnion) each transition/combine/final function + # implements, so an aggregate member is distinguishable from the identically + # named binary set/span union function. One-hop, faithful to the source tag. + idl, nagg = attach_aggfn_map(idl, MEOS_SRC) + print(f" Attached {nagg} @csqlaggfn aggregate names", file=sys.stderr) # Guard: a copy-paste @csqlfn in meos/src can point an ever/always function at # the opposite-prefix wrapper (eintersects_* tagged #Aintersects_*), flipping its # SQL name and breaking the binding overload dispatch. The parser is faithful, so