Skip to content

fix(external-function): manage signatures and grants - #90

Open
GClunies wants to merge 5 commits into
datacoves:mainfrom
GClunies:fix/external-function-signature
Open

GClunies wants to merge 5 commits into
datacoves:mainfrom
GClunies:fix/external-function-signature

Conversation

@GClunies

@GClunies GClunies commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

Closes #85.

Snowcap now manages external functions by their full signatures. It fetches and syncs overloaded functions. It uses FUNCTION for external function SQL and grants. It handles exclusions and zero-argument signatures.

This PR is stacked on PR #84 (branch fix/transfer-ownership-object-type). Its diff includes PR #84 commits until PR #84 merges.

…IP accepts

GRANT OWNERSHIP rejects integration subtype names (SECURITY INTEGRATION, STORAGE INTEGRATION, ...), MATERIALIZED VIEW and HYBRID TABLE. Snowflake names them INTEGRATION, VIEW and TABLE. A plan that transferred ownership of any of these failed at apply with a SQL compilation error, for example: GRANT OWNERSHIP ON SECURITY INTEGRATION X TO ROLE ACCOUNTADMIN COPY CURRENT GRANTS.

https://docs.snowflake.com/en/sql-reference/sql/grant-ownership
GRANT OWNERSHIP has no EXTERNAL FUNCTION object type, so a transfer away
from the default SYSADMIN owner failed with a syntax error. External
functions are granted as FUNCTION.
Five places tested for integration types with their own copy of
"INTEGRATION" in str(...): three in create_grant, one in the ownership
transfer, and one in the Grant owner default. If one copy changed, the
others could drift without a failing test. They now call
resource_type_is_integration in snowcap/enums.py, next to
resource_type_is_grant.

Behavior does not change.
@noel

noel commented Oct 13, 2026

Copy link
Copy Markdown
Contributor

Reviewed this PR (full external-function signature/grant handling). Found three real issues via code-review, verified each by running the actual code in a worktree:

  1. Most severe: ResourcePointer.__init__ (snowcap/resource.py, around the new _arg_types line) treats any string name containing ( as a function-style signature, regardless of resource type. This isn't scoped to external functions, it's a general regression. Reproduced:

    >>> rp = ResourcePointer(name='"MY(POOL)"', resource_type=ResourceType.WAREHOUSE)
    >>> rp._arg_types
    ['POOL']
    >>> str(rp.fqn)
    '"MY(POOL)"(POOL)'

    A quoted identifier that legitimately contains a literal ( (rare, but valid Snowflake) gets its FQN, equality, and hash silently corrupted. Worth gating the signature-parsing on resource_type being function-like before parsing ( out of the name.

  2. Confirmed: fetch_external_function (snowcap/data_provider.py) uses next() with no default to find the matching INFORMATION_SCHEMA.FUNCTIONS row. If DESC FUNCTION succeeds but nothing in INFORMATION_SCHEMA.FUNCTIONS matches the signature (race between the two queries, or a signature-format mismatch), this raises a raw uncaught StopIteration instead of returning None/a clear error like every other fetch_* in the file. Reproduced by feeding it an empty INFORMATION_SCHEMA result.

  3. Plausible, not yet reproduced against a live account: data["FUNCTION_OWNER"] is passed straight into _quote_snowflake_identifier with no None guard, unlike the codebase's existing _get_owner_identifier helper. If that column is ever NULL, this would raise TypeError instead of degrading.

Minor: ResourcePointer.__init__ re-parses the identifier with pyparsing a second time just to recover arg_types, duplicating the parse NamedResource.__init__ already did one line above, on every function/procedure/external-function grant target.

Ponytail/complexity pass: lean, nothing to cut, this diff even removes speculative code (the isinstance(on, ResourcePointer) branch in Grant.__init__), and necessary size for the problem it solves.

Separate heads-up, not a code issue: this PR and #84 both independently add resource_type_is_integration/_OWNERSHIP_OBJECT_TYPES/_ownership_object_type to lifecycle.py/enums.py. Whichever merges second will conflict with the other's version of those files.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ExternalFunction names leave out argument types, so Snowflake rejects GRANT OWNERSHIP on them

2 participants