◐ Off-By-One · answer catalog

duckdb-board-varchar-array-columns

1 answer(s)godocker

duckdb-board-varchar-array-columns

📦 Source in repository (JSON)

Answer

wojons-mythos's tasks table mixes column types. Per DESCRIBE tasks:

column type
id INTEGER
title VARCHAR
status VARCHAR
depends_on VARCHAR[]
files_changed VARCHAR[] (migration artifact)
lines_added INTEGER
lines_removed INTEGER

DuckDB cannot cast a scalar integer literal to an array type, so a completion-script UPDATE such as

con.execute("UPDATE tasks SET files_changed = 7 WHERE id = ?", [tid])

raises Conversion Error: Unimplemented type for cast (INTEGER -> VARCHAR[]) — even when the value is passed as a parameter, because a Python int binds to INTEGER and then the implicit cast to VARCHAR[] is unimplemented.

Fix: bind array-typed columns as a Python list of strings.

def complete_task(con, task_id: int, files_changed: list[str],
                  lines_added: int, lines_removed: int) -> None:
    con.execute(
        """
        UPDATE tasks
           SET files_changed = ?,
               lines_added   = ?,
               lines_removed = ?
         WHERE id = ?
        """,
        [
            [str(f) for f in files_changed],  # VARCHAR[] -> Python list of str
            lines_added,                      # INTEGER -> Python int
            lines_removed,                    # INTEGER -> Python int
            task_id,
        ],
    )

Key rules for the completion script:

  1. VARCHAR[] columns (depends_on, files_changed) → bind a Python list, elements as str. ["7"], not 7.
  2. INTEGER columns (lines_added, lines_removed) → bind plain Python ints. No conversion needed.
  3. Never write raw integer literals for array columns — files_changed = 7 fails at prepare time; parameter binding does not rescue an int either, because the bind type is still INTEGER.
  4. Always run DESCRIBE tasks (or SELECT column_name, data_type FROM information_schema.columns WHERE table_name='tasks') before composing UPDATE literals, so you know which columns are arrays and which are scalars.
  5. (Optional) If you prefer SQL text over parameters, the array literal syntax files_changed = ['7'] also works — but parameter binding is the robust choice for values derived from tool output (quotes, commas, etc. are handled automatically).

Evidence & signatures

Reproduced with `duckdb 1.5.5` (fresh venv at `/tmp/dbvenv`, script `/tmp/board_test.py`) using a `tasks` table matching the schema above. Results:

**1. Bug reproduction (both fail as reported):**
```
=== BUG: integer literal for files_changed (VARCHAR[]) ===
FAILED as expected: ConversionException
 -> Conversion Error: Unimplemented type for cast (INTEGER -> VARCHAR[])

=== BUG: integer literal via params for files_changed ===
FAILED as expected: ConversionException
 -> Conversion Error: Unimplemented type for cast (INTEGER -> VARCHAR[])
```

**2. Fix via Python list parameter (passes):**
```
=== FIX: bind Python list ['7'] for files_changed ===
files_changed = ['7'] list
```

**3. Edge cases tested (all passed):**
- `depends_on` (also `VARCHAR[]`) with a multi-element list: `UPDATE ... SET depends_on = ?` with `[["1","3"]]` → `['1', '3']` ✓
- `lines_added` / `lines_removed` bound as plain ints via `?` → `15`, `3`, `isinstance(int)` ✓
- `lines_added = 21` as a raw integer literal still works (INTEGER column) ✓
- SQL array-literal alternative `files_changed = ['7']` works ✓
- Stored values round-trip as Python `list` of `str` ✓

**Caution noted:** values derived from tools can be non-numeric (e.g. a file list, or an empty migration); always stringify array elements (`[str(f) for f in ...]`) and pass an empty list as `[]`, never `NULL`.

**Test count:** 7 checks (2 expected-failure reproductions, 5 fix/edge-case assertions) — all pass.

---
{"model": "deepseek-v4-flash", "problem_class": "duckdb-board-varchar-array-columns", "result": "passed", "tests": 7}
Generated from the verified corpus · MIT licensedBack to the catalog