Skip to content

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

Merged
TheodoreSpeaks merged 10 commits into
stagingfrom
improvement/table-rename-column
Jun 9, 2026
Merged

feat(tables): stable column ids for metadata-only rename#4898
TheodoreSpeaks merged 10 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

vercel Bot commented Jun 6, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Jun 9, 2026 6:08am

Request Review

@cursor

cursor Bot commented Jun 6, 2026

Copy link
Copy Markdown

PR Summary

High Risk
This changes the core table data model and touches v1 APIs, imports, workflow execution, and a large grid surface area; incorrect id/name translation could corrupt reads/writes or break external integrations.

Overview
Introduces stable column ids as the storage key for row JSONB, table metadata (widths, order, pins), workflow-group deps/mappings, and find/filter/sort—while display names stay what users see and what CSV/v1/copilot callers use.

A new column-keys layer (getColumnId, rowDataNameToId / rowDataIdToName, withGeneratedColumnIds, etc.) centralizes translation. Renames are metadata-only (schema label + optimistic cache); hooks no longer rewrite row-data keys on rename. Import/export read/write id-keyed rows but expose name-keyed CSV/JSON; auto-created import columns get pre-assigned ids. normalizeColumn now keeps id so API clients don’t drop it.

The workspace grid is id-native end-to-end (column.key for cells, drag/pin/order/undo, workflow sidebars store deps by id). Public v1 rows APIs and the copilot table tool translate at the boundary. Workflow column execution builds name-keyed workflow input from id-keyed row data. Contracts add optional column id and document find matches by column id.

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

Comment thread apps/sim/lib/table/service.ts
@greptile-apps

greptile-apps Bot commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR replaces name-keyed row storage with stable col_<uuid> column ids across the table subsystem, making column rename a metadata-only operation and fire-and-forgetting row-data cleanup on column delete.

  • Storage layer: column-keys.ts introduces getColumnId / generateColumnId / columnMatchesRef as the single chokepoint; every column-creation path (addTableColumn, addWorkflowGroup, addWorkflowGroupOutput, createTable) now stamps a stable id unconditionally.
  • Boundary translation: public v1 API, mothership tool, and CSV import/export translate column name ↔ id at the edge via buildIdByName / buildNameById / rowDataNameToId / rowDataIdToName; internal grid paths are id-native.
  • Migration 0229_backfill_column_ids: idempotent single-UPDATE stamps id = name on legacy columns (rows were already keyed by name, so no row rewrites needed).

Confidence Score: 5/5

Safe to merge — the stable-id system is consistent across all column-creation, storage, and translation paths, and the backfill migration is idempotent.

All column-creation paths unconditionally assign stable ids; boundary translation (name↔id) is applied consistently at the v1 API, mothership tool, and CSV edges; the migration correctly grandfathers legacy rows by setting id=name; assertValidSchema guards every schema write. The two deleteColumn/deleteColumns spread-pattern notes are a pre-existing condition that assertValidSchema already prevents from corrupting the DB — they improve error UX but are not correctness blockers.

apps/sim/lib/table/service.ts — the two deleteColumn/deleteColumns spread-pattern suggestions are worth applying to avoid opaque error messages when the last workflow-group output column is deleted.

Important Files Changed

Filename Overview
apps/sim/lib/table/column-keys.ts New module — the single chokepoint for id/name translation. All helpers (getColumnId, generateColumnId, columnMatchesRef, buildIdByName, buildNameById, row/filter/sort translators) are well-implemented and correctly documented.
apps/sim/lib/table/service.ts All column-creation paths (addTableColumn, addWorkflowGroup, addWorkflowGroupOutput, createTable) unconditionally assign stable ids; renameColumn is now metadata-only; deleteColumn/deleteColumns fire-and-forget row-data cleanup; upsert error message now surfaces display name instead of internal id.
packages/db/migrations/0229_backfill_column_ids.sql Idempotent single-UPDATE sets id=name for legacy columns; correctly skips columns that already carry an id; does not touch metadata (safe since id=name for legacy columns keeps existing metadata keys valid).
apps/sim/app/api/v1/tables/[tableId]/rows/upsert/route.ts Correctly translates incoming name-keyed row data to id-keyed before calling upsertRow; conflictTarget is left as-is (service resolves it by id-or-name); response data translated back to names.
apps/sim/lib/table/workflow-columns.ts validateSchema updated to key columnsById by getColumnId; assertValidSchema correctly catches group-references-missing-column invariant violations before any schema write.
apps/sim/lib/table/validation.ts All validation helpers (coerceRowValues, checkUniqueConstraintsDb, checkBatchUniqueConstraintsDb) updated to use getColumnId for JSONB key lookups; NAME_PATTERN guard on raw-SQL interpolation paths is preserved.
apps/sim/lib/table/tests/column-keys.test.ts New test file covering getColumnId fallback, generateColumnId format, columnMatchesRef (id-exact + name-ci), buildIdByName/buildNameById, row/filter/sort translators, and withGeneratedColumnIds; good coverage of the new chokepoint.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["v1 API / Mothership / CSV\n(column names on the wire)"] -->|rowDataNameToId\nfilterNamesToIds\nsortNamesToIds| B["Service Layer\n(row data keyed by col_id)"]
    B -->|rowDataIdToName\nbuildNameById| A
    B --> C["user_table_rows.data\nJSONB keyed by col_id"]
    B --> D["user_table_definitions.schema\ncolumns[].id = col_uuid"]
    B --> E["user_table_definitions.metadata\ncolumnOrder / widths / pinned\n(all keyed by col_id)"]
    F["Internal Grid / Hooks\n(id-native via getColumnId)"] -->|direct id keys| B
    G["Migration 0229\nid = name for legacy cols\nidempotent"] --> D
    subgraph col_keys["column-keys.ts chokepoint"]
        H["getColumnId(col)\ncol.id ?? col.name"]
        I["generateColumnId()\ncol_UUID"]
        J["columnMatchesRef(col, ref)\nid-exact OR name-ci"]
    end
    B --> col_keys
Loading

Reviews (10): Last reviewed commit: "fix(tables): translate id→name in CSV/JS..." | 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

Comment thread apps/sim/hooks/queries/tables.ts
@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator Author

@greptile review

Comment thread apps/sim/hooks/queries/tables.ts
…rename-column

# Conflicts:
#	packages/db/migrations/meta/0227_snapshot.json
#	packages/db/migrations/meta/_journal.json
@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator Author

@greptile review

@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator Author

@greptile review

…rename-column

# Conflicts:
#	packages/db/migrations/meta/0228_snapshot.json
#	packages/db/migrations/meta/_journal.json
@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator Author

@greptile review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

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 98c883c. Configure here.

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

@TheodoreSpeaks TheodoreSpeaks merged commit f7811f8 into staging Jun 9, 2026
14 checks passed
@TheodoreSpeaks TheodoreSpeaks deleted the improvement/table-rename-column branch June 9, 2026 06:33
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