{"slug": "case-study-duckdb-table-function-api-migration", "title": "Case Study: DuckDB Table Function API Migration", "summary": "A Codex GPT-5.5 run with GitHits fixed a DuckDB v1.3.2 table-function migration 34% faster and using 19% fewer tokens than a run without GitHits, by leveraging version-specific source evidence to correctly implement filter_prune semantics. The without-GitHits run produced a syntax-correct patch but missed the optimizer path for filter-only column pruning, demonstrating that compile-correctness is insufficient for behavioral correctness.", "body_md": "[Back to blog](/blog/)\n\nJune 3, 2026 · 3 min read\n\n# Case Study: DuckDB Table Function API Migration\n\nA measured Codex run fixing a DuckDB v1.3.2 table-function migration with version-specific source evidence.\n\nThe fixture is a C++ DuckDB table-function migration.\n\nBoth runs used Codex GPT-5.5 against the same fixture. The prompt was:\n\n```\nFix web_archive_scan.cpp so it is source-correct for DuckDB v1.3.2 C++ table-function projection and complex-filter pushdown API.\n```\n\nThe stale fixture came from older DuckDB table-function examples. The patch had\nto update `web_archive_scan.cpp`\n\nfor DuckDB v1.3.2 while preserving projection\nand filter pushdown behavior.\n\nA patch could compile and still be wrong if it dropped columns needed only by pushed filters.\n\nCase study replay\n\n## DuckDB API migration\n\nmodel Codex GPT-5.5Fix web_archive_scan.cpp so it is source-correct for DuckDB v1.3.2 C++ table-function projection and complex-filter pushdown API.\n\nWithout GitHits\n\n- tokens\n- 0\n- time\n- 0s / 496s\n\n- Ready. Click \"Watch Replay\" to start.\n- Produced a syntax-checking diff but missed the filter_prune path that enables filter-only column pruning.\n\nWith GitHits\n\n- tokens\n- 0\n- time\n- 0s / 327s\n\n- Ready. Click \"Watch Replay\" to start.\n- Caught the v1.3.2 callback signature, column_t projection mapping, TableFunctionSet include, and filter_prune semantics.\n\n## Result\n\n| Run | Time | Tokens | Tools |\n|---|---|---|---|\n| With GitHits | 327s | 1.41M | 40 |\n| Without GitHits | 496s | 1.73M | 48 |\n\nThe GitHits run was about 34% faster and used about 19% fewer processed tokens.\nIt also found the `filter_prune`\n\npath used for filter-only column pruning.\n\nThe no-GitHits run updated API shapes and passed syntax checks. It missed the path that tells DuckDB how to retain columns required only by pushed filters.\n\n## Failure Surface\n\nThe broken fixture mixed several kinds of API drift:\n\n- The\n`pushdown_complex_filter`\n\ncallback signature had changed. - Projection handling needed to account for DuckDB’s\n`column_t`\n\nand`projection_ids`\n\nbehavior. `TableFunctionSet`\n\nneeded the right include path.- Complex filter pushdown had to cooperate with column pruning instead of only erasing filters from a local vector.\n\nThe no-GitHits run fetched headers, made a sparse checkout, inspected optimizer files, and ran syntax checks. Most of that work was source acquisition and orientation: branch, header, optimizer file, usage site, and version.\n\nThe GitHits run moved from the fixture into version-specific DuckDB source evidence:\n\n`table_function.hpp`\n\nfor the v1.3.2 callback and init inputs.`projection_ids`\n\nusage to understand projection mapping.`remove_unused_columns.cpp`\n\nto confirm how filter-only columns are preserved.`logical_get.cpp`\n\nand`pushdown_get.cpp`\n\nto connect the table function API to optimizer behavior.`function_set.hpp`\n\nto settle the`TableFunctionSet`\n\ninclude.\n\nThat evidence covered both the API shape and the planner behavior.\n\n## Missed Behavior\n\nThe fixture contained this warning in the stale code:\n\n```\n// Older examples used column_ids directly. This is wrong when DuckDB has\n// produced projection_ids for a filtered/projection-pushed scan.\n```\n\nThat comment points at the right area, but it is incomplete. `projection_ids`\n\nexplains visible output columns. It does not fully explain columns needed only to evaluate filters that have been pushed into the scan.\n\nThe no-GitHits result did enough source work to avoid compilation errors. It stopped before the filter-pruning behavior.\n\nIn this fixture, compile-correct was not enough. The behavior depended on the optimizer path, not only on the table-function header.\n\n## Evidence Path\n\nThe required context was DuckDB v1.3.2 source in the files that implement table functions and optimizer pruning:\n\n- The new\n`pushdown_complex_filter`\n\nshape. - The projection mapping between requested output columns and table function state.\n- The role of\n`filter_prune`\n\nwhen filters reference columns that are not otherwise projected. - The include boundary for\n`TableFunctionSet`\n\n.\n\nThe no-GitHits run inspected real source too, but spent more of the run getting and locating it. The GitHits run reached the relevant files earlier and followed the planner path far enough to catch the column-pruning edge case.", "url": "https://wpnews.pro/news/case-study-duckdb-table-function-api-migration", "canonical_source": "https://githits.com/blog/duckdb-api-migration-case-study/", "published_at": "2026-06-03 00:00:00+00:00", "updated_at": "2026-06-16 07:55:03.519304+00:00", "lang": "en", "topics": ["developer-tools", "ai-tools", "large-language-models", "ai-research"], "entities": ["DuckDB", "Codex", "GPT-5.5", "GitHits", "TableFunctionSet", "pushdown_complex_filter", "projection_ids", "filter_prune"], "alternates": {"html": "https://wpnews.pro/news/case-study-duckdb-table-function-api-migration", "markdown": "https://wpnews.pro/news/case-study-duckdb-table-function-api-migration.md", "text": "https://wpnews.pro/news/case-study-duckdb-table-function-api-migration.txt", "jsonld": "https://wpnews.pro/news/case-study-duckdb-table-function-api-migration.jsonld"}}