Skip to content

config update: re-split script[] elements that contain multiple statements (gap beyond #245) #274

Description

@beckavac

Context

#245 (closed in v0.28.0) added auto-normalization script: str → array for SQL transformations via normalize_blocks_codes_script calling split_statements(). That closed the string-vs-array runtime crash.

There is a remaining gap on the same code path: when the input script is already a list but a single element contains multiple ;-separated statements, the normalizer short-circuits and never re-splits per-element.

Failure mode

Runtime crashes at job execution with:

Error "odbc_prepare(): SQL error: Actual statement count 2 did not match the desired statement count 1.,
SQL state 0A000 in SQLPrepare" while executing query "CREATE OR REPLACE TABLE ... ; alter session unset week_of_year_policy, week_start;"

ODBC SQLPrepare expects exactly 1 statement per script[i] element. Two statements glued by ; in one element pass the array-shape validator (it's still a list of strings), so the write lands as 200 OK and the version increments — crash happens only at job run time.

Root cause

src/keboola_agent_cli/sync/code_extraction.py line 173 in normalize_blocks_codes_script:

script = code.get("script")
if not isinstance(script, str):
    continue  # ← already a list → passed through unchanged, even if elements are malformed

The split_statements() state machine that #245 already wired up handles this case correctly — it just isn't invoked on per-element list contents.

Repro

Take any Snowflake transformation, put two ;-separated statements into one script[i] element via Storage API PUT (or have a third-party tool / older UI / hand-edited config produce that shape — both happen in practice). Storage API accepts it silently and the version increments. Run the job → the runtime error above.

A natural seed for this shape: legacy code where a CREATE OR REPLACE TABLE …; ALTER SESSION SET week_of_year_policy=1; pair was authored as one block, then ingested by something that wraps the whole block as a single array element.

Proposal

In the loop over codes, after the existing string-case branch, also pass each list element through split_statements() (SQL transformations only — the existing is_sql gate). If the element produces more than 1 statement, replace it in place with the multi-element split. Surface via the existing normalizations envelope with a new action label, e.g. sql_resplit, plus before_length=1 / after_length=N so the silent fix stays observable.

This keeps the helper's contract — "every list element is exactly one statement after kbagent writes it" — and reuses the same split_statements() logic that already respects '…' / \"…\" / \$\$…\$\$ / -- / # / // / /* … */.

Where this was hit

SK→CZ migration on 2026-05-11 of project 1507 transformation 745661351 (Data reports). Three elements contained CREATE OR REPLACE TABLE … ; alter session set/unset week_of_year_policy, week_start; glued in one element each:

  • C0 "CZ Presidents" el[23] (out.proxy_zombici)
  • C1 "SK Presidents" el[26] (tmp.sk_visits_cpc_ag_prep)
  • C1 "SK Presidents" el[29] (out.sk_proxy_start)

These survived v513 → v515 because the array was already a list and only specific elements got rewritten — the offending elements were not touched by the migration text replacements but were re-pushed verbatim. Manual fix: split each element in two and push via kbagent config update --configuration-file ….

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions