diff --git a/parser/boundargs.py b/parser/boundargs.py index 1efaff8..5807b0c 100644 --- a/parser/boundargs.py +++ b/parser/boundargs.py @@ -49,6 +49,12 @@ definition of each function it calls is read, and a function whose parameter names equal a member's is the generic that member wraps: its literals bind to the member by parameter name. + +A function MEOS takes from PostgreSQL is public under PostgreSQL's name only in MEOS, where +it calls its ``pg_`` twin, which takes the same parameters in the same order; the extension, +which links PostgreSQL's own function of that name, calls the twin: +``Jsonb_path_exists_common`` calls ``pg_jsonb_path_exists(jb, jp, vars, silent, tz)`` +for ``jsonb_path_exists``. A call to the twin is read as a call to the member. """ from __future__ import annotations @@ -310,7 +316,7 @@ def _wrapper_bound(body: str, func: dict, drift: list, array length (``@param[in] count``), an aggregate state (``@param[in,out] state``) — never a hard-coded literal, so it is skipped systematically. Only a bare identifier for an UNDOCUMENTED parameter is reported as drift (the exceptional manual gap).""" - args = _call_args(body, func["name"]) + args = _call_args(body, func["name"]) or _call_args(body, "pg_" + func["name"]) if not args: return {} subst = subst or {} diff --git a/tests/test_boundargs.py b/tests/test_boundargs.py index fb6b2cd..eec7e28 100644 --- a/tests/test_boundargs.py +++ b/tests/test_boundargs.py @@ -173,6 +173,85 @@ def test_caller_read_args_stay_out(self): self.assertNotIn("key", bound) +PG_TWIN = """ +static Datum +Jsonb_path_exists_common(FunctionCallInfo fcinfo, bool tz) +{ + Jsonb *jb = PG_GETARG_JSONB_P(0); + JsonPath *jp = PG_GETARG_JSONPATH_P(1); + Jsonb *vars = PG_GETARG_JSONB_P(2); + bool silent = PG_GETARG_BOOL(3); + int result = pg_jsonb_path_exists(jb, jp, vars, silent, tz); + PG_RETURN_BOOL(result == 1); +} + +PGDLLEXPORT Datum Jsonb_path_exists(PG_FUNCTION_ARGS); +PG_FUNCTION_INFO_V1(Jsonb_path_exists); +Datum +Jsonb_path_exists(PG_FUNCTION_ARGS) +{ + return Jsonb_path_exists_common(fcinfo, false); +} + +PGDLLEXPORT Datum Jsonb_path_exists_tz(PG_FUNCTION_ARGS); +PG_FUNCTION_INFO_V1(Jsonb_path_exists_tz); +Datum +Jsonb_path_exists_tz(PG_FUNCTION_ARGS) +{ + return Jsonb_path_exists_common(fcinfo, true); +} + +static Datum +Jsonb_path_match_common(FunctionCallInfo fcinfo, bool tz) +{ + Jsonb *jb = PG_GETARG_JSONB_P(0); + JsonPath *jp = PG_GETARG_JSONPATH_P(1); + bool result = path_match_other(jb, jp, tz); + PG_RETURN_BOOL(result); +} + +PGDLLEXPORT Datum Jsonb_path_match(PG_FUNCTION_ARGS); +PG_FUNCTION_INFO_V1(Jsonb_path_match); +Datum +Jsonb_path_match(PG_FUNCTION_ARGS) +{ + return Jsonb_path_match_common(fcinfo, false); +} +""" + + +class PgTwinTests(unittest.TestCase): + """A wrapper reaching a function MEOS takes from PostgreSQL through its pg_ twin, the + delegation #DelegatingWrapperTests reads, whose helper calls the twin.""" + + def setUp(self): + self.tmp = tempfile.TemporaryDirectory() + src = Path(self.tmp.name) / "src" + src.mkdir() + (src / "twin.c").write_text(PG_TWIN) + + def tearDown(self): + self.tmp.cleanup() + + def _idl(self, name, wrapper): + return {"functions": [ + {"name": name, "mdbC": wrapper, + "params": [{"name": "jb"}, {"name": "jp"}, {"name": "vars"}, + {"name": "silent"}, {"name": "tz"}]}]} + + def test_the_twin_binds_the_literal_of_each_wrapper(self): + for wrapper, tz in (("Jsonb_path_exists", "false"), ("Jsonb_path_exists_tz", "true")): + idl, _, drift = merge_boundargs(self._idl("jsonb_path_exists", wrapper), + self.tmp.name) + self.assertEqual(idl["functions"][0]["shape"]["boundArgs"], {"tz": tz}) + self.assertEqual(drift, []) + + def test_a_callee_that_is_not_the_twin_binds_nothing(self): + idl, _, _ = merge_boundargs(self._idl("jsonb_path_match", "Jsonb_path_match"), + self.tmp.name) + self.assertNotIn("boundArgs", idl["functions"][0].get("shape") or {}) + + class BoundArgsTests(unittest.TestCase): def setUp(self): self.tmp = tempfile.TemporaryDirectory()