Skip to content

fix(driver-sql): materialize declared object-level indexes (#1459)#1461

Merged
os-zhuang merged 2 commits into
mainfrom
claude/happy-feynman-6uU7G
Jun 1, 2026
Merged

fix(driver-sql): materialize declared object-level indexes (#1459)#1461
os-zhuang merged 2 commits into
mainfrom
claude/happy-feynman-6uU7G

Conversation

@os-zhuang
Copy link
Copy Markdown
Contributor

Summary

Fixes #1459. The SQL driver synced columns and field-level unique, but silently dropped object-level declared indexes (ObjectSchema.indexes: [{ fields, unique }]). As a result several documented multi-column UNIQUE / dedup guarantees were never enforced at the DB level — a fresh dev --fresh sqlite DB showed only PK autoindexes.

Affected guarantees that depended on an unenforced index:

  • sys_notification_delivery UNIQUE(notification_id, recipient_id, channel) — outbox enqueue() convergence-on-conflict
  • sys_notification_receipt UNIQUE(notification_id, user_id, channel) — bell mark-read / inbox delivered-receipt upserts
  • sys_notification.dedup_key UNIQUE (feat(messaging): race-safe dedup + opt-in retention (ADR-0030 hardening) #1458) — emit() dedup race-safety
  • sys_api_key.key, sys_organization.slug, and other platform objects' declared unique indexes

Changes

Wired declared-index sync into initObjects (syncDeclaredIndexes):

  • Materializes single- and multi-column indexes after the table is created/altered (so every referenced column physically exists), including UNIQUE.
  • NULL-distinct semantics are the cross-dialect default (SQLite/Postgres/MySQL), so multiple NULL rows stay insertable while non-NULL duplicates are rejected — matching the convergence-on-conflict pattern the messaging pipeline relies on.
  • Idempotent: deterministic, length-bounded index names (hash-suffixed past the 60-char mark for PG/MySQL limits) + per-dialect existing-index introspection (PRAGMA index_list / pg_indexes / information_schema.STATISTICS). An "already exists" race is absorbed.
  • Indexes referencing a non-materialized (virtual formula) column are skipped with a warning instead of failing sync.

Flipped the indexes capability flag false → true.

Tests

Added 7 tests to sql-driver-schema.test.ts, all asserting via PRAGMA index_list on a fresh in-memory sqlite DB:

  • single-column unique index materialized + rejects duplicates
  • multi-column (compound) unique index — same tuple rejected, differing tuple allowed
  • multiple NULLs allowed under a unique index (NULL-distinct)
  • non-unique index materialized
  • idempotent across repeated initObjects runs
  • declared index over a virtual column is skipped with a warning (no throw)
  • supports.indexes === true

Full driver-sql suite: 132 passed. Package builds clean (ESM/CJS/DTS).

Notes

Postgres/MySQL paths use the same knex alterTable + introspection helpers but were not exercised in CI here (no live PG/MySQL in this environment). The implementation is dialect-portable; a Postgres assertion can be added if a fixture is available.

https://claude.ai/code/session_01AgJ1guiaybKRbMfqAPCDeN


Generated by Claude Code

The SQL driver synced columns and field-level `unique`, but silently
dropped object-level declared `indexes` ([{ fields, unique }]). As a
result documented multi-column UNIQUE guarantees (notification delivery
dedup, receipt upserts, dedup_key convergence, api_key/slug uniqueness)
were never enforced at the DB level — a fresh `dev --fresh` sqlite DB
showed only PK autoindexes.

Wire declared-index sync into `initObjects`:
- materialize single- and multi-column indexes after the table is
  created/altered, including UNIQUE
- NULL-distinct semantics are the cross-dialect default, so multiple
  NULL rows stay insertable while non-NULL duplicates are rejected
- idempotent: deterministic, length-bounded index names + per-dialect
  existing-index introspection (sqlite/pg/mysql); "already exists" races
  are absorbed
- indexes referencing a non-materialized (virtual/formula) column are
  skipped with a warning instead of failing sync

Flip the `indexes` capability flag to true and add tests asserting the
index is materialized (PRAGMA index_list), rejects duplicates, allows
multiple NULLs, covers compound + non-unique indexes, and is idempotent.

https://claude.ai/code/session_01AgJ1guiaybKRbMfqAPCDeN
@vercel
Copy link
Copy Markdown

vercel Bot commented Jun 1, 2026

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

Project Deployment Actions Updated (UTC)
spec Canceled Canceled Jun 1, 2026 9:04am

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation tooling labels Jun 1, 2026
@os-zhuang os-zhuang marked this pull request as ready for review June 1, 2026 09:25
@os-zhuang os-zhuang merged commit 24c9013 into main Jun 1, 2026
12 checks passed
xuyushun441-sys pushed a commit that referenced this pull request Jun 2, 2026
Merge pull request #1461 from objectstack-ai/feat/gallery-cover-polish

objectui@382df5d30075385f637c5925775790173ecc855e
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SQL driver does not materialize declared object-level indexes (UNIQUE guarantees unenforced)

2 participants