Skip to content

feat(tables): stable column ids for metadata-only rename#4898

Open
TheodoreSpeaks wants to merge 4 commits into
stagingfrom
improvement/table-rename-column
Open

feat(tables): stable column ids for metadata-only rename#4898
TheodoreSpeaks wants to merge 4 commits into
stagingfrom
improvement/table-rename-column

Conversation

@TheodoreSpeaks
Copy link
Copy Markdown
Collaborator

Summary

  • Every table column gets a stable id; row data, metadata (widths/order/pins), workflow-group refs, and filter/sort key by id instead of by name
  • Column rename is metadata-only (no per-row JSONB rewrite); column delete is fire-and-forget (schema commits immediately, row storage reclaimed in the background)
  • First-party grid/API/hooks go id-native via a single getColumnId chokepoint; public v1 API + mothership tool + CSV translate name↔id at the boundary
  • Not tied to a feature flag — ships on deploy. Migration 0227_backfill_column_ids stamps id = name onto existing columns (metadata-only, idempotent); the legacy row-rewrite rename / sync-strip delete paths are removed

Type of Change

  • New feature (non-breaking)

Testing

  • Tested manually: rename keeps cell values with no row rewrite
  • Verified the backfill migration against a local DB (idempotent: UPDATE 101, re-run UPDATE 0)
  • bun run lint:check and bun run check:api-validation:strict pass
  • 283 table / contract / hook unit tests pass (incl. new column-keys tests)

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

@vercel
Copy link
Copy Markdown

vercel Bot commented Jun 6, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Jun 6, 2026 8:50pm

Request Review

@cursor
Copy link
Copy Markdown

cursor Bot commented Jun 6, 2026

PR Summary

High Risk
Broad change to row storage keys, all table read/write paths, imports, workflow execution, and undo—incorrect translation could drop or mis-map cell values for API clients and legacy columns.

Overview
Introduces stable column ids as the storage key for row JSONB, table metadata (widths, order, pins), workflow-group refs, and internal filter/sort. Display name is metadata only — renames no longer rewrite every row’s JSONB keys.

Adds lib/table/column-keys (getColumnId, generateColumnId, name↔id maps, filter/sort translation, withGeneratedColumnIds) and wires it through CSV/import (pre-assign ids on createColumns, coerce rows by id), create/import paths (coerce against post-createTable schema), and normalizeColumn (preserve id).

Public v1 row APIs and the copilot table tool accept and return name-keyed row data while persisting id-keyed data; filters/sorts are translated at the boundary.

The workspace grid switches cells, selection, drag/pin/order, undo, and workflow sidebars to column.key / getColumnId; rename no longer rewrites metadata keys. Optimistic rename in hooks stops remapping row cache keys. Workflow column execution builds name-keyed workflow input from id-keyed row data.

Contracts gain optional column id, optional create-column id for undo restore, and tests document metadata-only rename behavior.

Reviewed by Cursor Bugbot for commit 4dbcbcf. Bugbot is set up for automated code reviews on this repo. Configure here.

Comment thread apps/sim/lib/table/service.ts
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Jun 6, 2026

Greptile Summary

This PR migrates every table column to a stable opaque id (col_<uuid>) so that rename becomes a pure metadata write — no per-row JSONB rewrite — and column delete can reclaim row storage in the background. Row data, metadata (widths/order/pins), workflow-group refs, and filter/sort all key on the column's stable id via a single getColumnId chokepoint; the public v1 API, mothership tool, and CSV translate name↔id at the boundary.

  • New column-keys.ts module: getColumnId (legacy fallback id ?? name), generateColumnId (col_<uuid>), and helpers to translate row-data/filter/sort between name-keyed wire format and id-keyed storage.
  • renameColumn made metadata-only: removes the per-row UPDATE … data - oldName || jsonb_build_object(newName, data->oldName) and the group-ref cascade; both are now no-ops because the rename never touches the stable id.
  • deleteColumn/deleteColumns split into fast schema commit + fire-and-forget row-data strip (stripColumnDataInBackground), allowing the lock to be released immediately.
  • Migration 0227_backfill_column_ids stamps id = name on every existing column (idempotent); legacy row data keyed by name stays reachable because getColumnId falls back to name.

Confidence Score: 5/5

Safe to merge — ships atomically with an idempotent migration and a getColumnId fallback that keeps existing name-keyed row data reachable throughout the transition.

Every column-creation and column-mutation path now generates stable ids unconditionally. The legacy id=name invariant means existing name-keyed row data, metadata keys, and workflow-group refs are all transparently reachable via getColumnId. Name-to-id translation is consistently applied at every external boundary. No id-generation paths remain behind a feature flag.

No files require special attention — the broad surface area is covered by 283 existing tests plus the new column-keys.test.ts suite.

Important Files Changed

Filename Overview
apps/sim/lib/table/column-keys.ts New module: all id to name translation helpers. Well-documented, fully unit-tested, and correct.
apps/sim/lib/table/service.ts renameColumn is now metadata-only, deleteColumn/deleteColumns commit schema immediately and strip row data in the background, all column-resolution lookups use columnMatchesRef/getColumnId. No id-generation paths remain flag-gated.
apps/sim/lib/table/validation.ts All row-data lookups now use getColumnId(column) as the key; error messages continue to use column.name for readability.
apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-grid/table-grid.tsx UI fully migrated to use column.key for all data access, ordering, pinning, drag-and-drop, copy/paste, and undo pushes.
apps/sim/hooks/queries/tables.ts useUpdateColumn optimistic update now matches columns by stable id with name fallback; removed unnecessary optimistic row-data key-rename on rename.
packages/db/migrations/0227_backfill_column_ids.sql Idempotent UPDATE stamps id = name on every column missing an id; correctly acknowledges the advisory-lock race window.
apps/sim/lib/table/types.ts Added optional id field to ColumnDefinition; updated JSDoc for all fields that now hold column ids.
apps/sim/app/api/v1/tables/[tableId]/rows/route.ts All v1 row endpoints translate name-keyed wire data to id-keyed storage on the way in and translate back on the way out.
apps/sim/lib/copilot/tools/server/table/user-table.ts Mothership tool now translates name and id at every row-read/write boundary.
apps/sim/background/workflow-column-execution.ts Input row construction translates id-keyed stored data to name-keyed for workflow execution; inputMappings correctly use column ids to look up row.data.
apps/sim/lib/table/tests/column-keys.test.ts 162-line test suite covering all helpers with legacy and id-native paths.
apps/sim/hooks/use-table-undo.ts All three undo action types carry columnId and resolve colKey throughout.

Reviews (4): Last reviewed commit: "refactor(tables): mint column ids via ge..." | Re-trigger Greptile

Comment thread apps/sim/lib/table/service.ts
Comment thread apps/sim/lib/table/service.ts
@TheodoreSpeaks
Copy link
Copy Markdown
Collaborator Author

@greptile review

@TheodoreSpeaks
Copy link
Copy Markdown
Collaborator Author

@greptile review

@TheodoreSpeaks
Copy link
Copy Markdown
Collaborator Author

@greptile review

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 4dbcbcf. Configure here.

for (const [key, data] of context.rowSnapshots) {
queryClient.setQueryData(key, data)
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Delete column optimistic cache mismatch

Medium Severity

useDeleteColumn still treats the mutation argument as a display name when updating the schema cache, but the grid now calls delete with the stable column id. Optimistic updates drop row keys and widths correctly yet leave the column in cached schema until onSettled refetch, so deleted columns can briefly remain visible.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 4dbcbcf. Configure here.

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.

1 participant