From 24558e97103af641e01ff55bfca9489916e7e228 Mon Sep 17 00:00:00 2001 From: Esteban Zimanyi Date: Fri, 30 Oct 2026 00:05:38 +0100 Subject: [PATCH] Read each local of a declaration list as the literal it starts from _notnull_default reads the initializer of a local up to the comma of a declaration of several locals, as _guarded_default reads it: `int nrows = 0, ncols = 0;` starts nrows from 0, where it read `0, ncols = 0` and stated no literal. Raster_set_values reads the size of its flags into both locals only where argument 5 is not NULL, so the call leaving its flags to their default NULL binds nosetheight 0 beside nosetwidth 0 and noset NULL. A local starting from a call keeps stating none. Witness: tests/test_boundargs.py DeclarationListTests, the locals of Raster_set_values; the first test fails on master. Why: J15d of MobilityDB #2861, setValues(raster, integer, integer, integer, float[]), which the typed JVM surfaces leave unregistered while the catalog binds nosetwidth and not nosetheight for it. Measured: over the headers MobilityDB master 96f10bdac8 installs, the catalog changes only in the nullDefaultBinds of setValues, which gains nosetheight 0. The 601 tests pass, master's 599 and the 2 above. Over this catalog, JMEOS codegen/two-dimensional-array-input registers the call without the flags, 9585 overloads against 9584. --- parser/boundargs.py | 4 +++- tests/test_boundargs.py | 40 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/parser/boundargs.py b/parser/boundargs.py index 7f14de6..ad4418d 100644 --- a/parser/boundargs.py +++ b/parser/boundargs.py @@ -1086,7 +1086,9 @@ def _notnull_default(body: str, var: str, nargs: int) -> tuple[int, str] | None: re.finditer(r"(?])" + v + r"\s*=(?!=)|&\s*" + v + r"\b", b)] if len(writes) < 2 or writes[0][1]: return None - init = re.match(r"\s*([^;]+?)\s*;", b[b.index("=", writes[0][0]) + 1:]) + # an initialiser ends at the comma of a declaration of several locals, `int nrows = 0, + # ncols = 0;`, as at its semicolon, as #_guarded_default reads it + init = re.match(r"\s*([^;,]+?)\s*[;,]", b[b.index("=", writes[0][0]) + 1:]) lit = _literal(init.group(1)) if init else None if lit is None: return None diff --git a/tests/test_boundargs.py b/tests/test_boundargs.py index 553475a..a256e34 100644 --- a/tests/test_boundargs.py +++ b/tests/test_boundargs.py @@ -12,7 +12,8 @@ import unittest from pathlib import Path -from parser.boundargs import (_sql_arg_params, attach_call_literals, attach_null_default_binds, +from parser.boundargs import (_notnull_default, _sql_arg_params, attach_call_literals, + attach_null_default_binds, attach_type_derived_args, extract_call_literals, extract_wrappers, merge_boundargs, merge_sql_arg_params, @@ -1845,5 +1846,42 @@ def test_every_signature_stating_every_argument_binds_nothing(self): self.assertNotIn("boundArgs", idl["functions"][0]["sqlSignatures"][0]) + +SET_VALUES_WRAPPER = r""" +Datum +Raster_set_values(PG_FUNCTION_ARGS) +{ + Raster *rast = (Raster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); + bool *noset = NULL; + int nrows = 0, ncols = 0; + if (! PG_ARGISNULL(5)) + { + int ncount; + bool *nnulls; + Datum *nelems = raster_block_extract(PG_GETARG_ARRAYTYPE_P(5), &nrows, + &ncols, &nnulls, &ncount); + } + Raster *result = raster_set_values(rast, noset, ncols, nrows); + PG_RETURN_POINTER(result); +} +""" + + +class DeclarationListTests(unittest.TestCase): + """#_notnull_default over the locals of one declaration, as #_guarded_default reads + them: `int nrows = 0, ncols = 0;` starts both from 0, each written only where + argument 5 is not NULL, as Raster_set_values reads the size of its flags.""" + + def test_each_local_of_the_declaration_starts_from_its_literal(self): + self.assertEqual(_notnull_default(SET_VALUES_WRAPPER, "ncols", 7), (5, "0")) + self.assertEqual(_notnull_default(SET_VALUES_WRAPPER, "nrows", 7), (5, "0")) + + def test_a_local_starting_from_a_call_states_no_literal(self): + body = SET_VALUES_WRAPPER.replace("int nrows = 0, ncols = 0;", + "int nrows = f(a, b), ncols = 0;") + self.assertIsNone(_notnull_default(body, "nrows", 7)) + self.assertEqual(_notnull_default(body, "ncols", 7), (5, "0")) + + if __name__ == "__main__": unittest.main()