diff --git a/parser/compositions.py b/parser/compositions.py index 9135b83..22831af 100644 --- a/parser/compositions.py +++ b/parser/compositions.py @@ -497,6 +497,7 @@ def attach_compositions(idl, sql_src): _CAST_CALL = re.compile(r"^\s*(\w+)\s*\(\s*(\w+)\s*\)\s*$") +_C_LITERAL = re.compile(r"^(?:true|false|-?\d+(?:\.\d+)?)$") def attach_wrapper_compositions(idl, mdb_src, sql_src, meos_src): @@ -514,7 +515,9 @@ def attach_wrapper_compositions(idl, mdb_src, sql_src, meos_src): body cannot reach. Each signature is traced to the wrapper whose CREATE FUNCTION states it (#_signature_wrapper of parser/boundargs.py) and each argument of the call to the SQL argument it carries (#_caller_index); a cast is a public catalog function of - one parameter. A signature whose wrapper casts nothing stays where it is.""" + one parameter. A C literal the wrapper passes, as ``true`` for the ``spheroid`` of + ``edwithin_tgeo_geo``, is a value operand. A signature whose wrapper casts nothing + stays where it is.""" from parser.boundargs import (_call_args, _caller_index, _signature_wrapper, extract_wrappers) from parser.sqlfn import _meos_to_mdb, _wrapper_sql_sigs @@ -545,13 +548,15 @@ def attach_wrapper_compositions(idl, mdb_src, sql_src, meos_src): r"(?])" + re.escape(a) + r"\s*=(?!=)\s*([^;]+);", body)] hit = _CAST_CALL.match(rhs[0]) if len(rhs) == 1 else None cast = hit.group(1) if hit else None - if cast in public and len(public[cast].get("params") or ()) == 1: + if _C_LITERAL.match(a): + operands.append({"value": a, "param": p}) + elif cast in public and len(public[cast].get("params") or ()) == 1: operands.append({"arg": _caller_index(body, hit.group(2)), "casts": [cast], "param": p}) cast_seen = True else: operands.append({"arg": _caller_index(body, a), "param": p}) - if not cast_seen or any(o["arg"] is None for o in operands): + if not cast_seen or any("arg" in o and o["arg"] is None for o in operands): operands = None if operands is None: keep.append(sig) diff --git a/tests/test_compositions.py b/tests/test_compositions.py index 8035f5f..7eb79b0 100644 --- a/tests/test_compositions.py +++ b/tests/test_compositions.py @@ -334,6 +334,18 @@ def test_a_cast_no_create_cast_declares_stops_the_catalog(self): int result = eintersects_tgeo_geo(temp, gs); PG_RETURN_BOOL(result ? true : false); } + +Datum +Edwithin_tpose_geo(PG_FUNCTION_ARGS) +{ + Temporal *temp = PG_GETARG_TEMPORAL_P(0); + GSERIALIZED *gs = PG_GETARG_GSERIALIZED_P(1); + double dist = PG_GETARG_FLOAT8(2); + Temporal *tpoint = tpose_to_tpoint(temp); + int result = edwithin_tgeo_geo(tpoint, gs, dist, true); + pfree(tpoint); + PG_RETURN_BOOL(result ? true : false); +} ''' CAST_SQL = ''' @@ -347,6 +359,11 @@ def test_a_cast_no_create_cast_declares_stops_the_catalog(self): AS 'MODULE_PATHNAME', 'Eintersects_tgeo_geo' SUPPORT tspatial_supportfn LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; +CREATE FUNCTION eDwithin(tpose, geometry, float) + RETURNS boolean + AS 'MODULE_PATHNAME', 'Edwithin_tpose_geo' + SUPPORT tspatial_supportfn + LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; ''' CAST_MEOS = ''' @@ -359,6 +376,17 @@ def test_a_cast_no_create_cast_declares_stops_the_catalog(self): { return 0; } + +/** + * @brief Return 1 if a temporal geo is ever within a distance of a geometry + * @csqlfn #Edwithin_tpose_geo() + */ +int +edwithin_tgeo_geo(const Temporal *temp, const GSERIALIZED *gs, double dist, + bool spheroid) +{ + return 0; +} ''' @@ -386,20 +414,32 @@ def _attach(self): "sqlSignatures": [{"args": ["tgeompoint", "geometry"], "ret": "boolean"}, {"args": ["tpose", "geometry"], "ret": "boolean"}]}, {"name": "tpose_to_tpoint", "api": "public", "params": [{"name": "temp"}], - "sqlSignatures": []}], + "sqlSignatures": []}, + {"name": "edwithin_tgeo_geo", "api": "public", "sqlfn": "eDwithin", + "params": [{"name": "temp"}, {"name": "gs"}, {"name": "dist"}, + {"name": "spheroid"}], + "sqlSignatures": [{"args": ["tpose", "geometry", "float"], "ret": "boolean"}]}], "compositions": []} return attach_wrapper_compositions(idl, root / "src", root / "sql", root / "meos") def test_the_cast_wrapper_states_a_composition(self): idl, n = self._attach() - self.assertEqual(n, 1) - self.assertEqual(idl["compositions"], [{ + self.assertEqual(n, 2) + self.assertEqual(idl["compositions"][:1], [{ "sqlName": "eIntersects", "args": ["tpose", "geometry"], "required": 2, "argDefaults": [None, None], "ret": "boolean", "operands": [{"arg": 0, "casts": ["tpose_to_tpoint"], "param": "temp"}, {"arg": 1, "param": "gs"}], "call": "eintersects_tgeo_geo"}]) + def test_a_literal_argument_is_a_value_operand(self): + idl, _ = self._attach() + self.assertEqual(idl["compositions"][1]["operands"], [ + {"arg": 0, "casts": ["tpose_to_tpoint"], "param": "temp"}, + {"arg": 1, "param": "gs"}, {"arg": 2, "param": "dist"}, + {"value": "true", "param": "spheroid"}]) + self.assertEqual(idl["functions"][2]["sqlSignatures"], []) + def test_the_wrapper_casting_nothing_keeps_its_signature(self): idl, _ = self._attach() self.assertEqual([s["args"] for s in idl["functions"][0]["sqlSignatures"]],