Findings
Errors (23)
No primary key
Without a primary key rows are not individually addressable: no safe UPDATE/DELETE of one row, no logical replication, ORMs misbehave, and duplicates are legal.
Fix Add a natural composite key or a surrogate id.
ALTER TABLE codeintel_inference_scripts ADD PRIMARY KEY (...);
No primary key
Without a primary key rows are not individually addressable: no safe UPDATE/DELETE of one row, no logical replication, ORMs misbehave, and duplicates are legal.
Fix Add a natural composite key or a surrogate id.
ALTER TABLE codeintel_langugage_support_requests ADD PRIMARY KEY (...);
No primary key
Without a primary key rows are not individually addressable: no safe UPDATE/DELETE of one row, no logical replication, ORMs misbehave, and duplicates are legal.
Fix Add a natural composite key or a surrogate id.
ALTER TABLE configuration_policies_audit_logs ADD PRIMARY KEY (...);
No primary key
Without a primary key rows are not individually addressable: no safe UPDATE/DELETE of one row, no logical replication, ORMs misbehave, and duplicates are legal.
Fix Add a natural composite key or a surrogate id.
ALTER TABLE external_service_repos ADD PRIMARY KEY (...);
No primary key
Without a primary key rows are not individually addressable: no safe UPDATE/DELETE of one row, no logical replication, ORMs misbehave, and duplicates are legal.
Fix Add a natural composite key or a surrogate id.
ALTER TABLE external_service_sync_jobs ADD PRIMARY KEY (...);
No primary key
Without a primary key rows are not individually addressable: no safe UPDATE/DELETE of one row, no logical replication, ORMs misbehave, and duplicates are legal.
Fix Add a natural composite key or a surrogate id.
ALTER TABLE feature_flag_overrides ADD PRIMARY KEY (...);
No primary key
Without a primary key rows are not individually addressable: no safe UPDATE/DELETE of one row, no logical replication, ORMs misbehave, and duplicates are legal.
Fix Add a natural composite key or a surrogate id.
ALTER TABLE gitserver_repos_statistics ADD PRIMARY KEY (...);
No primary key
Without a primary key rows are not individually addressable: no safe UPDATE/DELETE of one row, no logical replication, ORMs misbehave, and duplicates are legal.
Fix Add a natural composite key or a surrogate id.
ALTER TABLE insights_settings_migration_jobs ADD PRIMARY KEY (...);
No primary key
Without a primary key rows are not individually addressable: no safe UPDATE/DELETE of one row, no logical replication, ORMs misbehave, and duplicates are legal.
Fix Add a natural composite key or a surrogate id.
ALTER TABLE lsif_nearest_uploads ADD PRIMARY KEY (...);
No primary key
Without a primary key rows are not individually addressable: no safe UPDATE/DELETE of one row, no logical replication, ORMs misbehave, and duplicates are legal.
Fix Add a natural composite key or a surrogate id.
ALTER TABLE lsif_nearest_uploads_links ADD PRIMARY KEY (...);
No primary key
Without a primary key rows are not individually addressable: no safe UPDATE/DELETE of one row, no logical replication, ORMs misbehave, and duplicates are legal.
Fix Add a natural composite key or a surrogate id.
ALTER TABLE lsif_uploads_audit_logs ADD PRIMARY KEY (...);
No primary key
Without a primary key rows are not individually addressable: no safe UPDATE/DELETE of one row, no logical replication, ORMs misbehave, and duplicates are legal.
Fix Add a natural composite key or a surrogate id.
ALTER TABLE lsif_uploads_reference_counts ADD PRIMARY KEY (...);
No primary key
Without a primary key rows are not individually addressable: no safe UPDATE/DELETE of one row, no logical replication, ORMs misbehave, and duplicates are legal.
Fix Add a natural composite key or a surrogate id.
ALTER TABLE lsif_uploads_visible_at_tip ADD PRIMARY KEY (...);
No primary key
Without a primary key rows are not individually addressable: no safe UPDATE/DELETE of one row, no logical replication, ORMs misbehave, and duplicates are legal.
Fix Add a natural composite key or a surrogate id.
ALTER TABLE query_runner_state ADD PRIMARY KEY (...);
No primary key
Without a primary key rows are not individually addressable: no safe UPDATE/DELETE of one row, no logical replication, ORMs misbehave, and duplicates are legal.
Fix Add a natural composite key or a surrogate id.
ALTER TABLE repo_pending_permissions ADD PRIMARY KEY (...);
No primary key
Without a primary key rows are not individually addressable: no safe UPDATE/DELETE of one row, no logical replication, ORMs misbehave, and duplicates are legal.
Fix Add a natural composite key or a surrogate id.
ALTER TABLE repo_permissions ADD PRIMARY KEY (...);
No primary key
Without a primary key rows are not individually addressable: no safe UPDATE/DELETE of one row, no logical replication, ORMs misbehave, and duplicates are legal.
Fix Add a natural composite key or a surrogate id.
ALTER TABLE repo_statistics ADD PRIMARY KEY (...);
No primary key
Without a primary key rows are not individually addressable: no safe UPDATE/DELETE of one row, no logical replication, ORMs misbehave, and duplicates are legal.
Fix Add a natural composite key or a surrogate id.
ALTER TABLE search_context_repos ADD PRIMARY KEY (...);
No primary key
Without a primary key rows are not individually addressable: no safe UPDATE/DELETE of one row, no logical replication, ORMs misbehave, and duplicates are legal.
Fix Add a natural composite key or a surrogate id.
ALTER TABLE sub_repo_permissions ADD PRIMARY KEY (...);
No primary key
Without a primary key rows are not individually addressable: no safe UPDATE/DELETE of one row, no logical replication, ORMs misbehave, and duplicates are legal.
Fix Add a natural composite key or a surrogate id.
ALTER TABLE user_emails ADD PRIMARY KEY (...);
No primary key
Without a primary key rows are not individually addressable: no safe UPDATE/DELETE of one row, no logical replication, ORMs misbehave, and duplicates are legal.
Fix Add a natural composite key or a surrogate id.
ALTER TABLE user_pending_permissions ADD PRIMARY KEY (...);
No primary key
Without a primary key rows are not individually addressable: no safe UPDATE/DELETE of one row, no logical replication, ORMs misbehave, and duplicates are legal.
Fix Add a natural composite key or a surrogate id.
ALTER TABLE user_permissions ADD PRIMARY KEY (...);
No primary key
Without a primary key rows are not individually addressable: no safe UPDATE/DELETE of one row, no logical replication, ORMs misbehave, and duplicates are legal.
Fix Add a natural composite key or a surrogate id.
ALTER TABLE user_public_repos ADD PRIMARY KEY (...);
Warnings (340)
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan access_requests to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to access_requests maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. decision_by_user_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_access_requests_decision_by_user_id ON access_requests(decision_by_user_id) WHERE decision_by_user_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan access_requests to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to access_requests maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_access_requests_tenant_id ON access_requests(tenant_id) WHERE tenant_id IS NOT NULL;
Enum-like column with no CHECK
status is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE access_requests ADD CONSTRAINT access_requests_status_check CHECK (status IN (...));
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan access_tokens to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to access_tokens maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_access_tokens_creator_user_id ON access_tokens(creator_user_id);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan access_tokens to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to access_tokens maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_access_tokens_subject_user_id ON access_tokens(subject_user_id);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan access_tokens to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to access_tokens maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_access_tokens_tenant_id ON access_tokens(tenant_id) WHERE tenant_id IS NOT NULL;
Soft delete collides with non-partial UNIQUE
access_tokens soft-deletes via deleted_at, but (value_sha256) is unique across live AND deleted rows. A user who deletes their account can never sign up again with the same value, and ON CONFLICT upserts will resurrect ghosts.
Fix Replace with a partial unique index scoped to live rows.
DROP CONSTRAINT/INDEX ...; CREATE UNIQUE INDEX access_tokens_value_sha256_live ON access_tokens(value_sha256) WHERE deleted_at IS NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan aggregated_user_statistics to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to aggregated_user_statistics maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_aggregated_user_statistics_tenant_id ON aggregated_user_statistics(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan assigned_owners to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to assigned_owners maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_assigned_owners_owner_user_id ON assigned_owners(owner_user_id);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan assigned_owners to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to assigned_owners maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_assigned_owners_tenant_id ON assigned_owners(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan assigned_owners to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to assigned_owners maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. who_assigned_user_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_assigned_owners_who_assigned_user_id ON assigned_owners(who_assigned_user_id) WHERE who_assigned_user_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on teams must scan assigned_teams to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to assigned_teams maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often teams rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_assigned_teams_owner_team_id ON assigned_teams(owner_team_id);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan assigned_teams to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to assigned_teams maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_assigned_teams_tenant_id ON assigned_teams(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan assigned_teams to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to assigned_teams maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. who_assigned_team_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_assigned_teams_who_assigned_team_id ON assigned_teams(who_assigned_team_id) WHERE who_assigned_team_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on batch_specs must scan batch_changes to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to batch_changes maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often batch_specs rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_batch_changes_batch_spec_id ON batch_changes(batch_spec_id);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan batch_changes to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to batch_changes maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. creator_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_batch_changes_creator_id ON batch_changes(creator_id) WHERE creator_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan batch_changes to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to batch_changes maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. last_applier_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_batch_changes_last_applier_id ON batch_changes(last_applier_id) WHERE last_applier_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan batch_changes to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to batch_changes maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_batch_changes_tenant_id ON batch_changes(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign-key cycle
batch_changes → batch_specs → batch_changes. Rows must be inserted with a deferred constraint or a NULL-then-update dance; backups/restores and truncation have no valid order; ON DELETE CASCADE can loop.
Fix Break the cycle (move one FK to a link table) or mark one FK DEFERRABLE INITIALLY DEFERRED.
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on github_apps must scan batch_changes_site_credentials to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to batch_changes_site_credentials maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often github_apps rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. github_app_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_batch_changes_site_credentials_github_app_id ON batch_changes_site_credentials(github_app_id) WHERE github_app_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan batch_changes_site_credentials to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to batch_changes_site_credentials maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_batch_changes_site_credentials_tenant_id ON batch_changes_site_credentials(tenant_id) WHERE tenant_id IS NOT NULL;
Enum-like column with no CHECK
external_service_type is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE batch_changes_site_credentials ADD CONSTRAINT batch_changes_site_credentials_external_service_type_check CHECK (external_service_type IN (...));
Polymorphic reference without referential integrity
external_service_id points at different tables depending on external_service_type. No FK can express that, so orphans accumulate silently and every join needs a CASE. Acceptable for append-only audit data; painful anywhere the target must still exist.
Fix Either one nullable FK per target with a CHECK that exactly one is set, or a supertype table that the targets reference.
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan batch_spec_execution_cache_entries to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to batch_spec_execution_cache_entries maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_batch_spec_execution_cache_entries_tenant_id ON batch_spec_execution_cache_entries(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan batch_spec_resolution_jobs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to batch_spec_resolution_jobs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_batch_spec_resolution_jobs_initiator_id ON batch_spec_resolution_jobs(initiator_id);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan batch_spec_resolution_jobs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to batch_spec_resolution_jobs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_batch_spec_resolution_jobs_tenant_id ON batch_spec_resolution_jobs(tenant_id) WHERE tenant_id IS NOT NULL;
Enum-like column with no CHECK
state is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE batch_spec_resolution_jobs ADD CONSTRAINT batch_spec_resolution_jobs_state_check CHECK (state IN (...));
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan batch_spec_workspace_execution_jobs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to batch_spec_workspace_execution_jobs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_batch_spec_workspace_execution_jobs_tenant_id ON batch_spec_workspace_execution_jobs(tenant_id) WHERE tenant_id IS NOT NULL;
Enum-like column with no CHECK
state is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE batch_spec_workspace_execution_jobs ADD CONSTRAINT batch_spec_workspace_execution_jobs_state_check CHECK (state IN (...));
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan batch_spec_workspace_execution_last_dequeues to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to batch_spec_workspace_execution_last_dequeues maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_batch_spec_workspace_execution_last_dequeues_tenant_id ON batch_spec_workspace_execution_last_dequeues(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan batch_spec_workspace_files to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to batch_spec_workspace_files maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_batch_spec_workspace_files_tenant_id ON batch_spec_workspace_files(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on repo must scan batch_spec_workspaces to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to batch_spec_workspaces maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often repo rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_batch_spec_workspaces_repo_id ON batch_spec_workspaces(repo_id);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan batch_spec_workspaces to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to batch_spec_workspaces maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_batch_spec_workspaces_tenant_id ON batch_spec_workspaces(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on batch_changes must scan batch_specs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to batch_specs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often batch_changes rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. batch_change_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_batch_specs_batch_change_id ON batch_specs(batch_change_id) WHERE batch_change_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan batch_specs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to batch_specs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_batch_specs_tenant_id ON batch_specs(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan batch_specs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to batch_specs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. user_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_batch_specs_user_id ON batch_specs(user_id) WHERE user_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan cached_available_indexers to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to cached_available_indexers maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_cached_available_indexers_tenant_id ON cached_available_indexers(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan changeset_events to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to changeset_events maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_changeset_events_tenant_id ON changeset_events(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on batch_changes must scan changeset_jobs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to changeset_jobs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often batch_changes rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_changeset_jobs_batch_change_id ON changeset_jobs(batch_change_id);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on changesets must scan changeset_jobs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to changeset_jobs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often changesets rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_changeset_jobs_changeset_id ON changeset_jobs(changeset_id);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan changeset_jobs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to changeset_jobs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_changeset_jobs_tenant_id ON changeset_jobs(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan changeset_jobs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to changeset_jobs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_changeset_jobs_user_id ON changeset_jobs(user_id);
Enum-like column with no CHECK
job_type is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE changeset_jobs ADD CONSTRAINT changeset_jobs_job_type_check CHECK (job_type IN (...));
Enum-like column with no CHECK
state is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE changeset_jobs ADD CONSTRAINT changeset_jobs_state_check CHECK (state IN (...));
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on repo must scan changeset_specs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to changeset_specs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often repo rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_changeset_specs_repo_id ON changeset_specs(repo_id);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan changeset_specs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to changeset_specs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_changeset_specs_tenant_id ON changeset_specs(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan changeset_specs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to changeset_specs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. user_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_changeset_specs_user_id ON changeset_specs(user_id) WHERE user_id IS NOT NULL;
Enum-like column with no CHECK
type is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE changeset_specs ADD CONSTRAINT changeset_specs_type_check CHECK (type IN (...));
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on batch_changes must scan changesets to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to changesets maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often batch_changes rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. owned_by_batch_change_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_changesets_owned_by_batch_change_id ON changesets(owned_by_batch_change_id) WHERE owned_by_batch_change_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on changeset_specs must scan changesets to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to changesets maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often changeset_specs rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. previous_spec_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_changesets_previous_spec_id ON changesets(previous_spec_id) WHERE previous_spec_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan changesets to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to changesets maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_changesets_tenant_id ON changesets(tenant_id) WHERE tenant_id IS NOT NULL;
Enum-like column with no CHECK
external_state is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE changesets ADD CONSTRAINT changesets_external_state_check CHECK (external_state IN (...));
F533changesets external_review_stateundocumented-enum Enum-like column with no CHECK
external_review_state is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE changesets ADD CONSTRAINT changesets_external_review_state_check CHECK (external_review_state IN (...));
F534changesets external_check_stateundocumented-enum Enum-like column with no CHECK
external_check_state is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE changesets ADD CONSTRAINT changesets_external_check_state_check CHECK (external_check_state IN (...));
Enum-like column with no CHECK
publication_state is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE changesets ADD CONSTRAINT changesets_publication_state_check CHECK (publication_state IN (...));
Enum-like column with no CHECK
reconciler_state is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE changesets ADD CONSTRAINT changesets_reconciler_state_check CHECK (reconciler_state IN (...));
Enum-like column with no CHECK
computed_state is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE changesets ADD CONSTRAINT changesets_computed_state_check CHECK (computed_state IN (...));
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on cm_emails must scan cm_action_jobs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to cm_action_jobs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often cm_emails rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. email is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_cm_action_jobs_email ON cm_action_jobs(email) WHERE email IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on cm_slack_webhooks must scan cm_action_jobs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to cm_action_jobs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often cm_slack_webhooks rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. slack_webhook is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_cm_action_jobs_slack_webhook ON cm_action_jobs(slack_webhook) WHERE slack_webhook IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan cm_action_jobs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to cm_action_jobs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_cm_action_jobs_tenant_id ON cm_action_jobs(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on cm_webhooks must scan cm_action_jobs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to cm_action_jobs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often cm_webhooks rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. webhook is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_cm_action_jobs_webhook ON cm_action_jobs(webhook) WHERE webhook IS NOT NULL;
Enum-like column with no CHECK
state is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE cm_action_jobs ADD CONSTRAINT cm_action_jobs_state_check CHECK (state IN (...));
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan cm_emails to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to cm_emails maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_cm_emails_changed_by ON cm_emails(changed_by);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan cm_emails to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to cm_emails maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_cm_emails_created_by ON cm_emails(created_by);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on cm_monitors must scan cm_emails to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to cm_emails maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often cm_monitors rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_cm_emails_monitor ON cm_emails(monitor);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan cm_emails to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to cm_emails maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_cm_emails_tenant_id ON cm_emails(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on repo must scan cm_last_searched to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to cm_last_searched maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often repo rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_cm_last_searched_repo_id ON cm_last_searched(repo_id);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan cm_last_searched to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to cm_last_searched maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_cm_last_searched_tenant_id ON cm_last_searched(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan cm_monitors to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to cm_monitors maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_cm_monitors_changed_by ON cm_monitors(changed_by);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan cm_monitors to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to cm_monitors maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_cm_monitors_created_by ON cm_monitors(created_by);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on orgs must scan cm_monitors to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to cm_monitors maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often orgs rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. namespace_org_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_cm_monitors_namespace_org_id ON cm_monitors(namespace_org_id) WHERE namespace_org_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan cm_monitors to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to cm_monitors maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_cm_monitors_tenant_id ON cm_monitors(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan cm_monitors to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to cm_monitors maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_cm_monitors_namespace_user_id ON cm_monitors(namespace_user_id);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan cm_queries to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to cm_queries maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_cm_queries_tenant_id ON cm_queries(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan cm_queries to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to cm_queries maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_cm_queries_changed_by ON cm_queries(changed_by);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan cm_queries to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to cm_queries maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_cm_queries_created_by ON cm_queries(created_by);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on cm_monitors must scan cm_queries to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to cm_queries maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often cm_monitors rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_cm_queries_monitor ON cm_queries(monitor);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on cm_emails must scan cm_recipients to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to cm_recipients maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often cm_emails rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_cm_recipients_email ON cm_recipients(email);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on orgs must scan cm_recipients to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to cm_recipients maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often orgs rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. namespace_org_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_cm_recipients_namespace_org_id ON cm_recipients(namespace_org_id) WHERE namespace_org_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan cm_recipients to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to cm_recipients maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_cm_recipients_tenant_id ON cm_recipients(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan cm_recipients to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to cm_recipients maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. namespace_user_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_cm_recipients_namespace_user_id ON cm_recipients(namespace_user_id) WHERE namespace_user_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan cm_slack_webhooks to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to cm_slack_webhooks maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_cm_slack_webhooks_changed_by ON cm_slack_webhooks(changed_by);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan cm_slack_webhooks to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to cm_slack_webhooks maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_cm_slack_webhooks_created_by ON cm_slack_webhooks(created_by);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan cm_slack_webhooks to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to cm_slack_webhooks maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_cm_slack_webhooks_tenant_id ON cm_slack_webhooks(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on cm_queries must scan cm_trigger_jobs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to cm_trigger_jobs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often cm_queries rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_cm_trigger_jobs_query ON cm_trigger_jobs(query);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan cm_trigger_jobs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to cm_trigger_jobs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_cm_trigger_jobs_tenant_id ON cm_trigger_jobs(tenant_id) WHERE tenant_id IS NOT NULL;
Enum-like column with no CHECK
state is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE cm_trigger_jobs ADD CONSTRAINT cm_trigger_jobs_state_check CHECK (state IN (...));
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan cm_webhooks to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to cm_webhooks maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_cm_webhooks_changed_by ON cm_webhooks(changed_by);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan cm_webhooks to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to cm_webhooks maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_cm_webhooks_created_by ON cm_webhooks(created_by);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan cm_webhooks to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to cm_webhooks maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_cm_webhooks_tenant_id ON cm_webhooks(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan code_hosts to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to code_hosts maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_code_hosts_tenant_id ON code_hosts(tenant_id) WHERE tenant_id IS NOT NULL;
Enum-like column with no CHECK
kind is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE code_hosts ADD CONSTRAINT code_hosts_kind_check CHECK (kind IN (...));
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan codeintel_autoindex_queue to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to codeintel_autoindex_queue maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_codeintel_autoindex_queue_tenant_id ON codeintel_autoindex_queue(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan codeintel_autoindexing_exceptions to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to codeintel_autoindexing_exceptions maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_codeintel_autoindexing_exceptions_tenant_id ON codeintel_autoindexing_exceptions(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan codeintel_commit_dates to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to codeintel_commit_dates maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_codeintel_commit_dates_tenant_id ON codeintel_commit_dates(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan codeintel_inference_scripts to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to codeintel_inference_scripts maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_codeintel_inference_scripts_tenant_id ON codeintel_inference_scripts(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan codeintel_initial_path_ranks to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to codeintel_initial_path_ranks maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_codeintel_initial_path_ranks_tenant_id ON codeintel_initial_path_ranks(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan codeintel_initial_path_ranks_processed to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to codeintel_initial_path_ranks_processed maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_codeintel_initial_path_ranks_processed_tenant_id ON codeintel_initial_path_ranks_processed(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan codeintel_langugage_support_requests to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to codeintel_langugage_support_requests maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_codeintel_langugage_support_requests_tenant_id ON codeintel_langugage_support_requests(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan codeintel_path_ranks to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to codeintel_path_ranks maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_codeintel_path_ranks_tenant_id ON codeintel_path_ranks(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan codeintel_ranking_definitions to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to codeintel_ranking_definitions maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_codeintel_ranking_definitions_tenant_id ON codeintel_ranking_definitions(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan codeintel_ranking_exports to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to codeintel_ranking_exports maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_codeintel_ranking_exports_tenant_id ON codeintel_ranking_exports(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on lsif_uploads must scan codeintel_ranking_exports to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to codeintel_ranking_exports maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often lsif_uploads rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. upload_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_codeintel_ranking_exports_upload_id ON codeintel_ranking_exports(upload_id) WHERE upload_id IS NOT NULL;
Soft delete collides with non-partial UNIQUE
codeintel_ranking_exports soft-deletes via deleted_at, but (graph_key, upload_id) is unique across live AND deleted rows. A user who deletes their account can never sign up again with the same value, and ON CONFLICT upserts will resurrect ghosts.
Fix Replace with a partial unique index scoped to live rows.
DROP CONSTRAINT/INDEX ...; CREATE UNIQUE INDEX codeintel_ranking_exports_graph_key_upload_id_live ON codeintel_ranking_exports(graph_key, upload_id) WHERE deleted_at IS NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan codeintel_ranking_graph_keys to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to codeintel_ranking_graph_keys maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_codeintel_ranking_graph_keys_tenant_id ON codeintel_ranking_graph_keys(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan codeintel_ranking_path_counts_inputs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to codeintel_ranking_path_counts_inputs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_codeintel_ranking_path_counts_inputs_tenant_id ON codeintel_ranking_path_counts_inputs(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan codeintel_ranking_progress to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to codeintel_ranking_progress maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_codeintel_ranking_progress_tenant_id ON codeintel_ranking_progress(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan codeintel_ranking_references to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to codeintel_ranking_references maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_codeintel_ranking_references_tenant_id ON codeintel_ranking_references(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan codeintel_ranking_references_processed to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to codeintel_ranking_references_processed maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_codeintel_ranking_references_processed_tenant_id ON codeintel_ranking_references_processed(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan codeowners to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to codeowners maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_codeowners_tenant_id ON codeowners(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on codeowners_owners must scan codeowners_individual_stats to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to codeowners_individual_stats maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often codeowners_owners rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_codeowners_individual_stats_owner_id ON codeowners_individual_stats(owner_id);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan codeowners_individual_stats to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to codeowners_individual_stats maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_codeowners_individual_stats_tenant_id ON codeowners_individual_stats(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan codeowners_owners to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to codeowners_owners maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_codeowners_owners_tenant_id ON codeowners_owners(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan commit_authors to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to commit_authors maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_commit_authors_tenant_id ON commit_authors(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan configuration_policies_audit_logs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to configuration_policies_audit_logs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_configuration_policies_audit_logs_tenant_id ON configuration_policies_audit_logs(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan context_detection_embedding_jobs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to context_detection_embedding_jobs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_context_detection_embedding_jobs_tenant_id ON context_detection_embedding_jobs(tenant_id) WHERE tenant_id IS NOT NULL;
Enum-like column with no CHECK
state is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE context_detection_embedding_jobs ADD CONSTRAINT context_detection_embedding_jobs_state_check CHECK (state IN (...));
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan discussion_comments to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to discussion_comments maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_discussion_comments_tenant_id ON discussion_comments(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan discussion_mail_reply_tokens to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to discussion_mail_reply_tokens maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_discussion_mail_reply_tokens_tenant_id ON discussion_mail_reply_tokens(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on discussion_threads must scan discussion_mail_reply_tokens to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to discussion_mail_reply_tokens maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often discussion_threads rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_discussion_mail_reply_tokens_thread_id ON discussion_mail_reply_tokens(thread_id);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on discussion_threads_target_repo must scan discussion_threads to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to discussion_threads maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often discussion_threads_target_repo rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. target_repo_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_discussion_threads_target_repo_id ON discussion_threads(target_repo_id) WHERE target_repo_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan discussion_threads to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to discussion_threads maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_discussion_threads_tenant_id ON discussion_threads(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign-key cycle
discussion_threads → discussion_threads_target_repo → discussion_threads. Rows must be inserted with a deferred constraint or a NULL-then-update dance; backups/restores and truncation have no valid order; ON DELETE CASCADE can loop.
Fix Break the cycle (move one FK to a link table) or mark one FK DEFERRABLE INITIALLY DEFERRED.
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan discussion_threads_target_repo to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to discussion_threads_target_repo maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_discussion_threads_target_repo_tenant_id ON discussion_threads_target_repo(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on discussion_threads must scan discussion_threads_target_repo to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to discussion_threads_target_repo maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often discussion_threads rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_discussion_threads_target_repo_thread_id ON discussion_threads_target_repo(thread_id);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan event_logs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to event_logs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_event_logs_tenant_id ON event_logs(tenant_id) WHERE tenant_id IS NOT NULL;
F546event_logs billing_product_categoryundocumented-enum Enum-like column with no CHECK
billing_product_category is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE event_logs ADD CONSTRAINT event_logs_billing_product_category_check CHECK (billing_product_category IN (...));
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan event_logs_export_allowlist to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to event_logs_export_allowlist maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_event_logs_export_allowlist_tenant_id ON event_logs_export_allowlist(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan event_logs_scrape_state to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to event_logs_scrape_state maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_event_logs_scrape_state_tenant_id ON event_logs_scrape_state(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan event_logs_scrape_state_own to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to event_logs_scrape_state_own maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_event_logs_scrape_state_own_tenant_id ON event_logs_scrape_state_own(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan executor_heartbeats to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to executor_heartbeats maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_executor_heartbeats_tenant_id ON executor_heartbeats(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan executor_job_tokens to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to executor_job_tokens maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_executor_job_tokens_tenant_id ON executor_job_tokens(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on executor_secrets must scan executor_secret_access_logs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to executor_secret_access_logs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often executor_secrets rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_executor_secret_access_logs_executor_secret_id ON executor_secret_access_logs(executor_secret_id);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan executor_secret_access_logs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to executor_secret_access_logs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_executor_secret_access_logs_tenant_id ON executor_secret_access_logs(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan executor_secret_access_logs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to executor_secret_access_logs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. user_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_executor_secret_access_logs_user_id ON executor_secret_access_logs(user_id) WHERE user_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan executor_secrets to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to executor_secrets maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. creator_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_executor_secrets_creator_id ON executor_secrets(creator_id) WHERE creator_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on orgs must scan executor_secrets to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to executor_secrets maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often orgs rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. namespace_org_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_executor_secrets_namespace_org_id ON executor_secrets(namespace_org_id) WHERE namespace_org_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan executor_secrets to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to executor_secrets maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. namespace_user_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_executor_secrets_namespace_user_id ON executor_secrets(namespace_user_id) WHERE namespace_user_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan executor_secrets to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to executor_secrets maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_executor_secrets_tenant_id ON executor_secrets(tenant_id) WHERE tenant_id IS NOT NULL;
Enum-like column with no CHECK
scope is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE executor_secrets ADD CONSTRAINT executor_secrets_scope_check CHECK (scope IN (...));
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan exhaustive_search_jobs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to exhaustive_search_jobs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_exhaustive_search_jobs_initiator_id ON exhaustive_search_jobs(initiator_id);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan exhaustive_search_jobs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to exhaustive_search_jobs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_exhaustive_search_jobs_tenant_id ON exhaustive_search_jobs(tenant_id) WHERE tenant_id IS NOT NULL;
Enum-like column with no CHECK
state is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE exhaustive_search_jobs ADD CONSTRAINT exhaustive_search_jobs_state_check CHECK (state IN (...));
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on repo must scan exhaustive_search_repo_jobs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to exhaustive_search_repo_jobs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often repo rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_exhaustive_search_repo_jobs_repo_id ON exhaustive_search_repo_jobs(repo_id);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on exhaustive_search_jobs must scan exhaustive_search_repo_jobs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to exhaustive_search_repo_jobs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often exhaustive_search_jobs rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_exhaustive_search_repo_jobs_search_job_id ON exhaustive_search_repo_jobs(search_job_id);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan exhaustive_search_repo_jobs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to exhaustive_search_repo_jobs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_exhaustive_search_repo_jobs_tenant_id ON exhaustive_search_repo_jobs(tenant_id) WHERE tenant_id IS NOT NULL;
Enum-like column with no CHECK
state is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE exhaustive_search_repo_jobs ADD CONSTRAINT exhaustive_search_repo_jobs_state_check CHECK (state IN (...));
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on exhaustive_search_repo_jobs must scan exhaustive_search_repo_revision_jobs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to exhaustive_search_repo_revision_jobs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often exhaustive_search_repo_jobs rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_exhaustive_search_repo_revision_jobs_search_repo_job_id ON exhaustive_search_repo_revision_jobs(search_repo_job_id);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan exhaustive_search_repo_revision_jobs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to exhaustive_search_repo_revision_jobs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_exhaustive_search_repo_revision_jobs_tenant_id ON exhaustive_search_repo_revision_jobs(tenant_id) WHERE tenant_id IS NOT NULL;
Enum-like column with no CHECK
state is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE exhaustive_search_repo_revision_jobs ADD CONSTRAINT exhaustive_search_repo_revision_jobs_state_check CHECK (state IN (...));
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan explicit_permissions_bitbucket_projects_jobs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to explicit_permissions_bitbucket_projects_jobs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_explicit_permissions_bitbucket_projects_jobs_tenant_id ON explicit_permissions_bitbucket_projects_jobs(tenant_id) WHERE tenant_id IS NOT NULL;
Enum-like column with no CHECK
state is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE explicit_permissions_bitbucket_projects_jobs ADD CONSTRAINT explicit_permissions_bitbucket_projects_jobs_state_check CHECK (state IN (...));
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan external_service_repos to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to external_service_repos maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_external_service_repos_tenant_id ON external_service_repos(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan external_service_sync_jobs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to external_service_sync_jobs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_external_service_sync_jobs_tenant_id ON external_service_sync_jobs(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on external_services must scan external_service_sync_jobs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to external_service_sync_jobs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often external_services rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_external_service_sync_jobs_external_service_id ON external_service_sync_jobs(external_service_id);
Enum-like column with no CHECK
state is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE external_service_sync_jobs ADD CONSTRAINT external_service_sync_jobs_state_check CHECK (state IN (...));
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on code_hosts must scan external_services to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to external_services maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often code_hosts rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. code_host_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_external_services_code_host_id ON external_services(code_host_id) WHERE code_host_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan external_services to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to external_services maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. creator_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_external_services_creator_id ON external_services(creator_id) WHERE creator_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan external_services to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to external_services maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. last_updater_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_external_services_last_updater_id ON external_services(last_updater_id) WHERE last_updater_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan external_services to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to external_services maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_external_services_tenant_id ON external_services(tenant_id) WHERE tenant_id IS NOT NULL;
Enum-like column with no CHECK
kind is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE external_services ADD CONSTRAINT external_services_kind_check CHECK (kind IN (...));
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan feature_flag_overrides to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to feature_flag_overrides maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_feature_flag_overrides_tenant_id ON feature_flag_overrides(tenant_id) WHERE tenant_id IS NOT NULL;
Soft delete collides with non-partial UNIQUE
feature_flag_overrides soft-deletes via deleted_at, but (namespace_org_id, flag_name) is unique across live AND deleted rows. A user who deletes their account can never sign up again with the same value, and ON CONFLICT upserts will resurrect ghosts.
Fix Replace with a partial unique index scoped to live rows.
DROP CONSTRAINT/INDEX ...; CREATE UNIQUE INDEX feature_flag_overrides_namespace_org_id_flag_name_live ON feature_flag_overrides(namespace_org_id, flag_name) WHERE deleted_at IS NULL;
Soft delete collides with non-partial UNIQUE
feature_flag_overrides soft-deletes via deleted_at, but (namespace_user_id, flag_name) is unique across live AND deleted rows. A user who deletes their account can never sign up again with the same value, and ON CONFLICT upserts will resurrect ghosts.
Fix Replace with a partial unique index scoped to live rows.
DROP CONSTRAINT/INDEX ...; CREATE UNIQUE INDEX feature_flag_overrides_namespace_user_id_flag_name_live ON feature_flag_overrides(namespace_user_id, flag_name) WHERE deleted_at IS NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan feature_flags to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to feature_flags maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_feature_flags_tenant_id ON feature_flags(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan github_app_installs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to github_app_installs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_github_app_installs_tenant_id ON github_app_installs(tenant_id) WHERE tenant_id IS NOT NULL;
Enum-like column with no CHECK
account_type is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE github_app_installs ADD CONSTRAINT github_app_installs_account_type_check CHECK (account_type IN (...));
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan github_apps to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to github_apps maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_github_apps_tenant_id ON github_apps(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on webhooks must scan github_apps to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to github_apps maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often webhooks rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. webhook_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_github_apps_webhook_id ON github_apps(webhook_id) WHERE webhook_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan gitserver_relocator_jobs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to gitserver_relocator_jobs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_gitserver_relocator_jobs_tenant_id ON gitserver_relocator_jobs(tenant_id) WHERE tenant_id IS NOT NULL;
Enum-like column with no CHECK
state is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE gitserver_relocator_jobs ADD CONSTRAINT gitserver_relocator_jobs_state_check CHECK (state IN (...));
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan gitserver_repos to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to gitserver_repos maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_gitserver_repos_tenant_id ON gitserver_repos(tenant_id) WHERE tenant_id IS NOT NULL;
Enum-like column with no CHECK
clone_status is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE gitserver_repos ADD CONSTRAINT gitserver_repos_clone_status_check CHECK (clone_status IN (...));
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan gitserver_repos_statistics to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to gitserver_repos_statistics maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_gitserver_repos_statistics_tenant_id ON gitserver_repos_statistics(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan gitserver_repos_sync_output to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to gitserver_repos_sync_output maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_gitserver_repos_sync_output_tenant_id ON gitserver_repos_sync_output(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan global_state to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to global_state maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_global_state_tenant_id ON global_state(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan insights_query_runner_jobs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to insights_query_runner_jobs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_insights_query_runner_jobs_tenant_id ON insights_query_runner_jobs(tenant_id) WHERE tenant_id IS NOT NULL;
Enum-like column with no CHECK
state is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE insights_query_runner_jobs ADD CONSTRAINT insights_query_runner_jobs_state_check CHECK (state IN (...));
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan insights_query_runner_jobs_dependencies to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to insights_query_runner_jobs_dependencies maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_insights_query_runner_jobs_dependencies_tenant_id ON insights_query_runner_jobs_dependencies(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan insights_settings_migration_jobs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to insights_settings_migration_jobs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_insights_settings_migration_jobs_tenant_id ON insights_settings_migration_jobs(tenant_id) WHERE tenant_id IS NOT NULL;
Enum-like column with no CHECK
type is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code. The comment lists The type of Git object (e.g., COMMIT, BRANCH, TAG)., i.e. the values are known.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE lsif_configuration_policies ADD CONSTRAINT lsif_configuration_policies_type_check CHECK (type IN (...));
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on lsif_uploads must scan lsif_dependency_indexing_jobs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to lsif_dependency_indexing_jobs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often lsif_uploads rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. upload_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_lsif_dependency_indexing_jobs_upload_id ON lsif_dependency_indexing_jobs(upload_id) WHERE upload_id IS NOT NULL;
Enum-like column with no CHECK
state is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE lsif_dependency_indexing_jobs ADD CONSTRAINT lsif_dependency_indexing_jobs_state_check CHECK (state IN (...));
Enum-like column with no CHECK
external_service_kind is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code. The comment lists Filter the external services for this kind to wait to have synced. If empty, external_service_sync is ignored and no external services are polled for their last sync time., i.e. the values are known.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE lsif_dependency_indexing_jobs ADD CONSTRAINT lsif_dependency_indexing_jobs_external_service_kind_check CHECK (external_service_kind IN (...));
Enum-like column with no CHECK
state is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE lsif_dependency_syncing_jobs ADD CONSTRAINT lsif_dependency_syncing_jobs_state_check CHECK (state IN (...));
Enum-like column with no CHECK
state is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE lsif_indexes ADD CONSTRAINT lsif_indexes_state_check CHECK (state IN (...));
Enum-like column with no CHECK
state is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE lsif_uploads ADD CONSTRAINT lsif_uploads_state_check CHECK (state IN (...));
Enum-like column with no CHECK
content_type is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code. The comment lists The content type of the upload record. For now, the default value is application/x-ndjson+lsif to backfill existing records. This will change as we remove LSIF support., i.e. the values are known.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE lsif_uploads ADD CONSTRAINT lsif_uploads_content_type_check CHECK (content_type IN (...));
Enum-like column with no CHECK
content_type is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE lsif_uploads_audit_logs ADD CONSTRAINT lsif_uploads_audit_logs_content_type_check CHECK (content_type IN (...));
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on orgs must scan names to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to names maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often orgs rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. org_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_names_org_id ON names(org_id) WHERE org_id IS NOT NULL;
F171names team_idfk-index Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on teams must scan names to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to names maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often teams rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. team_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_names_team_id ON names(team_id) WHERE team_id IS NOT NULL;
F172names tenant_idfk-index Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan names to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to names maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_names_tenant_id ON names(tenant_id) WHERE tenant_id IS NOT NULL;
F173names user_idfk-index Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan names to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to names maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. user_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_names_user_id ON names(user_id) WHERE user_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan namespace_permissions to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to namespace_permissions maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_namespace_permissions_tenant_id ON namespace_permissions(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan namespace_permissions to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to namespace_permissions maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_namespace_permissions_user_id ON namespace_permissions(user_id);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan notebook_stars to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to notebook_stars maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_notebook_stars_tenant_id ON notebook_stars(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan notebooks to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to notebooks maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. creator_user_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_notebooks_creator_user_id ON notebooks(creator_user_id) WHERE creator_user_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan notebooks to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to notebooks maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_notebooks_tenant_id ON notebooks(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan notebooks to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to notebooks maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. updater_user_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_notebooks_updater_user_id ON notebooks(updater_user_id) WHERE updater_user_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan org_invitations to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to org_invitations maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_org_invitations_sender_user_id ON org_invitations(sender_user_id);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan org_invitations to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to org_invitations maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_org_invitations_tenant_id ON org_invitations(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan org_members to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to org_members maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_org_members_tenant_id ON org_members(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan org_members to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to org_members maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_org_members_user_id ON org_members(user_id);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan org_stats to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to org_stats maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_org_stats_tenant_id ON org_stats(tenant_id) WHERE tenant_id IS NOT NULL;
F185orgs tenant_idfk-index Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan orgs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to orgs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_orgs_tenant_id ON orgs(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan orgs_open_beta_stats to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to orgs_open_beta_stats maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_orgs_open_beta_stats_tenant_id ON orgs_open_beta_stats(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan out_of_band_migrations to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to out_of_band_migrations maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_out_of_band_migrations_tenant_id ON out_of_band_migrations(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on out_of_band_migrations must scan out_of_band_migrations_errors to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to out_of_band_migrations_errors maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often out_of_band_migrations rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_out_of_band_migrations_errors_migration_id ON out_of_band_migrations_errors(migration_id);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan out_of_band_migrations_errors to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to out_of_band_migrations_errors maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_out_of_band_migrations_errors_tenant_id ON out_of_band_migrations_errors(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on outbound_webhooks must scan outbound_webhook_event_types to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to outbound_webhook_event_types maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often outbound_webhooks rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_outbound_webhook_event_types_outbound_webhook_id ON outbound_webhook_event_types(outbound_webhook_id);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan outbound_webhook_event_types to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to outbound_webhook_event_types maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_outbound_webhook_event_types_tenant_id ON outbound_webhook_event_types(tenant_id) WHERE tenant_id IS NOT NULL;
Enum-like column with no CHECK
event_type is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE outbound_webhook_event_types ADD CONSTRAINT outbound_webhook_event_types_event_type_check CHECK (event_type IN (...));
Enum-like column with no CHECK
scope is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE outbound_webhook_event_types ADD CONSTRAINT outbound_webhook_event_types_scope_check CHECK (scope IN (...));
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan outbound_webhook_jobs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to outbound_webhook_jobs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_outbound_webhook_jobs_tenant_id ON outbound_webhook_jobs(tenant_id) WHERE tenant_id IS NOT NULL;
Enum-like column with no CHECK
event_type is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE outbound_webhook_jobs ADD CONSTRAINT outbound_webhook_jobs_event_type_check CHECK (event_type IN (...));
Enum-like column with no CHECK
scope is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE outbound_webhook_jobs ADD CONSTRAINT outbound_webhook_jobs_scope_check CHECK (scope IN (...));
Enum-like column with no CHECK
state is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE outbound_webhook_jobs ADD CONSTRAINT outbound_webhook_jobs_state_check CHECK (state IN (...));
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on outbound_webhook_jobs must scan outbound_webhook_logs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to outbound_webhook_logs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often outbound_webhook_jobs rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_outbound_webhook_logs_job_id ON outbound_webhook_logs(job_id);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan outbound_webhook_logs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to outbound_webhook_logs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_outbound_webhook_logs_tenant_id ON outbound_webhook_logs(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan outbound_webhooks to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to outbound_webhooks maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. created_by is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_outbound_webhooks_created_by ON outbound_webhooks(created_by) WHERE created_by IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan outbound_webhooks to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to outbound_webhooks maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_outbound_webhooks_tenant_id ON outbound_webhooks(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan outbound_webhooks to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to outbound_webhooks maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. updated_by is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_outbound_webhooks_updated_by ON outbound_webhooks(updated_by) WHERE updated_by IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on commit_authors must scan own_aggregate_recent_contribution to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to own_aggregate_recent_contribution maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often commit_authors rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_own_aggregate_recent_contribution_commit_author_id ON own_aggregate_recent_contribution(commit_author_id);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan own_aggregate_recent_contribution to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to own_aggregate_recent_contribution maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_own_aggregate_recent_contribution_tenant_id ON own_aggregate_recent_contribution(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan own_aggregate_recent_view to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to own_aggregate_recent_view maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_own_aggregate_recent_view_tenant_id ON own_aggregate_recent_view(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan own_aggregate_recent_view to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to own_aggregate_recent_view maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_own_aggregate_recent_view_viewer_id ON own_aggregate_recent_view(viewer_id);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan own_background_jobs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to own_background_jobs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_own_background_jobs_tenant_id ON own_background_jobs(tenant_id) WHERE tenant_id IS NOT NULL;
Enum-like column with no CHECK
state is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE own_background_jobs ADD CONSTRAINT own_background_jobs_state_check CHECK (state IN (...));
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan own_signal_configurations to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to own_signal_configurations maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_own_signal_configurations_tenant_id ON own_signal_configurations(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on repo_paths must scan own_signal_recent_contribution to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to own_signal_recent_contribution maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often repo_paths rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_own_signal_recent_contribution_changed_file_path_id ON own_signal_recent_contribution(changed_file_path_id);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on commit_authors must scan own_signal_recent_contribution to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to own_signal_recent_contribution maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often commit_authors rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_own_signal_recent_contribution_commit_author_id ON own_signal_recent_contribution(commit_author_id);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan own_signal_recent_contribution to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to own_signal_recent_contribution maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_own_signal_recent_contribution_tenant_id ON own_signal_recent_contribution(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan ownership_path_stats to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to ownership_path_stats maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_ownership_path_stats_tenant_id ON ownership_path_stats(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan package_repo_filters to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to package_repo_filters maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_package_repo_filters_tenant_id ON package_repo_filters(tenant_id) WHERE tenant_id IS NOT NULL;
Soft delete collides with non-partial UNIQUE
package_repo_filters soft-deletes via deleted_at, but (scheme, matcher) is unique across live AND deleted rows. A user who deletes their account can never sign up again with the same value, and ON CONFLICT upserts will resurrect ghosts.
Fix Replace with a partial unique index scoped to live rows.
DROP CONSTRAINT/INDEX ...; CREATE UNIQUE INDEX package_repo_filters_scheme_matcher_live ON package_repo_filters(scheme, matcher) WHERE deleted_at IS NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan package_repo_versions to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to package_repo_versions maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_package_repo_versions_tenant_id ON package_repo_versions(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan permission_sync_jobs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to permission_sync_jobs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_permission_sync_jobs_tenant_id ON permission_sync_jobs(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan permission_sync_jobs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to permission_sync_jobs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. triggered_by_user_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_permission_sync_jobs_triggered_by_user_id ON permission_sync_jobs(triggered_by_user_id) WHERE triggered_by_user_id IS NOT NULL;
Enum-like column with no CHECK
state is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE permission_sync_jobs ADD CONSTRAINT permission_sync_jobs_state_check CHECK (state IN (...));
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan permissions to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to permissions maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_permissions_tenant_id ON permissions(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan phabricator_repos to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to phabricator_repos maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_phabricator_repos_tenant_id ON phabricator_repos(tenant_id) WHERE tenant_id IS NOT NULL;
Soft delete collides with non-partial UNIQUE
phabricator_repos soft-deletes via deleted_at, but (repo_name) is unique across live AND deleted rows. A user who deletes their account can never sign up again with the same value, and ON CONFLICT upserts will resurrect ghosts.
Fix Replace with a partial unique index scoped to live rows.
DROP CONSTRAINT/INDEX ...; CREATE UNIQUE INDEX phabricator_repos_repo_name_live ON phabricator_repos(repo_name) WHERE deleted_at IS NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on product_subscriptions must scan product_licenses to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to product_licenses maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often product_subscriptions rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_product_licenses_product_subscription_id ON product_licenses(product_subscription_id);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan product_licenses to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to product_licenses maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_product_licenses_tenant_id ON product_licenses(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan product_subscriptions to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to product_subscriptions maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_product_subscriptions_tenant_id ON product_subscriptions(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan product_subscriptions to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to product_subscriptions maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_product_subscriptions_user_id ON product_subscriptions(user_id);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan prompts to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to prompts maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. created_by is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_prompts_created_by ON prompts(created_by) WHERE created_by IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan prompts to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to prompts maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_prompts_tenant_id ON prompts(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan prompts to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to prompts maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. updated_by is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_prompts_updated_by ON prompts(updated_by) WHERE updated_by IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan query_runner_state to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to query_runner_state maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_query_runner_state_tenant_id ON query_runner_state(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan redis_key_value to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to redis_key_value maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_redis_key_value_tenant_id ON redis_key_value(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan registry_extension_releases to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to registry_extension_releases maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_registry_extension_releases_creator_user_id ON registry_extension_releases(creator_user_id);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan registry_extension_releases to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to registry_extension_releases maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_registry_extension_releases_tenant_id ON registry_extension_releases(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on orgs must scan registry_extensions to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to registry_extensions maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often orgs rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. publisher_org_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_registry_extensions_publisher_org_id ON registry_extensions(publisher_org_id) WHERE publisher_org_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan registry_extensions to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to registry_extensions maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. publisher_user_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_registry_extensions_publisher_user_id ON registry_extensions(publisher_user_id) WHERE publisher_user_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan registry_extensions to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to registry_extensions maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_registry_extensions_tenant_id ON registry_extensions(tenant_id) WHERE tenant_id IS NOT NULL;
Soft delete collides with non-partial UNIQUE
registry_extensions soft-deletes via deleted_at, but (uuid) is unique across live AND deleted rows. A user who deletes their account can never sign up again with the same value, and ON CONFLICT upserts will resurrect ghosts.
Fix Replace with a partial unique index scoped to live rows.
DROP CONSTRAINT/INDEX ...; CREATE UNIQUE INDEX registry_extensions_uuid_live ON registry_extensions(uuid) WHERE deleted_at IS NULL;
F059repo tenant_idfk-index Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan repo to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to repo maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_repo_tenant_id ON repo(tenant_id) WHERE tenant_id IS NOT NULL;
F538repo external_service_typeundocumented-enum Enum-like column with no CHECK
external_service_type is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE repo ADD CONSTRAINT repo_external_service_type_check CHECK (external_service_type IN (...));
F582repo external_service_type, external_service_id, external_idsoft-delete-unique Soft delete collides with non-partial UNIQUE
repo soft-deletes via deleted_at, but (external_service_type, external_service_id, external_id) is unique across live AND deleted rows. A user who deletes their account can never sign up again with the same value, and ON CONFLICT upserts will resurrect ghosts.
Fix Replace with a partial unique index scoped to live rows.
DROP CONSTRAINT/INDEX ...; CREATE UNIQUE INDEX repo_external_service_type_external_service_id_external_id_live ON repo(external_service_type, external_service_id, external_id) WHERE deleted_at IS NULL;
F583repo namesoft-delete-unique Soft delete collides with non-partial UNIQUE
repo soft-deletes via deleted_at, but (name) is unique across live AND deleted rows. A user who deletes their account can never sign up again with the same value, and ON CONFLICT upserts will resurrect ghosts.
Fix Replace with a partial unique index scoped to live rows.
DROP CONSTRAINT/INDEX ...; CREATE UNIQUE INDEX repo_name_live ON repo(name) WHERE deleted_at IS NULL;
F591repo external_service_type, external_service_idpolymorphic-reference Polymorphic reference without referential integrity
external_service_id points at different tables depending on external_service_type. No FK can express that, so orphans accumulate silently and every join needs a CASE. Acceptable for append-only audit data; painful anywhere the target must still exist.
Fix Either one nullable FK per target with a CHECK that exactly one is set, or a supertype table that the targets reference.
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan repo_commits_changelists to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to repo_commits_changelists maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_repo_commits_changelists_tenant_id ON repo_commits_changelists(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan repo_embedding_job_stats to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to repo_embedding_job_stats maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_repo_embedding_job_stats_tenant_id ON repo_embedding_job_stats(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan repo_embedding_jobs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to repo_embedding_jobs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_repo_embedding_jobs_tenant_id ON repo_embedding_jobs(tenant_id) WHERE tenant_id IS NOT NULL;
Enum-like column with no CHECK
state is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE repo_embedding_jobs ADD CONSTRAINT repo_embedding_jobs_state_check CHECK (state IN (...));
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan repo_kvps to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to repo_kvps maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_repo_kvps_tenant_id ON repo_kvps(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on repo_paths must scan repo_paths to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to repo_paths maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often repo_paths rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. parent_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_repo_paths_parent_id ON repo_paths(parent_id) WHERE parent_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan repo_paths to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to repo_paths maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_repo_paths_tenant_id ON repo_paths(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan repo_pending_permissions to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to repo_pending_permissions maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_repo_pending_permissions_tenant_id ON repo_pending_permissions(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan repo_permissions to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to repo_permissions maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_repo_permissions_tenant_id ON repo_permissions(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan repo_statistics to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to repo_statistics maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_repo_statistics_tenant_id ON repo_statistics(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on roles must scan role_permissions to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to role_permissions maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often roles rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_role_permissions_role_id ON role_permissions(role_id);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan role_permissions to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to role_permissions maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_role_permissions_tenant_id ON role_permissions(tenant_id) WHERE tenant_id IS NOT NULL;
F240roles tenant_idfk-index Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan roles to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to roles maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_roles_tenant_id ON roles(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan saved_searches to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to saved_searches maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. created_by is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_saved_searches_created_by ON saved_searches(created_by) WHERE created_by IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on orgs must scan saved_searches to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to saved_searches maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often orgs rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. org_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_saved_searches_org_id ON saved_searches(org_id) WHERE org_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan saved_searches to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to saved_searches maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_saved_searches_tenant_id ON saved_searches(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan saved_searches to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to saved_searches maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. updated_by is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_saved_searches_updated_by ON saved_searches(updated_by) WHERE updated_by IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan saved_searches to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to saved_searches maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. user_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_saved_searches_user_id ON saved_searches(user_id) WHERE user_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on search_contexts must scan search_context_default to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to search_context_default maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often search_contexts rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_search_context_default_search_context_id ON search_context_default(search_context_id);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan search_context_default to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to search_context_default maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_search_context_default_tenant_id ON search_context_default(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on search_contexts must scan search_context_repos to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to search_context_repos maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often search_contexts rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_search_context_repos_search_context_id ON search_context_repos(search_context_id);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan search_context_repos to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to search_context_repos maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_search_context_repos_tenant_id ON search_context_repos(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan search_context_stars to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to search_context_stars maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_search_context_stars_tenant_id ON search_context_stars(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan search_context_stars to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to search_context_stars maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_search_context_stars_user_id ON search_context_stars(user_id);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on orgs must scan search_contexts to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to search_contexts maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often orgs rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. namespace_org_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_search_contexts_namespace_org_id ON search_contexts(namespace_org_id) WHERE namespace_org_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan search_contexts to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to search_contexts maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. namespace_user_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_search_contexts_namespace_user_id ON search_contexts(namespace_user_id) WHERE namespace_user_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan search_contexts to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to search_contexts maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_search_contexts_tenant_id ON search_contexts(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan security_event_logs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to security_event_logs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_security_event_logs_tenant_id ON security_event_logs(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan settings to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to settings maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. author_user_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_settings_author_user_id ON settings(author_user_id) WHERE author_user_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan settings to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to settings maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_settings_tenant_id ON settings(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan sub_repo_permissions to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to sub_repo_permissions maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_sub_repo_permissions_tenant_id ON sub_repo_permissions(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan survey_responses to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to survey_responses maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_survey_responses_tenant_id ON survey_responses(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan survey_responses to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to survey_responses maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. user_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_survey_responses_user_id ON survey_responses(user_id) WHERE user_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan syntactic_scip_indexing_jobs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to syntactic_scip_indexing_jobs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_syntactic_scip_indexing_jobs_tenant_id ON syntactic_scip_indexing_jobs(tenant_id) WHERE tenant_id IS NOT NULL;
Enum-like column with no CHECK
state is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE syntactic_scip_indexing_jobs ADD CONSTRAINT syntactic_scip_indexing_jobs_state_check CHECK (state IN (...));
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan syntactic_scip_last_index_scan to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to syntactic_scip_last_index_scan maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_syntactic_scip_last_index_scan_tenant_id ON syntactic_scip_last_index_scan(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan team_members to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to team_members maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_team_members_tenant_id ON team_members(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan team_members to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to team_members maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_team_members_user_id ON team_members(user_id);
F265teams creator_idfk-index Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan teams to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to teams maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. creator_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_teams_creator_id ON teams(creator_id) WHERE creator_id IS NOT NULL;
F266teams parent_team_idfk-index Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on teams must scan teams to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to teams maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often teams rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. parent_team_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_teams_parent_team_id ON teams(parent_team_id) WHERE parent_team_id IS NOT NULL;
F267teams tenant_idfk-index Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan teams to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to teams maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_teams_tenant_id ON teams(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan telemetry_events_export_queue to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to telemetry_events_export_queue maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_telemetry_events_export_queue_tenant_id ON telemetry_events_export_queue(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan temporary_settings to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to temporary_settings maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_temporary_settings_tenant_id ON temporary_settings(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on github_apps must scan user_credentials to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to user_credentials maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often github_apps rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. github_app_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_user_credentials_github_app_id ON user_credentials(github_app_id) WHERE github_app_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan user_credentials to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to user_credentials maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_user_credentials_tenant_id ON user_credentials(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan user_credentials to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to user_credentials maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_user_credentials_user_id ON user_credentials(user_id);
Enum-like column with no CHECK
external_service_type is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE user_credentials ADD CONSTRAINT user_credentials_external_service_type_check CHECK (external_service_type IN (...));
F592user_credentials external_service_type, external_service_idpolymorphic-reference Polymorphic reference without referential integrity
external_service_id points at different tables depending on external_service_type. No FK can express that, so orphans accumulate silently and every join needs a CASE. Acceptable for append-only audit data; painful anywhere the target must still exist.
Fix Either one nullable FK per target with a CHECK that exactly one is set, or a supertype table that the targets reference.
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan user_emails to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to user_emails maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_user_emails_tenant_id ON user_emails(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan user_external_accounts to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to user_external_accounts maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_user_external_accounts_tenant_id ON user_external_accounts(tenant_id) WHERE tenant_id IS NOT NULL;
Enum-like column with no CHECK
service_type is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE user_external_accounts ADD CONSTRAINT user_external_accounts_service_type_check CHECK (service_type IN (...));
Polymorphic reference without referential integrity
service_id points at different tables depending on service_type. No FK can express that, so orphans accumulate silently and every join needs a CASE. Acceptable for append-only audit data; painful anywhere the target must still exist.
Fix Either one nullable FK per target with a CHECK that exactly one is set, or a supertype table that the targets reference.
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan user_onboarding_tour to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to user_onboarding_tour maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_user_onboarding_tour_tenant_id ON user_onboarding_tour(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan user_onboarding_tour to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to user_onboarding_tour maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. updated_by is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_user_onboarding_tour_updated_by ON user_onboarding_tour(updated_by) WHERE updated_by IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan user_pending_permissions to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to user_pending_permissions maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_user_pending_permissions_tenant_id ON user_pending_permissions(tenant_id) WHERE tenant_id IS NOT NULL;
Enum-like column with no CHECK
object_type is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE user_pending_permissions ADD CONSTRAINT user_pending_permissions_object_type_check CHECK (object_type IN (...));
Enum-like column with no CHECK
service_type is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE user_pending_permissions ADD CONSTRAINT user_pending_permissions_service_type_check CHECK (service_type IN (...));
Polymorphic reference without referential integrity
service_id points at different tables depending on service_type. No FK can express that, so orphans accumulate silently and every join needs a CASE. Acceptable for append-only audit data; painful anywhere the target must still exist.
Fix Either one nullable FK per target with a CHECK that exactly one is set, or a supertype table that the targets reference.
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan user_permissions to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to user_permissions maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_user_permissions_tenant_id ON user_permissions(tenant_id) WHERE tenant_id IS NOT NULL;
Enum-like column with no CHECK
object_type is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE user_permissions ADD CONSTRAINT user_permissions_object_type_check CHECK (object_type IN (...));
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on repo must scan user_public_repos to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to user_public_repos maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often repo rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_user_public_repos_repo_id ON user_public_repos(repo_id);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan user_public_repos to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to user_public_repos maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_user_public_repos_tenant_id ON user_public_repos(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan user_repo_permissions to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to user_repo_permissions maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_user_repo_permissions_tenant_id ON user_repo_permissions(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on roles must scan user_roles to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to user_roles maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often roles rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add an index on the FK column(s).
CREATE INDEX CONCURRENTLY idx_user_roles_role_id ON user_roles(role_id);
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan user_roles to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to user_roles maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_user_roles_tenant_id ON user_roles(tenant_id) WHERE tenant_id IS NOT NULL;
F221users tenant_idfk-index Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan users to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to users maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_users_tenant_id ON users(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan vulnerabilities to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to vulnerabilities maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_vulnerabilities_tenant_id ON vulnerabilities(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan vulnerability_affected_packages to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to vulnerability_affected_packages maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_vulnerability_affected_packages_tenant_id ON vulnerability_affected_packages(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan vulnerability_affected_symbols to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to vulnerability_affected_symbols maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_vulnerability_affected_symbols_tenant_id ON vulnerability_affected_symbols(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan vulnerability_matches to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to vulnerability_matches maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_vulnerability_matches_tenant_id ON vulnerability_matches(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan webhook_logs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to webhook_logs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_webhook_logs_tenant_id ON webhook_logs(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on webhooks must scan webhook_logs to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to webhook_logs maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often webhooks rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. webhook_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_webhook_logs_webhook_id ON webhook_logs(webhook_id) WHERE webhook_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan webhooks to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to webhooks maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. created_by_user_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_webhooks_created_by_user_id ON webhooks(created_by_user_id) WHERE created_by_user_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan webhooks to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to webhooks maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_webhooks_tenant_id ON webhooks(tenant_id) WHERE tenant_id IS NOT NULL;
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on users must scan webhooks to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to webhooks maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often users rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. updated_by_user_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_webhooks_updated_by_user_id ON webhooks(updated_by_user_id) WHERE updated_by_user_id IS NOT NULL;
F579webhooks code_host_kindundocumented-enum Enum-like column with no CHECK
code_host_kind is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code. The comment lists Kind of an external service for which webhooks are registered., i.e. the values are known.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE webhooks ADD CONSTRAINT webhooks_code_host_kind_check CHECK (code_host_kind IN (...));
Foreign key without index
PostgreSQL does not index FK columns automatically. Every DELETE/UPDATE on tenants must scan zoekt_repos to check the constraint, and every join from the parent side is a sequential scan. The scan cost grows with the child table forever, so this gets worse on its own.
An index is not free. Every write to zoekt_repos maintains it — insert, update and delete alike — and it takes disk. Weigh that against how often tenants rows are actually deleted or re-keyed — if the answer is never, the scan never happens and the index only costs.
Fix Add a partial index, scoped to the rows that have a value. tenant_id is nullable, so the NULL rows are not indexed at all and the write cost is a fraction of a full index, while the constraint check still uses it.
CREATE INDEX CONCURRENTLY idx_zoekt_repos_tenant_id ON zoekt_repos(tenant_id) WHERE tenant_id IS NOT NULL;
Enum-like column with no CHECK
index_status is a short string that clearly takes a fixed set of values, but the database accepts anything. Typos become new states, and nobody can list the legal values without reading application code.
Fix Add a CHECK constraint (cheap, easy to evolve) or a lookup table if values need metadata. Avoid native ENUM types unless the set is truly frozen.
ALTER TABLE zoekt_repos ADD CONSTRAINT zoekt_repos_index_status_check CHECK (index_status IN (...));
Notes (294)
Nullable foreign key
access_requests.decision_by_user_id may be NULL, so the relationship to users is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
access_requests.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Either/or foreign keys without a CHECK
access_requests has several nullable FKs (decision_by_user_id, tenant_id) that look like an exclusive arc — a row should point at exactly one of them. Nothing stops zero or both.
Fix Add a CHECK that exactly one is non-null, or restructure with a supertype.
ALTER TABLE access_requests ADD CONSTRAINT access_requests_one_target CHECK ((decision_by_user_id IS NOT NULL)::int + (tenant_id IS NOT NULL)::int = 1);
Nullable foreign key
access_tokens.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
aggregated_user_statistics.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
assigned_owners.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
assigned_owners.who_assigned_user_id may be NULL, so the relationship to users is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Either/or foreign keys without a CHECK
assigned_owners has several nullable FKs (tenant_id, who_assigned_user_id) that look like an exclusive arc — a row should point at exactly one of them. Nothing stops zero or both.
Fix Add a CHECK that exactly one is non-null, or restructure with a supertype.
ALTER TABLE assigned_owners ADD CONSTRAINT assigned_owners_one_target CHECK ((tenant_id IS NOT NULL)::int + (who_assigned_user_id IS NOT NULL)::int = 1);
TIMESTAMP without time zone
assigned_at stores wall-clock time with no zone. It reads back differently depending on the session's TimeZone, and DST transitions produce ambiguous values.
Fix Use TIMESTAMPTZ.
ALTER TABLE assigned_owners ALTER COLUMN assigned_at TYPE timestamptz;
Nullable foreign key
assigned_teams.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
assigned_teams.who_assigned_team_id may be NULL, so the relationship to users is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Either/or foreign keys without a CHECK
assigned_teams has several nullable FKs (tenant_id, who_assigned_team_id) that look like an exclusive arc — a row should point at exactly one of them. Nothing stops zero or both.
Fix Add a CHECK that exactly one is non-null, or restructure with a supertype.
ALTER TABLE assigned_teams ADD CONSTRAINT assigned_teams_one_target CHECK ((tenant_id IS NOT NULL)::int + (who_assigned_team_id IS NOT NULL)::int = 1);
TIMESTAMP without time zone
assigned_at stores wall-clock time with no zone. It reads back differently depending on the session's TimeZone, and DST transitions produce ambiguous values.
Fix Use TIMESTAMPTZ.
ALTER TABLE assigned_teams ALTER COLUMN assigned_at TYPE timestamptz;
Nullable foreign key
batch_changes.creator_id may be NULL, so the relationship to users is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
batch_changes.last_applier_id may be NULL, so the relationship to users is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
batch_changes.namespace_org_id may be NULL, so the relationship to orgs is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
batch_changes.namespace_user_id may be NULL, so the relationship to users is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
batch_changes.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
F598batch_changes creator_id, last_applier_id, namespace_org_id, namespace_user_id, tenant_idexclusive-arc Either/or foreign keys without a CHECK
batch_changes has several nullable FKs (creator_id, last_applier_id, namespace_org_id, namespace_user_id, tenant_id) that look like an exclusive arc — a row should point at exactly one of them. Nothing stops zero or both.
Fix Add a CHECK that exactly one is non-null, or restructure with a supertype.
ALTER TABLE batch_changes ADD CONSTRAINT batch_changes_one_target CHECK ((creator_id IS NOT NULL)::int + (last_applier_id IS NOT NULL)::int + (namespace_org_id IS NOT NULL)::int + (namespace_user_id IS NOT NULL)::int + (tenant_id IS NOT NULL)::int = 1);
Nullable foreign key
batch_changes_site_credentials.github_app_id may be NULL, so the relationship to github_apps is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
batch_changes_site_credentials.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Either/or foreign keys without a CHECK
batch_changes_site_credentials has several nullable FKs (github_app_id, tenant_id) that look like an exclusive arc — a row should point at exactly one of them. Nothing stops zero or both.
Fix Add a CHECK that exactly one is non-null, or restructure with a supertype.
ALTER TABLE batch_changes_site_credentials ADD CONSTRAINT batch_changes_site_credentials_one_target CHECK ((github_app_id IS NOT NULL)::int + (tenant_id IS NOT NULL)::int = 1);
Nullable foreign key
batch_spec_execution_cache_entries.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
batch_spec_resolution_jobs.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
batch_spec_workspace_execution_jobs.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
batch_spec_workspace_execution_last_dequeues.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
batch_spec_workspace_files.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
batch_spec_workspaces.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
batch_specs.batch_change_id may be NULL, so the relationship to batch_changes is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
batch_specs.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
batch_specs.user_id may be NULL, so the relationship to users is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
F600batch_specs batch_change_id, tenant_id, user_idexclusive-arc Either/or foreign keys without a CHECK
batch_specs has several nullable FKs (batch_change_id, tenant_id, user_id) that look like an exclusive arc — a row should point at exactly one of them. Nothing stops zero or both.
Fix Add a CHECK that exactly one is non-null, or restructure with a supertype.
ALTER TABLE batch_specs ADD CONSTRAINT batch_specs_one_target CHECK ((batch_change_id IS NOT NULL)::int + (tenant_id IS NOT NULL)::int + (user_id IS NOT NULL)::int = 1);
Nullable foreign key
cached_available_indexers.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
changeset_events.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
changeset_jobs.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
changeset_specs.batch_spec_id may be NULL, so the relationship to batch_specs is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
changeset_specs.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
changeset_specs.user_id may be NULL, so the relationship to users is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Either/or foreign keys without a CHECK
changeset_specs has several nullable FKs (batch_spec_id, tenant_id, user_id) that look like an exclusive arc — a row should point at exactly one of them. Nothing stops zero or both.
Fix Add a CHECK that exactly one is non-null, or restructure with a supertype.
ALTER TABLE changeset_specs ADD CONSTRAINT changeset_specs_one_target CHECK ((batch_spec_id IS NOT NULL)::int + (tenant_id IS NOT NULL)::int + (user_id IS NOT NULL)::int = 1);
Nullable foreign key
changesets.current_spec_id may be NULL, so the relationship to changeset_specs is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
F322changesets owned_by_batch_change_idfk-nullable Nullable foreign key
changesets.owned_by_batch_change_id may be NULL, so the relationship to batch_changes is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
changesets.previous_spec_id may be NULL, so the relationship to changeset_specs is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
changesets.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
F602changesets current_spec_id, owned_by_batch_change_id, previous_spec_id, tenant_idexclusive-arc Either/or foreign keys without a CHECK
changesets has several nullable FKs (current_spec_id, owned_by_batch_change_id, previous_spec_id, tenant_id) that look like an exclusive arc — a row should point at exactly one of them. Nothing stops zero or both.
Fix Add a CHECK that exactly one is non-null, or restructure with a supertype.
ALTER TABLE changesets ADD CONSTRAINT changesets_one_target CHECK ((current_spec_id IS NOT NULL)::int + (owned_by_batch_change_id IS NOT NULL)::int + (previous_spec_id IS NOT NULL)::int + (tenant_id IS NOT NULL)::int = 1);
Wide table (45 columns)
Tables this wide usually hide several entities (or a JSON column that wants to be one). Every row update rewrites the whole tuple; TOAST kicks in; indexes bloat.
Fix Look for column groups that always change together and split them out.
Nullable foreign key
cm_action_jobs.email may be NULL, so the relationship to cm_emails is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
cm_action_jobs.slack_webhook may be NULL, so the relationship to cm_slack_webhooks is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
cm_action_jobs.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
cm_action_jobs.trigger_event may be NULL, so the relationship to cm_trigger_jobs is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
cm_action_jobs.webhook may be NULL, so the relationship to cm_webhooks is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
F603cm_action_jobs email, slack_webhook, tenant_id, trigger_event, webhookexclusive-arc Either/or foreign keys without a CHECK
cm_action_jobs has several nullable FKs (email, slack_webhook, tenant_id, trigger_event, webhook) that look like an exclusive arc — a row should point at exactly one of them. Nothing stops zero or both.
Fix Add a CHECK that exactly one is non-null, or restructure with a supertype.
ALTER TABLE cm_action_jobs ADD CONSTRAINT cm_action_jobs_one_target CHECK ((email IS NOT NULL)::int + (slack_webhook IS NOT NULL)::int + (tenant_id IS NOT NULL)::int + (trigger_event IS NOT NULL)::int + (webhook IS NOT NULL)::int = 1);
Nullable foreign key
cm_emails.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
cm_last_searched.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
cm_monitors.namespace_org_id may be NULL, so the relationship to orgs is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
cm_monitors.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
F604cm_monitors namespace_org_id, tenant_idexclusive-arc Either/or foreign keys without a CHECK
cm_monitors has several nullable FKs (namespace_org_id, tenant_id) that look like an exclusive arc — a row should point at exactly one of them. Nothing stops zero or both.
Fix Add a CHECK that exactly one is non-null, or restructure with a supertype.
ALTER TABLE cm_monitors ADD CONSTRAINT cm_monitors_one_target CHECK ((namespace_org_id IS NOT NULL)::int + (tenant_id IS NOT NULL)::int = 1);
Nullable foreign key
cm_queries.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
cm_recipients.namespace_org_id may be NULL, so the relationship to orgs is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
cm_recipients.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
cm_recipients.namespace_user_id may be NULL, so the relationship to users is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
F605cm_recipients namespace_org_id, tenant_id, namespace_user_idexclusive-arc Either/or foreign keys without a CHECK
cm_recipients has several nullable FKs (namespace_org_id, tenant_id, namespace_user_id) that look like an exclusive arc — a row should point at exactly one of them. Nothing stops zero or both.
Fix Add a CHECK that exactly one is non-null, or restructure with a supertype.
ALTER TABLE cm_recipients ADD CONSTRAINT cm_recipients_one_target CHECK ((namespace_org_id IS NOT NULL)::int + (tenant_id IS NOT NULL)::int + (namespace_user_id IS NOT NULL)::int = 1);
Nullable foreign key
cm_slack_webhooks.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
cm_trigger_jobs.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
cm_webhooks.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
code_hosts.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
codeintel_autoindex_queue.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
codeintel_autoindexing_exceptions.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
codeintel_commit_dates.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
codeintel_inference_scripts.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
codeintel_initial_path_ranks.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
codeintel_initial_path_ranks_processed.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
codeintel_langugage_support_requests.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
codeintel_path_ranks.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
codeintel_ranking_definitions.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
codeintel_ranking_exports.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
codeintel_ranking_exports.upload_id may be NULL, so the relationship to lsif_uploads is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Either/or foreign keys without a CHECK
codeintel_ranking_exports has several nullable FKs (tenant_id, upload_id) that look like an exclusive arc — a row should point at exactly one of them. Nothing stops zero or both.
Fix Add a CHECK that exactly one is non-null, or restructure with a supertype.
ALTER TABLE codeintel_ranking_exports ADD CONSTRAINT codeintel_ranking_exports_one_target CHECK ((tenant_id IS NOT NULL)::int + (upload_id IS NOT NULL)::int = 1);
Nullable foreign key
codeintel_ranking_graph_keys.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
codeintel_ranking_path_counts_inputs.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
codeintel_ranking_progress.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
codeintel_ranking_references.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
codeintel_ranking_references_processed.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
codeowners.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
codeowners_individual_stats.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
TIMESTAMP without time zone
updated_at stores wall-clock time with no zone. It reads back differently depending on the session's TimeZone, and DST transitions produce ambiguous values.
Fix Use TIMESTAMPTZ.
ALTER TABLE codeowners_individual_stats ALTER COLUMN updated_at TYPE timestamptz;
Nullable foreign key
codeowners_owners.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
commit_authors.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
configuration_policies_audit_logs.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
context_detection_embedding_jobs.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Isolated table
critical_and_site_config references nothing and nothing references it. Either it is a staging/log table (fine, say so), or it is dead, or it is the seed of a second data model growing beside the first.
Fix Document its purpose or drop it.
Nullable foreign key
discussion_comments.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
discussion_mail_reply_tokens.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
discussion_threads.target_repo_id may be NULL, so the relationship to discussion_threads_target_repo is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
discussion_threads.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Either/or foreign keys without a CHECK
discussion_threads has several nullable FKs (target_repo_id, tenant_id) that look like an exclusive arc — a row should point at exactly one of them. Nothing stops zero or both.
Fix Add a CHECK that exactly one is non-null, or restructure with a supertype.
ALTER TABLE discussion_threads ADD CONSTRAINT discussion_threads_one_target CHECK ((target_repo_id IS NOT NULL)::int + (tenant_id IS NOT NULL)::int = 1);
Nullable foreign key
discussion_threads_target_repo.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
event_logs.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
event_logs_export_allowlist.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
event_logs_scrape_state.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
event_logs_scrape_state_own.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
executor_heartbeats.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
executor_job_tokens.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
executor_secret_access_logs.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
executor_secret_access_logs.user_id may be NULL, so the relationship to users is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Either/or foreign keys without a CHECK
executor_secret_access_logs has several nullable FKs (tenant_id, user_id) that look like an exclusive arc — a row should point at exactly one of them. Nothing stops zero or both.
Fix Add a CHECK that exactly one is non-null, or restructure with a supertype.
ALTER TABLE executor_secret_access_logs ADD CONSTRAINT executor_secret_access_logs_one_target CHECK ((tenant_id IS NOT NULL)::int + (user_id IS NOT NULL)::int = 1);
Nullable foreign key
executor_secrets.creator_id may be NULL, so the relationship to users is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
executor_secrets.namespace_org_id may be NULL, so the relationship to orgs is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
executor_secrets.namespace_user_id may be NULL, so the relationship to users is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
executor_secrets.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
F609executor_secrets creator_id, namespace_org_id, namespace_user_id, tenant_idexclusive-arc Either/or foreign keys without a CHECK
executor_secrets has several nullable FKs (creator_id, namespace_org_id, namespace_user_id, tenant_id) that look like an exclusive arc — a row should point at exactly one of them. Nothing stops zero or both.
Fix Add a CHECK that exactly one is non-null, or restructure with a supertype.
ALTER TABLE executor_secrets ADD CONSTRAINT executor_secrets_one_target CHECK ((creator_id IS NOT NULL)::int + (namespace_org_id IS NOT NULL)::int + (namespace_user_id IS NOT NULL)::int + (tenant_id IS NOT NULL)::int = 1);
Nullable foreign key
exhaustive_search_jobs.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
exhaustive_search_repo_jobs.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
exhaustive_search_repo_revision_jobs.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
explicit_permissions_bitbucket_projects_jobs.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
external_service_repos.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
external_service_sync_jobs.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
external_services.code_host_id may be NULL, so the relationship to code_hosts is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
external_services.creator_id may be NULL, so the relationship to users is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
external_services.last_updater_id may be NULL, so the relationship to users is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
external_services.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
F610external_services code_host_id, creator_id, last_updater_id, tenant_idexclusive-arc Either/or foreign keys without a CHECK
external_services has several nullable FKs (code_host_id, creator_id, last_updater_id, tenant_id) that look like an exclusive arc — a row should point at exactly one of them. Nothing stops zero or both.
Fix Add a CHECK that exactly one is non-null, or restructure with a supertype.
ALTER TABLE external_services ADD CONSTRAINT external_services_one_target CHECK ((code_host_id IS NOT NULL)::int + (creator_id IS NOT NULL)::int + (last_updater_id IS NOT NULL)::int + (tenant_id IS NOT NULL)::int = 1);
Nullable foreign key
feature_flag_overrides.namespace_org_id may be NULL, so the relationship to orgs is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
feature_flag_overrides.namespace_user_id may be NULL, so the relationship to users is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
feature_flag_overrides.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Either/or foreign keys without a CHECK
feature_flag_overrides has several nullable FKs (namespace_org_id, namespace_user_id, tenant_id) that look like an exclusive arc — a row should point at exactly one of them. Nothing stops zero or both.
Fix Add a CHECK that exactly one is non-null, or restructure with a supertype.
ALTER TABLE feature_flag_overrides ADD CONSTRAINT feature_flag_overrides_one_target CHECK ((namespace_org_id IS NOT NULL)::int + (namespace_user_id IS NOT NULL)::int + (tenant_id IS NOT NULL)::int = 1);
Nullable foreign key
feature_flags.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
github_app_installs.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
github_apps.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
github_apps.webhook_id may be NULL, so the relationship to webhooks is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Either/or foreign keys without a CHECK
github_apps has several nullable FKs (tenant_id, webhook_id) that look like an exclusive arc — a row should point at exactly one of them. Nothing stops zero or both.
Fix Add a CHECK that exactly one is non-null, or restructure with a supertype.
ALTER TABLE github_apps ADD CONSTRAINT github_apps_one_target CHECK ((tenant_id IS NOT NULL)::int + (webhook_id IS NOT NULL)::int = 1);
Nullable foreign key
gitserver_relocator_jobs.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
gitserver_repos.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
gitserver_repos_statistics.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
gitserver_repos_sync_output.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
global_state.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
insights_query_runner_jobs.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
insights_query_runner_jobs_dependencies.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
TIMESTAMP without time zone
recording_time stores wall-clock time with no zone. It reads back differently depending on the session's TimeZone, and DST transitions produce ambiguous values.
Fix Use TIMESTAMPTZ.
ALTER TABLE insights_query_runner_jobs_dependencies ALTER COLUMN recording_time TYPE timestamptz;
Nullable foreign key
insights_settings_migration_jobs.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
TIMESTAMP without time zone
completed_at stores wall-clock time with no zone. It reads back differently depending on the session's TimeZone, and DST transitions produce ambiguous values.
Fix Use TIMESTAMPTZ.
ALTER TABLE insights_settings_migration_jobs ALTER COLUMN completed_at TYPE timestamptz;
Isolated table
lsif_configuration_policies references nothing and nothing references it. Either it is a staging/log table (fine, say so), or it is dead, or it is the seed of a second data model growing beside the first.
Fix Document its purpose or drop it.
Isolated table
lsif_configuration_policies_repository_pattern_lookup references nothing and nothing references it. Either it is a staging/log table (fine, say so), or it is dead, or it is the seed of a second data model growing beside the first.
Fix Document its purpose or drop it.
Nullable foreign key
lsif_dependency_indexing_jobs.upload_id may be NULL, so the relationship to lsif_uploads is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
lsif_dependency_syncing_jobs.upload_id may be NULL, so the relationship to lsif_uploads is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Isolated table
lsif_dirty_repositories references nothing and nothing references it. Either it is a staging/log table (fine, say so), or it is dead, or it is the seed of a second data model growing beside the first.
Fix Document its purpose or drop it.
Isolated table
lsif_indexes references nothing and nothing references it. Either it is a staging/log table (fine, say so), or it is dead, or it is the seed of a second data model growing beside the first.
Fix Document its purpose or drop it.
Isolated table
lsif_last_index_scan references nothing and nothing references it. Either it is a staging/log table (fine, say so), or it is dead, or it is the seed of a second data model growing beside the first.
Fix Document its purpose or drop it.
Isolated table
lsif_last_retention_scan references nothing and nothing references it. Either it is a staging/log table (fine, say so), or it is dead, or it is the seed of a second data model growing beside the first.
Fix Document its purpose or drop it.
Isolated table
lsif_nearest_uploads references nothing and nothing references it. Either it is a staging/log table (fine, say so), or it is dead, or it is the seed of a second data model growing beside the first.
Fix Document its purpose or drop it.
Isolated table
lsif_nearest_uploads_links references nothing and nothing references it. Either it is a staging/log table (fine, say so), or it is dead, or it is the seed of a second data model growing beside the first.
Fix Document its purpose or drop it.
Wide table (35 columns)
Tables this wide usually hide several entities (or a JSON column that wants to be one). Every row update rewrites the whole tuple; TOAST kicks in; indexes bloat.
Fix Look for column groups that always change together and split them out.
Isolated table
lsif_uploads_audit_logs references nothing and nothing references it. Either it is a staging/log table (fine, say so), or it is dead, or it is the seed of a second data model growing beside the first.
Fix Document its purpose or drop it.
Isolated table
lsif_uploads_visible_at_tip references nothing and nothing references it. Either it is a staging/log table (fine, say so), or it is dead, or it is the seed of a second data model growing beside the first.
Fix Document its purpose or drop it.
TIMESTAMP without time zone
last_scanned_at stores wall-clock time with no zone. It reads back differently depending on the session's TimeZone, and DST transitions produce ambiguous values.
Fix Use TIMESTAMPTZ.
ALTER TABLE lsif_uploads_vulnerability_scan ALTER COLUMN last_scanned_at TYPE timestamptz;
F412names org_idfk-nullable Nullable foreign key
names.org_id may be NULL, so the relationship to orgs is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
F413names team_idfk-nullable Nullable foreign key
names.team_id may be NULL, so the relationship to teams is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
F414names tenant_idfk-nullable Nullable foreign key
names.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
F415names user_idfk-nullable Nullable foreign key
names.user_id may be NULL, so the relationship to users is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
F613names org_id, team_id, tenant_id, user_idexclusive-arc Either/or foreign keys without a CHECK
names has several nullable FKs (org_id, team_id, tenant_id, user_id) that look like an exclusive arc — a row should point at exactly one of them. Nothing stops zero or both.
Fix Add a CHECK that exactly one is non-null, or restructure with a supertype.
ALTER TABLE names ADD CONSTRAINT names_one_target CHECK ((org_id IS NOT NULL)::int + (team_id IS NOT NULL)::int + (tenant_id IS NOT NULL)::int + (user_id IS NOT NULL)::int = 1);
Nullable foreign key
namespace_permissions.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
notebook_stars.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
notebooks.creator_user_id may be NULL, so the relationship to users is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
notebooks.namespace_org_id may be NULL, so the relationship to orgs is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
notebooks.namespace_user_id may be NULL, so the relationship to users is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
notebooks.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
notebooks.updater_user_id may be NULL, so the relationship to users is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
F614notebooks creator_user_id, namespace_org_id, namespace_user_id, tenant_id, updater_user_idexclusive-arc Either/or foreign keys without a CHECK
notebooks has several nullable FKs (creator_user_id, namespace_org_id, namespace_user_id, tenant_id, updater_user_id) that look like an exclusive arc — a row should point at exactly one of them. Nothing stops zero or both.
Fix Add a CHECK that exactly one is non-null, or restructure with a supertype.
ALTER TABLE notebooks ADD CONSTRAINT notebooks_one_target CHECK ((creator_user_id IS NOT NULL)::int + (namespace_org_id IS NOT NULL)::int + (namespace_user_id IS NOT NULL)::int + (tenant_id IS NOT NULL)::int + (updater_user_id IS NOT NULL)::int = 1);
Nullable foreign key
org_invitations.recipient_user_id may be NULL, so the relationship to users is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
org_invitations.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Either/or foreign keys without a CHECK
org_invitations has several nullable FKs (recipient_user_id, tenant_id) that look like an exclusive arc — a row should point at exactly one of them. Nothing stops zero or both.
Fix Add a CHECK that exactly one is non-null, or restructure with a supertype.
ALTER TABLE org_invitations ADD CONSTRAINT org_invitations_one_target CHECK ((recipient_user_id IS NOT NULL)::int + (tenant_id IS NOT NULL)::int = 1);
Nullable foreign key
org_members.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
org_stats.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
F427orgs tenant_idfk-nullable Nullable foreign key
orgs.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
orgs_open_beta_stats.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
out_of_band_migrations.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
out_of_band_migrations_errors.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
outbound_webhook_event_types.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
outbound_webhook_jobs.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
outbound_webhook_logs.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
outbound_webhooks.created_by may be NULL, so the relationship to users is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
outbound_webhooks.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
outbound_webhooks.updated_by may be NULL, so the relationship to users is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
own_aggregate_recent_contribution.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
own_aggregate_recent_view.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
own_background_jobs.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
own_signal_configurations.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
own_signal_recent_contribution.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
TIMESTAMP without time zone
commit_timestamp stores wall-clock time with no zone. It reads back differently depending on the session's TimeZone, and DST transitions produce ambiguous values.
Fix Use TIMESTAMPTZ.
ALTER TABLE own_signal_recent_contribution ALTER COLUMN commit_timestamp TYPE timestamptz;
Nullable foreign key
ownership_path_stats.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
TIMESTAMP without time zone
last_updated_at stores wall-clock time with no zone. It reads back differently depending on the session's TimeZone, and DST transitions produce ambiguous values.
Fix Use TIMESTAMPTZ.
ALTER TABLE ownership_path_stats ALTER COLUMN last_updated_at TYPE timestamptz;
Nullable foreign key
package_repo_filters.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
package_repo_versions.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
permission_sync_jobs.repository_id may be NULL, so the relationship to repo is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
permission_sync_jobs.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
permission_sync_jobs.triggered_by_user_id may be NULL, so the relationship to users is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
permission_sync_jobs.user_id may be NULL, so the relationship to users is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Either/or foreign keys without a CHECK
permission_sync_jobs has several nullable FKs (repository_id, tenant_id, triggered_by_user_id, user_id) that look like an exclusive arc — a row should point at exactly one of them. Nothing stops zero or both.
Fix Add a CHECK that exactly one is non-null, or restructure with a supertype.
ALTER TABLE permission_sync_jobs ADD CONSTRAINT permission_sync_jobs_one_target CHECK ((repository_id IS NOT NULL)::int + (tenant_id IS NOT NULL)::int + (triggered_by_user_id IS NOT NULL)::int + (user_id IS NOT NULL)::int = 1);
Nullable foreign key
permissions.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
phabricator_repos.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
product_licenses.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
product_subscriptions.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
prompts.created_by may be NULL, so the relationship to users is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
F454prompts owner_org_idfk-nullable Nullable foreign key
prompts.owner_org_id may be NULL, so the relationship to orgs is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
F455prompts owner_user_idfk-nullable Nullable foreign key
prompts.owner_user_id may be NULL, so the relationship to users is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
prompts.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
prompts.updated_by may be NULL, so the relationship to users is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
F617prompts owner_org_id, owner_user_id, tenant_idexclusive-arc Either/or foreign keys without a CHECK
prompts has several nullable FKs (owner_org_id, owner_user_id, tenant_id) that look like an exclusive arc — a row should point at exactly one of them. Nothing stops zero or both.
Fix Add a CHECK that exactly one is non-null, or restructure with a supertype.
ALTER TABLE prompts ADD CONSTRAINT prompts_one_target CHECK ((owner_org_id IS NOT NULL)::int + (owner_user_id IS NOT NULL)::int + (tenant_id IS NOT NULL)::int = 1);
Nullable foreign key
query_runner_state.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
redis_key_value.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
registry_extension_releases.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
registry_extensions.publisher_org_id may be NULL, so the relationship to orgs is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
registry_extensions.publisher_user_id may be NULL, so the relationship to users is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
registry_extensions.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Either/or foreign keys without a CHECK
registry_extensions has several nullable FKs (publisher_org_id, publisher_user_id, tenant_id) that look like an exclusive arc — a row should point at exactly one of them. Nothing stops zero or both.
Fix Add a CHECK that exactly one is non-null, or restructure with a supertype.
ALTER TABLE registry_extensions ADD CONSTRAINT registry_extensions_one_target CHECK ((publisher_org_id IS NOT NULL)::int + (publisher_user_id IS NOT NULL)::int + (tenant_id IS NOT NULL)::int = 1);
F325repo tenant_idfk-nullable Nullable foreign key
repo.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Hub table: referenced by 22 tables
Any change to its key, its delete semantics, or its partitioning touches every dependent. Migrations on hub tables need the longest lock windows and the most careful rollout.
Fix Treat schema changes here as breaking changes with a written rollout plan.
Nullable foreign key
repo_commits_changelists.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
repo_embedding_job_stats.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
repo_embedding_jobs.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
repo_kvps.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
repo_paths.parent_id may be NULL, so the relationship to repo_paths is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
repo_paths.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Either/or foreign keys without a CHECK
repo_paths has several nullable FKs (parent_id, tenant_id) that look like an exclusive arc — a row should point at exactly one of them. Nothing stops zero or both.
Fix Add a CHECK that exactly one is non-null, or restructure with a supertype.
ALTER TABLE repo_paths ADD CONSTRAINT repo_paths_one_target CHECK ((parent_id IS NOT NULL)::int + (tenant_id IS NOT NULL)::int = 1);
F637repo_paths tree_files_counts_updated_attimestamp-tz TIMESTAMP without time zone
tree_files_counts_updated_at stores wall-clock time with no zone. It reads back differently depending on the session's TimeZone, and DST transitions produce ambiguous values.
Fix Use TIMESTAMPTZ.
ALTER TABLE repo_paths ALTER COLUMN tree_files_counts_updated_at TYPE timestamptz;
Nullable foreign key
repo_pending_permissions.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
repo_permissions.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
repo_statistics.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
role_permissions.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
F475roles tenant_idfk-nullable Nullable foreign key
roles.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
saved_searches.created_by may be NULL, so the relationship to users is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
saved_searches.org_id may be NULL, so the relationship to orgs is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
saved_searches.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
saved_searches.updated_by may be NULL, so the relationship to users is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
saved_searches.user_id may be NULL, so the relationship to users is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Either/or foreign keys without a CHECK
saved_searches has several nullable FKs (org_id, tenant_id, user_id) that look like an exclusive arc — a row should point at exactly one of them. Nothing stops zero or both.
Fix Add a CHECK that exactly one is non-null, or restructure with a supertype.
ALTER TABLE saved_searches ADD CONSTRAINT saved_searches_one_target CHECK ((org_id IS NOT NULL)::int + (tenant_id IS NOT NULL)::int + (user_id IS NOT NULL)::int = 1);
Nullable foreign key
search_context_default.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
search_context_repos.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
search_context_stars.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
search_contexts.namespace_org_id may be NULL, so the relationship to orgs is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
search_contexts.namespace_user_id may be NULL, so the relationship to users is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
search_contexts.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
F621search_contexts namespace_org_id, namespace_user_id, tenant_idexclusive-arc Either/or foreign keys without a CHECK
search_contexts has several nullable FKs (namespace_org_id, namespace_user_id, tenant_id) that look like an exclusive arc — a row should point at exactly one of them. Nothing stops zero or both.
Fix Add a CHECK that exactly one is non-null, or restructure with a supertype.
ALTER TABLE search_contexts ADD CONSTRAINT search_contexts_one_target CHECK ((namespace_org_id IS NOT NULL)::int + (namespace_user_id IS NOT NULL)::int + (tenant_id IS NOT NULL)::int = 1);
Nullable foreign key
security_event_logs.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
settings.author_user_id may be NULL, so the relationship to users is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
settings.org_id may be NULL, so the relationship to orgs is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
settings.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
settings.user_id may be NULL, so the relationship to users is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
F622settings author_user_id, org_id, tenant_id, user_idexclusive-arc Either/or foreign keys without a CHECK
settings has several nullable FKs (author_user_id, org_id, tenant_id, user_id) that look like an exclusive arc — a row should point at exactly one of them. Nothing stops zero or both.
Fix Add a CHECK that exactly one is non-null, or restructure with a supertype.
ALTER TABLE settings ADD CONSTRAINT settings_one_target CHECK ((author_user_id IS NOT NULL)::int + (org_id IS NOT NULL)::int + (tenant_id IS NOT NULL)::int + (user_id IS NOT NULL)::int = 1);
Nullable foreign key
sub_repo_permissions.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
survey_responses.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
survey_responses.user_id may be NULL, so the relationship to users is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Either/or foreign keys without a CHECK
survey_responses has several nullable FKs (tenant_id, user_id) that look like an exclusive arc — a row should point at exactly one of them. Nothing stops zero or both.
Fix Add a CHECK that exactly one is non-null, or restructure with a supertype.
ALTER TABLE survey_responses ADD CONSTRAINT survey_responses_one_target CHECK ((tenant_id IS NOT NULL)::int + (user_id IS NOT NULL)::int = 1);
Nullable foreign key
syntactic_scip_indexing_jobs.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
syntactic_scip_last_index_scan.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
team_members.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
F498teams creator_idfk-nullable Nullable foreign key
teams.creator_id may be NULL, so the relationship to users is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
F499teams parent_team_idfk-nullable Nullable foreign key
teams.parent_team_id may be NULL, so the relationship to teams is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
F500teams tenant_idfk-nullable Nullable foreign key
teams.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
F624teams creator_id, parent_team_id, tenant_idexclusive-arc Either/or foreign keys without a CHECK
teams has several nullable FKs (creator_id, parent_team_id, tenant_id) that look like an exclusive arc — a row should point at exactly one of them. Nothing stops zero or both.
Fix Add a CHECK that exactly one is non-null, or restructure with a supertype.
ALTER TABLE teams ADD CONSTRAINT teams_one_target CHECK ((creator_id IS NOT NULL)::int + (parent_team_id IS NOT NULL)::int + (tenant_id IS NOT NULL)::int = 1);
Nullable foreign key
telemetry_events_export_queue.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
temporary_settings.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Hub table: referenced by 157 tables
Any change to its key, its delete semantics, or its partitioning touches every dependent. Migrations on hub tables need the longest lock windows and the most careful rollout.
Fix Treat schema changes here as breaking changes with a written rollout plan.
Nullable foreign key
user_credentials.github_app_id may be NULL, so the relationship to github_apps is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
user_credentials.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Either/or foreign keys without a CHECK
user_credentials has several nullable FKs (github_app_id, tenant_id) that look like an exclusive arc — a row should point at exactly one of them. Nothing stops zero or both.
Fix Add a CHECK that exactly one is non-null, or restructure with a supertype.
ALTER TABLE user_credentials ADD CONSTRAINT user_credentials_one_target CHECK ((github_app_id IS NOT NULL)::int + (tenant_id IS NOT NULL)::int = 1);
Nullable foreign key
user_emails.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
user_external_accounts.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
user_onboarding_tour.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
user_onboarding_tour.updated_by may be NULL, so the relationship to users is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
TIMESTAMP without time zone
created_at stores wall-clock time with no zone. It reads back differently depending on the session's TimeZone, and DST transitions produce ambiguous values.
Fix Use TIMESTAMPTZ.
ALTER TABLE user_onboarding_tour ALTER COLUMN created_at TYPE timestamptz;
Nullable foreign key
user_pending_permissions.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
user_permissions.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
user_public_repos.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
user_repo_permissions.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
user_repo_permissions.user_external_account_id may be NULL, so the relationship to user_external_accounts is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
user_repo_permissions.user_id may be NULL, so the relationship to users is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Either/or foreign keys without a CHECK
user_repo_permissions has several nullable FKs (tenant_id, user_external_account_id, user_id) that look like an exclusive arc — a row should point at exactly one of them. Nothing stops zero or both.
Fix Add a CHECK that exactly one is non-null, or restructure with a supertype.
ALTER TABLE user_repo_permissions ADD CONSTRAINT user_repo_permissions_one_target CHECK ((tenant_id IS NOT NULL)::int + (user_external_account_id IS NOT NULL)::int + (user_id IS NOT NULL)::int = 1);
Nullable foreign key
user_roles.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
F458users tenant_idfk-nullable Nullable foreign key
users.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Hub table: referenced by 80 tables
Any change to its key, its delete semantics, or its partitioning touches every dependent. Migrations on hub tables need the longest lock windows and the most careful rollout.
Fix Treat schema changes here as breaking changes with a written rollout plan.
Isolated table
versions references nothing and nothing references it. Either it is a staging/log table (fine, say so), or it is dead, or it is the seed of a second data model growing beside the first.
Fix Document its purpose or drop it.
Nullable foreign key
vulnerabilities.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
vulnerability_affected_packages.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
vulnerability_affected_symbols.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
vulnerability_matches.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
webhook_logs.external_service_id may be NULL, so the relationship to external_services is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
webhook_logs.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
webhook_logs.webhook_id may be NULL, so the relationship to webhooks is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
F627webhook_logs external_service_id, tenant_id, webhook_idexclusive-arc Either/or foreign keys without a CHECK
webhook_logs has several nullable FKs (external_service_id, tenant_id, webhook_id) that look like an exclusive arc — a row should point at exactly one of them. Nothing stops zero or both.
Fix Add a CHECK that exactly one is non-null, or restructure with a supertype.
ALTER TABLE webhook_logs ADD CONSTRAINT webhook_logs_one_target CHECK ((external_service_id IS NOT NULL)::int + (tenant_id IS NOT NULL)::int + (webhook_id IS NOT NULL)::int = 1);
F523webhooks created_by_user_idfk-nullable Nullable foreign key
webhooks.created_by_user_id may be NULL, so the relationship to users is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
Nullable foreign key
webhooks.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
F525webhooks updated_by_user_idfk-nullable Nullable foreign key
webhooks.updated_by_user_id may be NULL, so the relationship to users is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.
F628webhooks created_by_user_id, tenant_id, updated_by_user_idexclusive-arc Either/or foreign keys without a CHECK
webhooks has several nullable FKs (created_by_user_id, tenant_id, updated_by_user_id) that look like an exclusive arc — a row should point at exactly one of them. Nothing stops zero or both.
Fix Add a CHECK that exactly one is non-null, or restructure with a supertype.
ALTER TABLE webhooks ADD CONSTRAINT webhooks_one_target CHECK ((created_by_user_id IS NOT NULL)::int + (tenant_id IS NOT NULL)::int + (updated_by_user_id IS NOT NULL)::int = 1);
Nullable foreign key
zoekt_repos.tenant_id may be NULL, so the relationship to tenants is optional. That is legitimate (e.g. approved_by before approval) but often a modelling shrug: a row with no owner, or two nullable FKs that are secretly an either/or.
Fix Confirm the optionality is a domain rule; document it in the column comment.