fix(db): guard DDL on out-of-band (orphan) tables for clean replay#333
fix(db): guard DDL on out-of-band (orphan) tables for clean replay#333adm01-debug wants to merge 1 commit into
Conversation
Audit found only 5 post-baseline migrations doing DDL on tables that exist in prod but were never created by a migration (created out-of-band). Of those, t38 already self-guards (IF EXISTS) and 20260519163704 was a false positive (`tags` is a column of products). The 3 real blockers are guarded so a fresh replay no-ops on the missing table instead of aborting; production (where the tables exist) behaves identically: - onda13: CREATE POLICY on audit_log_gravacao / seo_audit_log -> wrapped in to_regclass(...) IS NOT NULL guards. - onda19: DROP/CREATE TRIGGER + ALTER on kit_component_print_areas, supplier_technique_mappings, tabela_preco_gravacao_oficial, variant_supplier_sources, and the view reading tabela_preco_gravacao_oficial -> wrapped in EXCEPTION WHEN undefined_table guards (non-orphan ALTERs left intact). - harden_anon_graphql_exposure: all REVOKE ... FROM anon wrapped (idempotent; no-op when the table is absent). https://claude.ai/code/session_01MBTzmQYmrgwLnwfxRS3PNU
|
Updates to Preview Branch (claude/replay-drift-tables) ↗︎
Tasks are run on every commit but only new migration files are pushed.
❌ Branch Error • Mon, 25 May 2026 11:04:09 UTC View logs for this Workflow Run ↗︎. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
WalkthroughPR introduz três migrações SQL que encapsulam operações DDL (políticas RLS, drops de triggers, alterações de colunas, revogações de grants) em blocos ChangesHardening de migrações SQL contra ambientes de replay
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Possibly related PRs
Suggested labels
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Warning Review ran into problems🔥 ProblemsGit: Failed to clone repository. Please run the Comment |
There was a problem hiding this comment.
Pull request overview
Guards post-baseline Supabase migrations so a clean DB replay won’t abort when encountering DDL against “out-of-band” (orphan) tables that exist in prod but not in the migration history.
Changes:
- Wrapped
REVOKE ... FROM anonstatements inDO ... EXCEPTION WHEN undefined_tableblocks to no-op when target relations don’t exist. - Wrapped
ALTER TABLE, trigger drop/create, and view rebuild steps (that touch orphan tables) inDO ... EXCEPTION WHEN undefined_tableblocks for replay safety. - Wrapped audit log RLS policy changes in
DOblocks gated byto_regclass(...) IS NOT NULL.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| supabase/migrations/20260524210100_harden_anon_graphql_exposure.sql | Makes REVOKE SELECT ... FROM anon idempotent when the underlying relation is missing. |
| supabase/migrations/20260515030000_onda19_numeric_precision.sql | Adds replay guards around orphan-table ALTER/TRIGGER/VIEW statements to prevent clean replay failures. |
| supabase/migrations/20260514220543_onda13_rls_audit_logs_admin_only.sql | Adds to_regclass guards so policy DDL no-ops if the audit tables don’t exist in replay. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| -- 5. RECREATE view (definição idêntica à original — só rebuild para usar novo tipo) | ||
| CREATE OR REPLACE VIEW public.v_audit_paradoxos_gravacao AS | ||
| WITH faixas_ord AS ( | ||
| SELECT t.id, | ||
| t.codigo_tabela, | ||
| t.nome, | ||
| t.grupo_tecnica, | ||
| t.custo_setup, | ||
| t.markup_percent, | ||
| f.quantidade_minima, | ||
| f.quantidade_maxima, | ||
| f.preco_unitario, | ||
| lag(f.preco_unitario) OVER ( | ||
| PARTITION BY t.id, | ||
| (COALESCE(f.largura_min, 0::numeric)), | ||
| (COALESCE(f.largura_max, 0::numeric)), | ||
| (COALESCE(f.altura_min, 0::numeric)), | ||
| (COALESCE(f.altura_max, 0::numeric)) | ||
| ORDER BY f.quantidade_minima | ||
| ) AS preco_anterior, | ||
| lag(f.quantidade_maxima) OVER ( | ||
| PARTITION BY t.id, | ||
| (COALESCE(f.largura_min, 0::numeric)), | ||
| (COALESCE(f.largura_max, 0::numeric)), | ||
| (COALESCE(f.altura_min, 0::numeric)), | ||
| (COALESCE(f.altura_max, 0::numeric)) | ||
| ORDER BY f.quantidade_minima | ||
| ) AS qty_max_anterior | ||
| FROM public.tabela_preco_gravacao_oficial t | ||
| JOIN public.tabela_preco_gravacao_oficial_faixa f | ||
| ON f.tabela_preco_gravacao_id = t.id | ||
| WHERE t.ativo = true | ||
| ) | ||
| SELECT | ||
| codigo_tabela, | ||
| nome, | ||
| grupo_tecnica, | ||
| qty_max_anterior AS qty_pico_ant, | ||
| preco_anterior, | ||
| round(GREATEST(qty_max_anterior::numeric * preco_anterior, custo_setup) * (1::numeric + markup_percent / 100::numeric), 2) AS venda_no_pico, | ||
| quantidade_minima AS qty_inicio, | ||
| preco_unitario AS preco_atual, | ||
| round(GREATEST(quantidade_minima::numeric * preco_unitario, custo_setup) * (1::numeric + markup_percent / 100::numeric), 2) AS venda_inicio, | ||
| round(GREATEST(quantidade_minima::numeric * preco_unitario, custo_setup) - GREATEST(qty_max_anterior::numeric * preco_anterior, custo_setup), 2) AS economia_cliente_se_subir_faixa, | ||
| CASE | ||
| WHEN preco_anterior IS NULL THEN 'primeira_faixa'::text | ||
| WHEN qty_max_anterior IS NULL THEN 'sem_pico_anterior'::text | ||
| WHEN GREATEST(quantidade_minima::numeric * preco_unitario, custo_setup) < GREATEST(qty_max_anterior::numeric * preco_anterior, custo_setup) THEN 'PARADOXO_NATURAL'::text | ||
| ELSE 'OK'::text | ||
| END AS status_natural | ||
| FROM faixas_ord | ||
| WHERE preco_anterior IS NOT NULL AND qty_max_anterior IS NOT NULL | ||
| ORDER BY codigo_tabela, quantidade_minima; | ||
| -- Guardada: referencia tabela_preco_gravacao_oficial(_faixa) (out-of-band). | ||
| DO $g$ BEGIN | ||
| CREATE OR REPLACE VIEW public.v_audit_paradoxos_gravacao AS | ||
| WITH faixas_ord AS ( |
🎯 PLANO MASTER — coordenação das 5 PRs abertas
Decisões cravadas (25/05/2026)
Estado atual
Topologia do drift de replay (Facet 2)
Sequência operacional após este comentário
🤖 Coordenação: Claude (sessão 25/05/2026). Quando reentrar em outra sessão, leia este bloco primeiro antes de fazer qualquer mudança — ele é a fonte da verdade. |
|
Your plan includes 5 reviews of capacity. Refill in 235 minutes and 7 seconds. |
🚪 Fechando esta PR — duplicada do #313 já mergeado em mainEvidência forense
Validação no código (origin/main @ caad622)Os 3 arquivos alvo desta PR já estão em main com os guards aplicados (totalizando 37 statements protegidos). Análise complementar do erro
|
Audit found only 5 post-baseline migrations doing DDL on tables that exist in prod but were never created by a migration (created out-of-band). Of those, t38 already self-guards (IF EXISTS) and 20260519163704 was a false positive (
tagsis a column of products). The 3 real blockers are guarded so a fresh replay no-ops on the missing table instead of aborting; production (where the tables exist) behaves identically:https://claude.ai/code/session_01MBTzmQYmrgwLnwfxRS3PNU
📋 Descrição
🎯 Tipo de mudança
🔗 Issues relacionadas
Closes #
Refs #
🌐 Sistemas afetados
🧪 Como testar
✅ Checklist pré-merge
Qualidade
npx tsc --noEmitpassa sem errosnpm run test)Segurança
console.logcom payloads sensíveis (usarlogger.*)Documentação
mem://) se a mudança afetar arquitetura/regras_backup_*_YYYYMMDDse destrutivasUI
📸 Screenshots (se UI)
🔄 Plano de rollback
Summary by cubic
Guards DDL in post-baseline migrations so a fresh DB replay no-ops on missing out-of-band tables instead of failing. Production behavior is unchanged.
to_regclass(...) IS NOT NULL.kit_component_print_areas,supplier_technique_mappings,tabela_preco_gravacao_oficial(_faixa), andvariant_supplier_sourcesinDO ... EXCEPTION WHEN undefined_tableblocks; non-orphan ALTERs left as-is.REVOKE ... FROM anonstatements inDO ... EXCEPTION WHEN undefined_tableto stay idempotent.Written for commit ba5a589. Summary will update on new commits. Review in cubic
Summary by CodeRabbit
Bug Fixes
Chores