Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion RULES.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ skipped unless `--execute` is passed.
| `VGI313` | warning | argument-description-states-type | An argument description should not restate the data type (it's a separate field). | |
| `VGI314` | warning | function-restates-argument-docs | A function's description shouldn't re-document its arguments (they'd drift). | |
| `VGI315` | warning | argument-type-consistent | An argument name should map to one SQL type across all functions (no type drift). | |
| `VGI316` | warning | array-argument-could-be-table | A function with a single multi-dimensional-array argument should take a table input. | |
| `VGI316` | warning | array-argument-could-be-table | A table function with a single multi-dimensional-array argument should take a table input. | |
| `VGI317` | error | constrained-argument-not-discoverable | An argument whose description enumerates allowed values or a numeric range should declare machine-readable constraints (choices / ge / le) so agents discover valid inputs. Needs a vgi extension exposing vgi_function_arguments(); silent on older ones. | |
| `VGI318` | error | default-violates-constraint | An argument's declared default must satisfy its own constraints — be a member of choices, inside the numeric range, and match the pattern. Needs a vgi extension exposing vgi_function_arguments(); silent on older ones. | |
| `VGI319` | warning | invalid-constraint | A declared constraint must be well-formed: the pattern must be a valid regex and the numeric range must be non-empty. Needs a vgi extension exposing vgi_function_arguments(). | |
Expand Down
16 changes: 9 additions & 7 deletions src/vgi_lint_check/rules/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -798,16 +798,18 @@ class ArrayArgumentCouldBeTable(Rule):
name = "array-argument-could-be-table"
category = FUNC
default_severity = Severity.WARNING
targets = (
ObjectKind.SCALAR_FUNCTION,
ObjectKind.AGGREGATE,
ObjectKind.MACRO,
ObjectKind.TABLE_FUNCTION,
# Table functions only. The remedy ("expose a table/relation input so callers
# pass a subquery FROM …") is only structurally possible for a table function:
# a scalar or aggregate function returns one value per row and cannot take a
# relation as an argument, and DuckDB macros take scalar expressions, not
# relations — flagging any of them is a false positive with no achievable fix.
targets = (ObjectKind.TABLE_FUNCTION,)
summary = (
"A table function with a single multi-dimensional-array argument should take a table input."
)
summary = "A function with a single multi-dimensional-array argument should take a table input."

def check(self, ctx: RuleContext) -> Iterator[Finding]:
for f in ctx.catalog.iter_all_functions():
for f in _iter_table_functions(ctx):
array_args = [
a
for a in f.arguments
Expand Down
15 changes: 12 additions & 3 deletions tests/unit/test_strict_rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -711,12 +711,12 @@ def test_vgi152_agent_test_tasks_present_nudge():


def test_vgi316_single_array_arg_suggests_table():
def codes_for(*args):
fn = F.func("main", "solve", description="d", arguments=list(args))
def codes_for(*args, ftype="table"):
fn = F.func("main", "solve", ftype=ftype, description="d", arguments=list(args))
s = F.schema("main", comment="c", tags=_TAGS, functions=[fn])
return {f.code for f in run(select_rules(Config()), RuleContext(F.catalog(s), Config()))}

# a single 2-D array argument (a matrix / list of rows) -> fires
# a TABLE FUNCTION with a single 2-D array argument (a matrix / list of rows) -> fires
assert "VGI316" in codes_for(F.arg("distance", "BIGINT[][]", "the distance matrix"))
# a list-of-struct (typed rows) -> fires
assert "VGI316" in codes_for(F.arg("rows", "STRUCT(a INTEGER, b INTEGER)[]", "rows"))
Expand All @@ -726,6 +726,15 @@ def codes_for(*args):
)
# a plain 1-D array (a vector) -> not flagged
assert "VGI316" not in codes_for(F.arg("v", "BIGINT[]", "a vector of weights"))
# a SCALAR function with the same matrix arg -> NOT flagged: a scalar returns one
# value per row and has no table-function form, so the remedy is impossible.
assert "VGI316" not in codes_for(
F.arg("distance", "BIGINT[][]", "the distance matrix"), ftype="scalar"
)
# likewise an AGGREGATE with a matrix arg -> not flagged.
assert "VGI316" not in codes_for(
F.arg("distance", "BIGINT[][]", "the distance matrix"), ftype="aggregate"
)


def test_vgi145_view_wraps_table_function():
Expand Down