Skip to content

mcp: ALTER TABLE RENAME drops _table_catalog prose metadata (source_url, purpose, notes, …) #59

@StefanSteiner

Description

@StefanSteiner

Summary

ALTER TABLE ... RENAME TO ... (via the execute tool) re-stubs the
target table's _table_catalog row and drops all prose metadata
(source_url, source_description, purpose, notes, license).
It also resets load_tool, load_params, created_by, and
last_modified_by to NULL/unknown, and overwrites loaded_at /
last_refreshed_at with the rename time (so the row looks like a
fresh load that didn't actually happen).

The rename succeeds and the data is fine — only the catalog
provenance is lost.

Steps to reproduce

// 1. Load and annotate
load_file({
  table: "population",
  path: "/tmp/owid/population.csv",
  database: "persistent"
})
set_table_metadata({
  table: "population",
  database: "persistent",
  source_url: "https://ourworldindata.org/grapher/population",
  purpose: "Population denominators for per-person analyses.",
  notes: "Refresh: curl ..."
})

// Catalog row now has source_url, purpose, notes populated.

// 2. Rename
execute({
  database: "persistent",
  sql: ["ALTER TABLE population RENAME TO owid_population"]
})

// 3. Inspect catalog
query({
  database: "persistent",
  sql: "SELECT table_name, source_url, purpose, notes, load_tool,
               loaded_at, created_by
        FROM _table_catalog WHERE table_name = 'owid_population'"
})

Expected

The row should follow the rename — the same provenance, with only
table_name updated. loaded_at and last_refreshed_at should
stay anchored to the original load time.

Actual

table_name      | owid_population
source_url      | NULL              ← lost
purpose         | NULL              ← lost
notes           | NULL              ← lost
load_tool       | unknown           ← was "load_file"
loaded_at       | 2026-05-27 18:40  ← reset to rename time
                                       (was 2026-05-27 18:21,
                                        the original load time)
created_by      | NULL              ← was "claude-code 2.1.152"

The old row (table_name = 'population') is gone — so this is a
delete-and-restub on rename, not an update.

Why it matters

The catalog is the user-visible record of "where did this data come
from and why is it here." Losing it on rename means:

  • Any prose set via set_table_metadata is silent collateral.
  • load_params (which records the original source path / mode /
    schema overrides) is gone, so notes-based "refresh by re-running
    this command" instructions become the only recovery path — and
    those are exactly what was just deleted.
  • Renames are a normal organizational operation (e.g. adding a
    source-prefix like owid_ to group related tables); users
    shouldn't have to choose between tidy names and provenance.

Suggested fix

When ALTER TABLE ... RENAME TO ... is observed (either via
execute SQL inspection or post-hoc reconciliation against
SHOW TABLES), update _table_catalog.table_name in place rather
than dropping the row and lazily re-stubbing on next access.

If in-place update isn't feasible (e.g. catalog reconciliation runs
on a different timer), at minimum carry forward the prose fields
from the old row when the new stub is written.

OR

Add a dedicated rename_table tool would let the server handle the catalog update atomically: update table_name in place, preserve all prose fields, and keep
loaded_at/created_by anchored. The execute path can't do that cleanly because the server only sees raw SQL, not the intent.

Worth adding to the issue as a "Suggested fix / new tool" option alongside the in-place-update approach.

Workaround

After every rename, re-run set_table_metadata with the same
fields. Tedious but works. Verified on hyper-rust-api 0.2.3.re93d08d2.

Environment

  • hyper-rust-api version: 0.2.3.re93d08d2
  • macOS 25.4.0 (Darwin)
  • Persistent database (~/Library/Application Support/hyperdb/workspace.hyper)

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions