fix: redirect all user_id FKs from auth.users to public.users for Clerk auth support#712
Conversation
…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 reviews are paused for this user.Troubleshooting steps vary by plan Learn more → On a Teams plan? Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center? |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
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. |
|
Warning Review limit reached
Next review available in: 44 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the 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 configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (2)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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
📒 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 & IntegrationVerify
ON DELETE CASCADEis intentional for all tables.All 7 FKs use
ON DELETE CASCADE, meaning deleting a user frompublic.userswill automatically delete all their chats, messages, system_prompts, locations, visualizations, chat_participants, and prompt_generation_jobs. If the original constraints (referencingauth.users) used a different delete behavior (e.g.,ON DELETE SET NULLorON DELETE RESTRICT), this migration silently changes the data integrity semantics. Confirm this cascade behavior is desired, especially for tables likemessageswhere cascading from a parentchatsdeletion may already handle cleanup.
15-64: 🩺 Stability & AvailabilityVerify transaction wrapping behavior in drizzle-kit.
If drizzle-kit executes this migration inside a single transaction, the
DROP CONSTRAINT+ADD CONSTRAINTsequence is atomic (good — ifADDfails,DROPis rolled back). However, if it does not wrap in a transaction, there's a window betweenDROPandADDwhere 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.
| 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); |
There was a problem hiding this comment.
🩺 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.
| 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.
|
Someone is attempting to deploy a commit to the QCX-MAIN Team on Vercel. A member of the Team first needs to authorize it. |
Problem
Chat history save was failing for Clerk-authenticated users with the error:
Root Cause
All
user_idforeign key constraints on the chat-related tables referencedauth.users.id, but Clerk-authenticated users only exist inpublic.users(not inauth.users). Theauth.userstable is managed by Supabase Auth, while Clerk users are synced topublic.usersvia thesync_clerk_user()function and theon_auth_user_createdtrigger.Affected constraints:
chats_user_id_fkey— was pointing toauth.users.idmessages_user_id_fkey— was pointing toauth.users.idsystem_prompts_user_id_fkey— was pointing toauth.users.idlocations_user_id_fkey— was pointing toauth.users.idvisualizations_user_id_fkey— was pointing toauth.users.idchat_participants_user_id_fkey— was pointing toauth.users.idprompt_generation_jobs_user_id_fkey— was pointing toauth.users.idFix
All FK constraints have been redirected to reference
public.users.idas the canonical identity table. This normalizes the schema so thatpublic.usersserves as the unified identity store for both Clerk and Supabase Auth users.Additionally, performance indexes have been added on all
user_idcolumns.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