Skip to content

fix: redirect all user_id FKs from auth.users to public.users for Clerk auth support#712

Merged
ngoiyaeric merged 2 commits into
QueueLab:mainfrom
ngoiyaeric:fix/chat-db-normalization-v2
Jul 9, 2026
Merged

fix: redirect all user_id FKs from auth.users to public.users for Clerk auth support#712
ngoiyaeric merged 2 commits into
QueueLab:mainfrom
ngoiyaeric:fix/chat-db-normalization-v2

Conversation

@ngoiyaeric

@ngoiyaeric ngoiyaeric commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

Problem

Chat history save was failing for Clerk-authenticated users with the error:

insert or update on table "chats" violates foreign key constraint "chats_user_id_fkey"
Key (user_id)=(957edcef-6fbb-499b-bf14-2b5c9d3bdc9d) is not present in table "users".

Root Cause

All user_id foreign key constraints on the chat-related tables referenced auth.users.id, but Clerk-authenticated users only exist in public.users (not in auth.users). The auth.users table is managed by Supabase Auth, while Clerk users are synced to public.users via the sync_clerk_user() function and the on_auth_user_created trigger.

Affected constraints:

  • chats_user_id_fkey — was pointing to auth.users.id
  • messages_user_id_fkey — was pointing to auth.users.id
  • system_prompts_user_id_fkey — was pointing to auth.users.id
  • locations_user_id_fkey — was pointing to auth.users.id
  • visualizations_user_id_fkey — was pointing to auth.users.id
  • chat_participants_user_id_fkey — was pointing to auth.users.id
  • prompt_generation_jobs_user_id_fkey — was pointing to auth.users.id

Fix

All FK constraints have been redirected to reference public.users.id as the canonical identity table. This normalizes the schema so that public.users serves as the unified identity store for both Clerk and Supabase Auth users.

Additionally, performance indexes have been added on all user_id columns.

Database Change

The Supabase migration has already been applied directly to the production database via the Supabase MCP tool. This PR includes the migration file for schema tracking and drizzle-kit consistency.

Summary by CodeRabbit

  • Bug Fixes
    • Updated database relationships so user-owned data now consistently links to the app’s main user records.
    • Improved delete behavior so related chats, messages, prompts, locations, visualizations, participants, and job records are removed automatically when a user is deleted.
    • Added supporting database indexes to help keep user-related lookups responsive.

…rk auth support

Root cause: Clerk-authenticated users exist in public.users (with clerk_user_id
mapping) but NOT in auth.users. The FK constraints on chats, messages,
system_prompts, locations, visualizations, chat_participants, and
prompt_generation_jobs pointed to auth.users.id, causing foreign key violations
(23503) when inserting records for Clerk-authenticated users.

Fix: All user_id FK constraints now reference public.users.id as the canonical
identity table. This normalizes the schema so that public.users serves as the
unified identity store for both Clerk and Supabase Auth users.

Also adds performance indexes on all user_id columns.

Note: The Supabase migration has already been applied to the production database
via the Supabase MCP tool. This migration file is for schema tracking and
drizzle-kit consistency.
@qodo-code-review

Copy link
Copy Markdown
Contributor

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@vercel

vercel Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

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

Project Deployment Actions Updated (UTC)
qcx Ready Ready Preview, Comment Jul 9, 2026 8:29pm

@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.


ngoiyaeric seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account.
You have signed the CLA already but the status is still pending? Let us recheck it.

@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@ngoiyaeric, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 44 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3b3b61ab-f8b1-48e1-9ac9-0d95e0040f57

📥 Commits

Reviewing files that changed from the base of the PR and between 46527a5 and cdb8fdd.

📒 Files selected for processing (2)
  • drizzle/migrations/0005_fix_chat_db_fk_normalization.sql
  • drizzle/migrations/0006_add_user_id_indexes.sql
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@drizzle/migrations/0005_fix_chat_db_fk_normalization.sql`:
- Around line 67-73: The index creation statements in the migration need to be
changed to use CONCURRENTLY so they don’t block writes; update the CREATE INDEX
statements for the affected tables in the migration section that builds the
user_id indexes. If drizzle-kit executes migrations inside a transaction, move
these index statements out into a separate migration or ensure they run without
a transaction, since CREATE INDEX CONCURRENTLY cannot be executed inside one.
- Around line 38-64: Add NOT VALID to each foreign key added in the migration so
the new constraints on chats, messages, system_prompts, locations,
visualizations, chat_participants, and prompt_generation_jobs do not block
writes during creation, then add separate VALIDATE CONSTRAINT statements
afterward to enforce them safely; update the ALTER TABLE blocks in this
migration to follow the NOT VALID + VALIDATE pattern for each *_user_id_fkey
constraint.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 38fdb8b7-700c-46ba-b9bc-cff82a9dd901

📥 Commits

Reviewing files that changed from the base of the PR and between 7f7ca97 and 46527a5.

📒 Files selected for processing (1)
  • drizzle/migrations/0005_fix_chat_db_fk_normalization.sql
📜 Review details
🧰 Additional context used
🪛 SQLFluff (4.2.2)
drizzle/migrations/0005_fix_chat_db_fk_normalization.sql

[error] 38-40: ADD CONSTRAINT ... FOREIGN KEY should use NOT VALID to avoid locking the table while validating existing rows.

(PG01)


[error] 42-44: ADD CONSTRAINT ... FOREIGN KEY should use NOT VALID to avoid locking the table while validating existing rows.

(PG01)


[error] 46-48: ADD CONSTRAINT ... FOREIGN KEY should use NOT VALID to avoid locking the table while validating existing rows.

(PG01)


[error] 50-52: ADD CONSTRAINT ... FOREIGN KEY should use NOT VALID to avoid locking the table while validating existing rows.

(PG01)


[error] 54-56: ADD CONSTRAINT ... FOREIGN KEY should use NOT VALID to avoid locking the table while validating existing rows.

(PG01)


[error] 58-60: ADD CONSTRAINT ... FOREIGN KEY should use NOT VALID to avoid locking the table while validating existing rows.

(PG01)


[error] 62-64: ADD CONSTRAINT ... FOREIGN KEY should use NOT VALID to avoid locking the table while validating existing rows.

(PG01)


[error] 67-67: CREATE INDEX should use CONCURRENTLY to avoid locking the table during the build.

(PG01)


[error] 68-68: CREATE INDEX should use CONCURRENTLY to avoid locking the table during the build.

(PG01)


[error] 69-69: CREATE INDEX should use CONCURRENTLY to avoid locking the table during the build.

(PG01)


[error] 70-70: CREATE INDEX should use CONCURRENTLY to avoid locking the table during the build.

(PG01)


[error] 71-71: CREATE INDEX should use CONCURRENTLY to avoid locking the table during the build.

(PG01)


[error] 72-72: CREATE INDEX should use CONCURRENTLY to avoid locking the table during the build.

(PG01)


[error] 73-73: CREATE INDEX should use CONCURRENTLY to avoid locking the table during the build.

(PG01)

🪛 Squawk (2.59.0)
drizzle/migrations/0005_fix_chat_db_fk_normalization.sql

[warning] 39-40: By default new constraints require a table scan and block writes to the table while that scan occurs. Use NOT VALID with a later VALIDATE CONSTRAINT call.

(constraint-missing-not-valid)


[warning] 39-40: Adding a foreign key constraint requires a table scan and a SHARE ROW EXCLUSIVE lock on both tables, which blocks writes to each table. Add NOT VALID to the constraint in one transaction and then VALIDATE the constraint in a separate transaction.

(adding-foreign-key-constraint)


[warning] 43-44: By default new constraints require a table scan and block writes to the table while that scan occurs. Use NOT VALID with a later VALIDATE CONSTRAINT call.

(constraint-missing-not-valid)


[warning] 43-44: Adding a foreign key constraint requires a table scan and a SHARE ROW EXCLUSIVE lock on both tables, which blocks writes to each table. Add NOT VALID to the constraint in one transaction and then VALIDATE the constraint in a separate transaction.

(adding-foreign-key-constraint)


[warning] 47-48: By default new constraints require a table scan and block writes to the table while that scan occurs. Use NOT VALID with a later VALIDATE CONSTRAINT call.

(constraint-missing-not-valid)


[warning] 47-48: Adding a foreign key constraint requires a table scan and a SHARE ROW EXCLUSIVE lock on both tables, which blocks writes to each table. Add NOT VALID to the constraint in one transaction and then VALIDATE the constraint in a separate transaction.

(adding-foreign-key-constraint)


[warning] 51-52: By default new constraints require a table scan and block writes to the table while that scan occurs. Use NOT VALID with a later VALIDATE CONSTRAINT call.

(constraint-missing-not-valid)


[warning] 51-52: Adding a foreign key constraint requires a table scan and a SHARE ROW EXCLUSIVE lock on both tables, which blocks writes to each table. Add NOT VALID to the constraint in one transaction and then VALIDATE the constraint in a separate transaction.

(adding-foreign-key-constraint)


[warning] 55-56: By default new constraints require a table scan and block writes to the table while that scan occurs. Use NOT VALID with a later VALIDATE CONSTRAINT call.

(constraint-missing-not-valid)


[warning] 55-56: Adding a foreign key constraint requires a table scan and a SHARE ROW EXCLUSIVE lock on both tables, which blocks writes to each table. Add NOT VALID to the constraint in one transaction and then VALIDATE the constraint in a separate transaction.

(adding-foreign-key-constraint)


[warning] 59-60: By default new constraints require a table scan and block writes to the table while that scan occurs. Use NOT VALID with a later VALIDATE CONSTRAINT call.

(constraint-missing-not-valid)


[warning] 59-60: Adding a foreign key constraint requires a table scan and a SHARE ROW EXCLUSIVE lock on both tables, which blocks writes to each table. Add NOT VALID to the constraint in one transaction and then VALIDATE the constraint in a separate transaction.

(adding-foreign-key-constraint)


[warning] 63-64: By default new constraints require a table scan and block writes to the table while that scan occurs. Use NOT VALID with a later VALIDATE CONSTRAINT call.

(constraint-missing-not-valid)


[warning] 63-64: Adding a foreign key constraint requires a table scan and a SHARE ROW EXCLUSIVE lock on both tables, which blocks writes to each table. Add NOT VALID to the constraint in one transaction and then VALIDATE the constraint in a separate transaction.

(adding-foreign-key-constraint)


[warning] 67-67: During normal index creation, table updates are blocked, but reads are still allowed. Use concurrently to avoid blocking writes.

(require-concurrent-index-creation)


[warning] 68-68: During normal index creation, table updates are blocked, but reads are still allowed. Use concurrently to avoid blocking writes.

(require-concurrent-index-creation)


[warning] 69-69: During normal index creation, table updates are blocked, but reads are still allowed. Use concurrently to avoid blocking writes.

(require-concurrent-index-creation)


[warning] 70-70: During normal index creation, table updates are blocked, but reads are still allowed. Use concurrently to avoid blocking writes.

(require-concurrent-index-creation)


[warning] 71-71: During normal index creation, table updates are blocked, but reads are still allowed. Use concurrently to avoid blocking writes.

(require-concurrent-index-creation)


[warning] 72-72: During normal index creation, table updates are blocked, but reads are still allowed. Use concurrently to avoid blocking writes.

(require-concurrent-index-creation)


[warning] 73-73: During normal index creation, table updates are blocked, but reads are still allowed. Use concurrently to avoid blocking writes.

(require-concurrent-index-creation)

🔇 Additional comments (2)
drizzle/migrations/0005_fix_chat_db_fk_normalization.sql (2)

38-64: 🗄️ Data Integrity & Integration

Verify ON DELETE CASCADE is intentional for all tables.

All 7 FKs use ON DELETE CASCADE, meaning deleting a user from public.users will automatically delete all their chats, messages, system_prompts, locations, visualizations, chat_participants, and prompt_generation_jobs. If the original constraints (referencing auth.users) used a different delete behavior (e.g., ON DELETE SET NULL or ON DELETE RESTRICT), this migration silently changes the data integrity semantics. Confirm this cascade behavior is desired, especially for tables like messages where cascading from a parent chats deletion may already handle cleanup.


15-64: 🩺 Stability & Availability

Verify transaction wrapping behavior in drizzle-kit.

If drizzle-kit executes this migration inside a single transaction, the DROP CONSTRAINT + ADD CONSTRAINT sequence is atomic (good — if ADD fails, DROP is rolled back). However, if it does not wrap in a transaction, there's a window between DROP and ADD where no FK constraint exists, allowing orphaned rows. Additionally, CREATE INDEX CONCURRENTLY (suggested above) cannot run inside a transaction. Verify drizzle-kit's transaction behavior to determine the correct approach for both the FK and index statements.

Comment thread drizzle/migrations/0005_fix_chat_db_fk_normalization.sql Outdated
Comment on lines +67 to +73
CREATE INDEX IF NOT EXISTS idx_chats_user_id ON public.chats(user_id);
CREATE INDEX IF NOT EXISTS idx_messages_user_id ON public.messages(user_id);
CREATE INDEX IF NOT EXISTS idx_system_prompts_user_id ON public.system_prompts(user_id);
CREATE INDEX IF NOT EXISTS idx_locations_user_id ON public.locations(user_id);
CREATE INDEX IF NOT EXISTS idx_visualizations_user_id ON public.visualizations(user_id);
CREATE INDEX IF NOT EXISTS idx_chat_participants_user_id ON public.chat_participants(user_id);
CREATE INDEX IF NOT EXISTS idx_prompt_generation_jobs_user_id ON public.prompt_generation_jobs(user_id);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Use CONCURRENTLY for index creation to avoid blocking writes.

Standard CREATE INDEX blocks all writes to the table during the build. For production tables, use CREATE INDEX CONCURRENTLY which allows writes to proceed. However, CONCURRENTLY cannot run inside a transaction block — if drizzle-kit wraps migrations in a transaction, this will fail. In that case, these index statements need to be in a separate migration file or run outside a transaction.

⚡ Proposed fix — CONCURRENTLY (if migrations run outside a transaction)
 -- Step 3: Add indexes on user_id columns for query performance
-CREATE INDEX IF NOT EXISTS idx_chats_user_id ON public.chats(user_id);
-CREATE INDEX IF NOT EXISTS idx_messages_user_id ON public.messages(user_id);
-CREATE INDEX IF NOT EXISTS idx_system_prompts_user_id ON public.system_prompts(user_id);
-CREATE INDEX IF NOT EXISTS idx_locations_user_id ON public.locations(user_id);
-CREATE INDEX IF NOT EXISTS idx_visualizations_user_id ON public.visualizations(user_id);
-CREATE INDEX IF NOT EXISTS idx_chat_participants_user_id ON public.chat_participants(user_id);
-CREATE INDEX IF NOT EXISTS idx_prompt_generation_jobs_user_id ON public.prompt_generation_jobs(user_id);
+CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_chats_user_id ON public.chats(user_id);
+CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_messages_user_id ON public.messages(user_id);
+CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_system_prompts_user_id ON public.system_prompts(user_id);
+CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_locations_user_id ON public.locations(user_id);
+CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_visualizations_user_id ON public.visualizations(user_id);
+CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_chat_participants_user_id ON public.chat_participants(user_id);
+CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_prompt_generation_jobs_user_id ON public.prompt_generation_jobs(user_id);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
CREATE INDEX IF NOT EXISTS idx_chats_user_id ON public.chats(user_id);
CREATE INDEX IF NOT EXISTS idx_messages_user_id ON public.messages(user_id);
CREATE INDEX IF NOT EXISTS idx_system_prompts_user_id ON public.system_prompts(user_id);
CREATE INDEX IF NOT EXISTS idx_locations_user_id ON public.locations(user_id);
CREATE INDEX IF NOT EXISTS idx_visualizations_user_id ON public.visualizations(user_id);
CREATE INDEX IF NOT EXISTS idx_chat_participants_user_id ON public.chat_participants(user_id);
CREATE INDEX IF NOT EXISTS idx_prompt_generation_jobs_user_id ON public.prompt_generation_jobs(user_id);
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_chats_user_id ON public.chats(user_id);
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_messages_user_id ON public.messages(user_id);
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_system_prompts_user_id ON public.system_prompts(user_id);
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_locations_user_id ON public.locations(user_id);
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_visualizations_user_id ON public.visualizations(user_id);
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_chat_participants_user_id ON public.chat_participants(user_id);
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_prompt_generation_jobs_user_id ON public.prompt_generation_jobs(user_id);
🧰 Tools
🪛 SQLFluff (4.2.2)

[error] 67-67: CREATE INDEX should use CONCURRENTLY to avoid locking the table during the build.

(PG01)


[error] 68-68: CREATE INDEX should use CONCURRENTLY to avoid locking the table during the build.

(PG01)


[error] 69-69: CREATE INDEX should use CONCURRENTLY to avoid locking the table during the build.

(PG01)


[error] 70-70: CREATE INDEX should use CONCURRENTLY to avoid locking the table during the build.

(PG01)


[error] 71-71: CREATE INDEX should use CONCURRENTLY to avoid locking the table during the build.

(PG01)


[error] 72-72: CREATE INDEX should use CONCURRENTLY to avoid locking the table during the build.

(PG01)


[error] 73-73: CREATE INDEX should use CONCURRENTLY to avoid locking the table during the build.

(PG01)

🪛 Squawk (2.59.0)

[warning] 67-67: During normal index creation, table updates are blocked, but reads are still allowed. Use concurrently to avoid blocking writes.

(require-concurrent-index-creation)


[warning] 68-68: During normal index creation, table updates are blocked, but reads are still allowed. Use concurrently to avoid blocking writes.

(require-concurrent-index-creation)


[warning] 69-69: During normal index creation, table updates are blocked, but reads are still allowed. Use concurrently to avoid blocking writes.

(require-concurrent-index-creation)


[warning] 70-70: During normal index creation, table updates are blocked, but reads are still allowed. Use concurrently to avoid blocking writes.

(require-concurrent-index-creation)


[warning] 71-71: During normal index creation, table updates are blocked, but reads are still allowed. Use concurrently to avoid blocking writes.

(require-concurrent-index-creation)


[warning] 72-72: During normal index creation, table updates are blocked, but reads are still allowed. Use concurrently to avoid blocking writes.

(require-concurrent-index-creation)


[warning] 73-73: During normal index creation, table updates are blocked, but reads are still allowed. Use concurrently to avoid blocking writes.

(require-concurrent-index-creation)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@drizzle/migrations/0005_fix_chat_db_fk_normalization.sql` around lines 67 -
73, The index creation statements in the migration need to be changed to use
CONCURRENTLY so they don’t block writes; update the CREATE INDEX statements for
the affected tables in the migration section that builds the user_id indexes. If
drizzle-kit executes migrations inside a transaction, move these index
statements out into a separate migration or ensure they run without a
transaction, since CREATE INDEX CONCURRENTLY cannot be executed inside one.

Source: Linters/SAST tools

…LY indexes

- Migration 0005: Drop old auth.users FKs, create new public.users FKs with NOT VALID
  to avoid blocking writes during creation, then VALIDATE CONSTRAINT to enforce.
- Migration 0006: Separate file for CREATE INDEX CONCURRENTLY on user_id columns.
  Must run outside a transaction (drizzle-kit wraps in transaction).
  Includes manual execution instructions.

Both changes reduce lock contention and prevent write-blocking during deployment.
@vercel

vercel Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Someone is attempting to deploy a commit to the QCX-MAIN Team on Vercel.

A member of the Team first needs to authorize it.

@ngoiyaeric ngoiyaeric merged commit de1c5c0 into QueueLab:main Jul 9, 2026
3 of 5 checks passed
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.

2 participants