Sourcegraph

Code search and intelligence: repositories, code-intel uploads, batch changes, insights, permissions. The frontend database's squashed migrations. Domains here are by table-name prefix, not by judgment.

180 tables · 1715 columns · 362 foreign keys · 31 domains  |  23 errors · 340 warnings · 294 notes

Generated from sourcegraph-public-snapshot@main.sql on 2026-09-05.

Conventions

Conventions are claims. Findings below are where the schema breaks them.

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 (...);
F004external_service_reposprimary-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 (...);
F006feature_flag_overridesprimary-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 (...);
F009lsif_nearest_uploadsprimary-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 (...);
F014query_runner_stateprimary-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 (...);
F016repo_permissionsprimary-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 (...);
F017repo_statisticsprimary-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 (...);
F018search_context_reposprimary-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 (...);
F019sub_repo_permissionsprimary-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 (...);
F020user_emailsprimary-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 (...);
F022user_permissionsprimary-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 (...);
F023user_public_reposprimary-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)

F024access_requests decision_by_user_idfk-index

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;
F025access_requests tenant_idfk-index

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;
F527access_requests statusundocumented-enum

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 (...));
F026access_tokens creator_user_idfk-index

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);
F027access_tokens subject_user_idfk-index

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);
F028access_tokens tenant_idfk-index

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;
F581access_tokens value_sha256soft-delete-unique

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;
F029aggregated_user_statistics tenant_idfk-index

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;
F030assigned_owners owner_user_idfk-index

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);
F031assigned_owners tenant_idfk-index

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;
F032assigned_owners who_assigned_user_idfk-index

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;
F033assigned_teams owner_team_idfk-index

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);
F034assigned_teams tenant_idfk-index

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;
F035assigned_teams who_assigned_team_idfk-index

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;
F036batch_changes batch_spec_idfk-index

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);
F037batch_changes creator_idfk-index

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;
F038batch_changes last_applier_idfk-index

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;
F039batch_changes tenant_idfk-index

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;
F651batch_changesfk-cycle

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.

F040batch_changes_site_credentials github_app_idfk-index

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;
F041batch_changes_site_credentials tenant_idfk-index

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;
F528batch_changes_site_credentials 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 batch_changes_site_credentials ADD CONSTRAINT batch_changes_site_credentials_external_service_type_check CHECK (external_service_type IN (...));
F590batch_changes_site_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 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;
F043batch_spec_resolution_jobs initiator_idfk-index

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);
F044batch_spec_resolution_jobs tenant_idfk-index

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;
F529batch_spec_resolution_jobs stateundocumented-enum

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;
F530batch_spec_workspace_execution_jobs stateundocumented-enum

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;
F047batch_spec_workspace_files tenant_idfk-index

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;
F048batch_spec_workspaces repo_idfk-index

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);
F049batch_spec_workspaces tenant_idfk-index

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;
F050batch_specs batch_change_idfk-index

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;
F051batch_specs tenant_idfk-index

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;
F052batch_specs user_idfk-index

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;
F060cached_available_indexers tenant_idfk-index

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;
F061changeset_events tenant_idfk-index

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;
F062changeset_jobs batch_change_idfk-index

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);
F063changeset_jobs changeset_idfk-index

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);
F064changeset_jobs tenant_idfk-index

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;
F065changeset_jobs user_idfk-index

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);
F539changeset_jobs job_typeundocumented-enum

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 (...));
F540changeset_jobs stateundocumented-enum

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 (...));
F053changeset_specs repo_idfk-index

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);
F054changeset_specs tenant_idfk-index

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;
F055changeset_specs user_idfk-index

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;
F531changeset_specs typeundocumented-enum

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 (...));
F056changesets owned_by_batch_change_idfk-index

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;
F057changesets previous_spec_idfk-index

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;
F058changesets tenant_idfk-index

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;
F532changesets external_stateundocumented-enum

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 (...));
F535changesets publication_stateundocumented-enum

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 (...));
F536changesets reconciler_stateundocumented-enum

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 (...));
F537changesets computed_stateundocumented-enum

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 (...));
F066cm_action_jobs emailfk-index

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;
F067cm_action_jobs slack_webhookfk-index

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;
F068cm_action_jobs tenant_idfk-index

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;
F069cm_action_jobs webhookfk-index

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;
F541cm_action_jobs stateundocumented-enum

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 (...));
F070cm_emails changed_byfk-index

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);
F071cm_emails created_byfk-index

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);
F072cm_emails monitorfk-index

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);
F073cm_emails tenant_idfk-index

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;
F074cm_last_searched repo_idfk-index

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);
F075cm_last_searched tenant_idfk-index

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;
F076cm_monitors changed_byfk-index

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);
F077cm_monitors created_byfk-index

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);
F078cm_monitors namespace_org_idfk-index

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;
F079cm_monitors tenant_idfk-index

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;
F080cm_monitors namespace_user_idfk-index

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);
F081cm_queries tenant_idfk-index

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;
F082cm_queries changed_byfk-index

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);
F083cm_queries created_byfk-index

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);
F084cm_queries monitorfk-index

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);
F085cm_recipients emailfk-index

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);
F086cm_recipients namespace_org_idfk-index

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;
F087cm_recipients tenant_idfk-index

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;
F088cm_recipients namespace_user_idfk-index

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;
F089cm_slack_webhooks changed_byfk-index

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);
F090cm_slack_webhooks created_byfk-index

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);
F091cm_slack_webhooks tenant_idfk-index

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;
F092cm_trigger_jobs queryfk-index

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);
F093cm_trigger_jobs tenant_idfk-index

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;
F542cm_trigger_jobs stateundocumented-enum

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 (...));
F094cm_webhooks changed_byfk-index

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);
F095cm_webhooks created_byfk-index

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);
F096cm_webhooks tenant_idfk-index

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;
F097code_hosts tenant_idfk-index

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;
F543code_hosts kindundocumented-enum

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 (...));
F098codeintel_autoindex_queue tenant_idfk-index

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;
F099codeintel_autoindexing_exceptions tenant_idfk-index

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;
F100codeintel_commit_dates tenant_idfk-index

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;
F101codeintel_inference_scripts tenant_idfk-index

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;
F102codeintel_initial_path_ranks tenant_idfk-index

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;
F105codeintel_path_ranks tenant_idfk-index

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;
F106codeintel_ranking_definitions tenant_idfk-index

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;
F107codeintel_ranking_exports tenant_idfk-index

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;
F108codeintel_ranking_exports upload_idfk-index

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;
F584codeintel_ranking_exports graph_key, upload_idsoft-delete-unique

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;
F109codeintel_ranking_graph_keys tenant_idfk-index

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;
F111codeintel_ranking_progress tenant_idfk-index

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;
F112codeintel_ranking_references tenant_idfk-index

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;
F114codeowners tenant_idfk-index

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;
F115codeowners_individual_stats owner_idfk-index

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);
F116codeowners_individual_stats tenant_idfk-index

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;
F117codeowners_owners tenant_idfk-index

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;
F118commit_authors tenant_idfk-index

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;
F119configuration_policies_audit_logs tenant_idfk-index

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;
F120context_detection_embedding_jobs tenant_idfk-index

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;
F545context_detection_embedding_jobs stateundocumented-enum

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 (...));
F121discussion_comments tenant_idfk-index

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;
F122discussion_mail_reply_tokens tenant_idfk-index

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;
F123discussion_mail_reply_tokens thread_idfk-index

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);
F124discussion_threads target_repo_idfk-index

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;
F125discussion_threads tenant_idfk-index

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;
F652discussion_threadsfk-cycle

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.

F126discussion_threads_target_repo tenant_idfk-index

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;
F127discussion_threads_target_repo thread_idfk-index

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);
F128event_logs tenant_idfk-index

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 (...));
F129event_logs_export_allowlist tenant_idfk-index

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;
F130event_logs_scrape_state tenant_idfk-index

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;
F131event_logs_scrape_state_own tenant_idfk-index

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;
F132executor_heartbeats tenant_idfk-index

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;
F133executor_job_tokens tenant_idfk-index

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;
F134executor_secret_access_logs executor_secret_idfk-index

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);
F135executor_secret_access_logs tenant_idfk-index

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;
F136executor_secret_access_logs user_idfk-index

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;
F137executor_secrets creator_idfk-index

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;
F138executor_secrets namespace_org_idfk-index

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;
F139executor_secrets namespace_user_idfk-index

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;
F140executor_secrets tenant_idfk-index

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;
F547executor_secrets scopeundocumented-enum

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 (...));
F141exhaustive_search_jobs initiator_idfk-index

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);
F142exhaustive_search_jobs tenant_idfk-index

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;
F548exhaustive_search_jobs stateundocumented-enum

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 (...));
F143exhaustive_search_repo_jobs repo_idfk-index

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);
F144exhaustive_search_repo_jobs search_job_idfk-index

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);
F145exhaustive_search_repo_jobs tenant_idfk-index

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;
F549exhaustive_search_repo_jobs stateundocumented-enum

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 (...));
F146exhaustive_search_repo_revision_jobs search_repo_job_idfk-index

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;
F550exhaustive_search_repo_revision_jobs stateundocumented-enum

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 (...));
F149external_service_repos tenant_idfk-index

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;
F150external_service_sync_jobs tenant_idfk-index

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;
F151external_service_sync_jobs external_service_idfk-index

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);
F552external_service_sync_jobs stateundocumented-enum

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 (...));
F152external_services code_host_idfk-index

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;
F153external_services creator_idfk-index

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;
F154external_services last_updater_idfk-index

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;
F155external_services tenant_idfk-index

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;
F553external_services kindundocumented-enum

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 (...));
F156feature_flag_overrides tenant_idfk-index

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;
F585feature_flag_overrides namespace_org_id, flag_namesoft-delete-unique

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;
F586feature_flag_overrides namespace_user_id, flag_namesoft-delete-unique

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;
F157feature_flags tenant_idfk-index

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;
F158github_app_installs tenant_idfk-index

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;
F554github_app_installs account_typeundocumented-enum

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 (...));
F159github_apps tenant_idfk-index

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;
F160github_apps webhook_idfk-index

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;
F161gitserver_relocator_jobs tenant_idfk-index

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;
F555gitserver_relocator_jobs stateundocumented-enum

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 (...));
F162gitserver_repos tenant_idfk-index

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;
F556gitserver_repos clone_statusundocumented-enum

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 (...));
F163gitserver_repos_statistics tenant_idfk-index

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;
F164gitserver_repos_sync_output tenant_idfk-index

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;
F165global_state tenant_idfk-index

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;
F166insights_query_runner_jobs tenant_idfk-index

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;
F557insights_query_runner_jobs stateundocumented-enum

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;
F168insights_settings_migration_jobs tenant_idfk-index

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;
F544lsif_configuration_policies typeundocumented-enum

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 (...));
F169lsif_dependency_indexing_jobs upload_idfk-index

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;
F558lsif_dependency_indexing_jobs stateundocumented-enum

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 (...));
F559lsif_dependency_indexing_jobs external_service_kindundocumented-enum

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 (...));
F560lsif_dependency_syncing_jobs stateundocumented-enum

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 (...));
F563lsif_indexes stateundocumented-enum

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 (...));
F561lsif_uploads stateundocumented-enum

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 (...));
F562lsif_uploads content_typeundocumented-enum

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 (...));
F564lsif_uploads_audit_logs content_typeundocumented-enum

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 (...));
F170names org_idfk-index

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;
F174namespace_permissions tenant_idfk-index

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;
F175namespace_permissions user_idfk-index

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);
F176notebook_stars tenant_idfk-index

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;
F177notebooks creator_user_idfk-index

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;
F178notebooks tenant_idfk-index

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;
F179notebooks updater_user_idfk-index

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;
F180org_invitations sender_user_idfk-index

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);
F181org_invitations tenant_idfk-index

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;
F182org_members tenant_idfk-index

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;
F183org_members user_idfk-index

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);
F184org_stats tenant_idfk-index

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;
F186orgs_open_beta_stats tenant_idfk-index

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;
F187out_of_band_migrations tenant_idfk-index

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;
F188out_of_band_migrations_errors migration_idfk-index

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);
F189out_of_band_migrations_errors tenant_idfk-index

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;
F190outbound_webhook_event_types outbound_webhook_idfk-index

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);
F191outbound_webhook_event_types tenant_idfk-index

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;
F565outbound_webhook_event_types event_typeundocumented-enum

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 (...));
F566outbound_webhook_event_types scopeundocumented-enum

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 (...));
F192outbound_webhook_jobs tenant_idfk-index

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;
F567outbound_webhook_jobs event_typeundocumented-enum

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 (...));
F568outbound_webhook_jobs scopeundocumented-enum

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 (...));
F569outbound_webhook_jobs stateundocumented-enum

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 (...));
F193outbound_webhook_logs job_idfk-index

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);
F194outbound_webhook_logs tenant_idfk-index

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;
F195outbound_webhooks created_byfk-index

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;
F196outbound_webhooks tenant_idfk-index

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;
F197outbound_webhooks updated_byfk-index

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;
F198own_aggregate_recent_contribution commit_author_idfk-index

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);
F199own_aggregate_recent_contribution tenant_idfk-index

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;
F200own_aggregate_recent_view tenant_idfk-index

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;
F201own_aggregate_recent_view viewer_idfk-index

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);
F202own_background_jobs tenant_idfk-index

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;
F570own_background_jobs stateundocumented-enum

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 (...));
F203own_signal_configurations tenant_idfk-index

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;
F204own_signal_recent_contribution changed_file_path_idfk-index

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);
F205own_signal_recent_contribution commit_author_idfk-index

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);
F206own_signal_recent_contribution tenant_idfk-index

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;
F207ownership_path_stats tenant_idfk-index

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;
F208package_repo_filters tenant_idfk-index

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;
F587package_repo_filters scheme, matchersoft-delete-unique

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;
F209package_repo_versions tenant_idfk-index

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;
F210permission_sync_jobs tenant_idfk-index

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;
F211permission_sync_jobs triggered_by_user_idfk-index

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;
F571permission_sync_jobs stateundocumented-enum

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 (...));
F212permissions tenant_idfk-index

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;
F213phabricator_repos tenant_idfk-index

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;
F588phabricator_repos repo_namesoft-delete-unique

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;
F214product_licenses product_subscription_idfk-index

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);
F215product_licenses tenant_idfk-index

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;
F216product_subscriptions tenant_idfk-index

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;
F217product_subscriptions user_idfk-index

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);
F218prompts created_byfk-index

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;
F219prompts tenant_idfk-index

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;
F220prompts updated_byfk-index

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;
F222query_runner_state tenant_idfk-index

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;
F223redis_key_value tenant_idfk-index

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;
F224registry_extension_releases creator_user_idfk-index

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);
F225registry_extension_releases tenant_idfk-index

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;
F226registry_extensions publisher_org_idfk-index

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;
F227registry_extensions publisher_user_idfk-index

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;
F228registry_extensions tenant_idfk-index

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;
F589registry_extensions uuidsoft-delete-unique

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.

F229repo_commits_changelists tenant_idfk-index

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;
F230repo_embedding_job_stats tenant_idfk-index

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;
F231repo_embedding_jobs tenant_idfk-index

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;
F572repo_embedding_jobs stateundocumented-enum

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 (...));
F232repo_kvps tenant_idfk-index

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;
F233repo_paths parent_idfk-index

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;
F234repo_paths tenant_idfk-index

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;
F235repo_pending_permissions tenant_idfk-index

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;
F236repo_permissions tenant_idfk-index

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;
F237repo_statistics tenant_idfk-index

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;
F238role_permissions role_idfk-index

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);
F239role_permissions tenant_idfk-index

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;
F241saved_searches created_byfk-index

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;
F242saved_searches org_idfk-index

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;
F243saved_searches tenant_idfk-index

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;
F244saved_searches updated_byfk-index

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;
F245saved_searches user_idfk-index

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;
F246search_context_default search_context_idfk-index

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);
F247search_context_default tenant_idfk-index

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;
F248search_context_repos search_context_idfk-index

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);
F249search_context_repos tenant_idfk-index

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;
F250search_context_stars tenant_idfk-index

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;
F251search_context_stars user_idfk-index

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);
F252search_contexts namespace_org_idfk-index

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;
F253search_contexts namespace_user_idfk-index

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;
F254search_contexts tenant_idfk-index

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;
F255security_event_logs tenant_idfk-index

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;
F256settings author_user_idfk-index

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;
F257settings tenant_idfk-index

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;
F258sub_repo_permissions tenant_idfk-index

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;
F259survey_responses tenant_idfk-index

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;
F260survey_responses user_idfk-index

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;
F261syntactic_scip_indexing_jobs tenant_idfk-index

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;
F573syntactic_scip_indexing_jobs stateundocumented-enum

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 (...));
F262syntactic_scip_last_index_scan tenant_idfk-index

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;
F263team_members tenant_idfk-index

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;
F264team_members user_idfk-index

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;
F268telemetry_events_export_queue tenant_idfk-index

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;
F269temporary_settings tenant_idfk-index

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;
F270user_credentials github_app_idfk-index

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;
F271user_credentials tenant_idfk-index

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;
F272user_credentials user_idfk-index

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);
F574user_credentials 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 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.

F273user_emails tenant_idfk-index

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;
F274user_external_accounts tenant_idfk-index

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;
F575user_external_accounts service_typeundocumented-enum

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 (...));
F593user_external_accounts service_type, service_idpolymorphic-reference

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.

F275user_onboarding_tour tenant_idfk-index

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;
F276user_onboarding_tour updated_byfk-index

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;
F277user_pending_permissions tenant_idfk-index

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;
F576user_pending_permissions object_typeundocumented-enum

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 (...));
F577user_pending_permissions service_typeundocumented-enum

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 (...));
F594user_pending_permissions service_type, service_idpolymorphic-reference

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.

F278user_permissions tenant_idfk-index

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;
F578user_permissions object_typeundocumented-enum

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 (...));
F279user_public_repos repo_idfk-index

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);
F280user_public_repos tenant_idfk-index

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;
F281user_repo_permissions tenant_idfk-index

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;
F282user_roles role_idfk-index

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);
F283user_roles tenant_idfk-index

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;
F284vulnerabilities tenant_idfk-index

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;
F285vulnerability_affected_packages tenant_idfk-index

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;
F286vulnerability_affected_symbols tenant_idfk-index

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;
F287vulnerability_matches tenant_idfk-index

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;
F288webhook_logs tenant_idfk-index

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;
F289webhook_logs webhook_idfk-index

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;
F290webhooks created_by_user_idfk-index

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;
F291webhooks tenant_idfk-index

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;
F292webhooks updated_by_user_idfk-index

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 (...));
F293zoekt_repos tenant_idfk-index

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;
F580zoekt_repos index_statusundocumented-enum

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)

F294access_requests decision_by_user_idfk-nullable

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.

F295access_requests tenant_idfk-nullable

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.

F595access_requests decision_by_user_id, tenant_idexclusive-arc

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);
F296access_tokens tenant_idfk-nullable

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.

F297aggregated_user_statistics tenant_idfk-nullable

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.

F298assigned_owners tenant_idfk-nullable

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.

F299assigned_owners who_assigned_user_idfk-nullable

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.

F596assigned_owners tenant_id, who_assigned_user_idexclusive-arc

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);
F629assigned_owners assigned_attimestamp-tz

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;
F300assigned_teams tenant_idfk-nullable

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.

F301assigned_teams who_assigned_team_idfk-nullable

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.

F597assigned_teams tenant_id, who_assigned_team_idexclusive-arc

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);
F630assigned_teams assigned_attimestamp-tz

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;
F302batch_changes creator_idfk-nullable

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.

F303batch_changes last_applier_idfk-nullable

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.

F304batch_changes namespace_org_idfk-nullable

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.

F305batch_changes namespace_user_idfk-nullable

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.

F306batch_changes tenant_idfk-nullable

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);
F307batch_changes_site_credentials github_app_idfk-nullable

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.

F308batch_changes_site_credentials tenant_idfk-nullable

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.

F599batch_changes_site_credentials github_app_id, tenant_idexclusive-arc

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);
F309batch_spec_execution_cache_entries tenant_idfk-nullable

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.

F310batch_spec_resolution_jobs tenant_idfk-nullable

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.

F311batch_spec_workspace_execution_jobs tenant_idfk-nullable

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.

F313batch_spec_workspace_files tenant_idfk-nullable

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.

F314batch_spec_workspaces tenant_idfk-nullable

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.

F315batch_specs batch_change_idfk-nullable

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.

F316batch_specs tenant_idfk-nullable

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.

F317batch_specs user_idfk-nullable

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);
F326cached_available_indexers tenant_idfk-nullable

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.

F327changeset_events tenant_idfk-nullable

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.

F328changeset_jobs tenant_idfk-nullable

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.

F318changeset_specs batch_spec_idfk-nullable

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.

F319changeset_specs tenant_idfk-nullable

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.

F320changeset_specs user_idfk-nullable

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.

F601changeset_specs batch_spec_id, tenant_id, user_idexclusive-arc

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);
F321changesets current_spec_idfk-nullable

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.

F323changesets previous_spec_idfk-nullable

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.

F324changesets tenant_idfk-nullable

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);
F656changesetswide-table

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.

F329cm_action_jobs emailfk-nullable

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.

F330cm_action_jobs slack_webhookfk-nullable

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.

F331cm_action_jobs tenant_idfk-nullable

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.

F332cm_action_jobs trigger_eventfk-nullable

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.

F333cm_action_jobs webhookfk-nullable

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);
F334cm_emails tenant_idfk-nullable

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.

F335cm_last_searched tenant_idfk-nullable

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.

F336cm_monitors namespace_org_idfk-nullable

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.

F337cm_monitors tenant_idfk-nullable

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);
F338cm_queries tenant_idfk-nullable

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.

F339cm_recipients namespace_org_idfk-nullable

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.

F340cm_recipients tenant_idfk-nullable

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.

F341cm_recipients namespace_user_idfk-nullable

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);
F342cm_slack_webhooks tenant_idfk-nullable

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.

F343cm_trigger_jobs tenant_idfk-nullable

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.

F344cm_webhooks tenant_idfk-nullable

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.

F345code_hosts tenant_idfk-nullable

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.

F346codeintel_autoindex_queue tenant_idfk-nullable

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.

F347codeintel_autoindexing_exceptions tenant_idfk-nullable

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.

F348codeintel_commit_dates tenant_idfk-nullable

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.

F349codeintel_inference_scripts tenant_idfk-nullable

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.

F350codeintel_initial_path_ranks tenant_idfk-nullable

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.

F352codeintel_langugage_support_requests tenant_idfk-nullable

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.

F353codeintel_path_ranks tenant_idfk-nullable

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.

F354codeintel_ranking_definitions tenant_idfk-nullable

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.

F355codeintel_ranking_exports tenant_idfk-nullable

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.

F356codeintel_ranking_exports upload_idfk-nullable

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.

F606codeintel_ranking_exports tenant_id, upload_idexclusive-arc

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);
F357codeintel_ranking_graph_keys tenant_idfk-nullable

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.

F358codeintel_ranking_path_counts_inputs tenant_idfk-nullable

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.

F359codeintel_ranking_progress tenant_idfk-nullable

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.

F360codeintel_ranking_references tenant_idfk-nullable

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.

F362codeowners tenant_idfk-nullable

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.

F363codeowners_individual_stats tenant_idfk-nullable

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.

F631codeowners_individual_stats updated_attimestamp-tz

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;
F364codeowners_owners tenant_idfk-nullable

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.

F365commit_authors tenant_idfk-nullable

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.

F366configuration_policies_audit_logs tenant_idfk-nullable

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.

F367context_detection_embedding_jobs tenant_idfk-nullable

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.

F641critical_and_site_configorphan-table

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.

F368discussion_comments tenant_idfk-nullable

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.

F369discussion_mail_reply_tokens tenant_idfk-nullable

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.

F370discussion_threads target_repo_idfk-nullable

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.

F371discussion_threads tenant_idfk-nullable

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.

F607discussion_threads target_repo_id, tenant_idexclusive-arc

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);
F372discussion_threads_target_repo tenant_idfk-nullable

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.

F373event_logs tenant_idfk-nullable

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.

F374event_logs_export_allowlist tenant_idfk-nullable

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.

F375event_logs_scrape_state tenant_idfk-nullable

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.

F376event_logs_scrape_state_own tenant_idfk-nullable

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.

F377executor_heartbeats tenant_idfk-nullable

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.

F378executor_job_tokens tenant_idfk-nullable

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.

F379executor_secret_access_logs tenant_idfk-nullable

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.

F380executor_secret_access_logs user_idfk-nullable

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.

F608executor_secret_access_logs tenant_id, user_idexclusive-arc

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);
F381executor_secrets creator_idfk-nullable

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.

F382executor_secrets namespace_org_idfk-nullable

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.

F383executor_secrets namespace_user_idfk-nullable

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.

F384executor_secrets tenant_idfk-nullable

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);
F385exhaustive_search_jobs tenant_idfk-nullable

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.

F386exhaustive_search_repo_jobs tenant_idfk-nullable

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.

F387exhaustive_search_repo_revision_jobs tenant_idfk-nullable

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.

F389external_service_repos tenant_idfk-nullable

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.

F390external_service_sync_jobs tenant_idfk-nullable

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.

F391external_services code_host_idfk-nullable

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.

F392external_services creator_idfk-nullable

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.

F393external_services last_updater_idfk-nullable

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.

F394external_services tenant_idfk-nullable

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);
F395feature_flag_overrides namespace_org_idfk-nullable

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.

F396feature_flag_overrides namespace_user_idfk-nullable

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.

F397feature_flag_overrides tenant_idfk-nullable

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.

F611feature_flag_overrides namespace_org_id, namespace_user_id, tenant_idexclusive-arc

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);
F398feature_flags tenant_idfk-nullable

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.

F399github_app_installs tenant_idfk-nullable

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.

F400github_apps tenant_idfk-nullable

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.

F401github_apps webhook_idfk-nullable

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.

F612github_apps tenant_id, webhook_idexclusive-arc

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);
F402gitserver_relocator_jobs tenant_idfk-nullable

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.

F403gitserver_repos tenant_idfk-nullable

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.

F404gitserver_repos_statistics tenant_idfk-nullable

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.

F405gitserver_repos_sync_output tenant_idfk-nullable

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.

F406global_state tenant_idfk-nullable

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.

F407insights_query_runner_jobs tenant_idfk-nullable

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.

F632insights_query_runner_jobs_dependencies recording_timetimestamp-tz

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;
F409insights_settings_migration_jobs tenant_idfk-nullable

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.

F633insights_settings_migration_jobs completed_attimestamp-tz

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.

F410lsif_dependency_indexing_jobs upload_idfk-nullable

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.

F411lsif_dependency_syncing_jobs upload_idfk-nullable

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.

F642lsif_dirty_repositoriesorphan-table

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.

F643lsif_indexesorphan-table

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.

F644lsif_last_index_scanorphan-table

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.

F645lsif_last_retention_scanorphan-table

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.

F646lsif_nearest_uploadsorphan-table

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.

F657lsif_uploadswide-table

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.

F648lsif_uploads_audit_logsorphan-table

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.

F634lsif_uploads_vulnerability_scan last_scanned_attimestamp-tz

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);
F416namespace_permissions tenant_idfk-nullable

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.

F417notebook_stars tenant_idfk-nullable

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.

F418notebooks creator_user_idfk-nullable

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.

F419notebooks namespace_org_idfk-nullable

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.

F420notebooks namespace_user_idfk-nullable

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.

F421notebooks tenant_idfk-nullable

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.

F422notebooks updater_user_idfk-nullable

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);
F423org_invitations recipient_user_idfk-nullable

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.

F424org_invitations tenant_idfk-nullable

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.

F615org_invitations recipient_user_id, tenant_idexclusive-arc

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);
F425org_members tenant_idfk-nullable

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.

F426org_stats tenant_idfk-nullable

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.

F428orgs_open_beta_stats tenant_idfk-nullable

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.

F429out_of_band_migrations tenant_idfk-nullable

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.

F430out_of_band_migrations_errors tenant_idfk-nullable

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.

F431outbound_webhook_event_types tenant_idfk-nullable

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.

F432outbound_webhook_jobs tenant_idfk-nullable

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.

F433outbound_webhook_logs tenant_idfk-nullable

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.

F434outbound_webhooks created_byfk-nullable

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.

F435outbound_webhooks tenant_idfk-nullable

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.

F436outbound_webhooks updated_byfk-nullable

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.

F437own_aggregate_recent_contribution tenant_idfk-nullable

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.

F438own_aggregate_recent_view tenant_idfk-nullable

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.

F439own_background_jobs tenant_idfk-nullable

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.

F440own_signal_configurations tenant_idfk-nullable

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.

F441own_signal_recent_contribution tenant_idfk-nullable

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.

F635own_signal_recent_contribution commit_timestamptimestamp-tz

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;
F442ownership_path_stats tenant_idfk-nullable

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.

F636ownership_path_stats last_updated_attimestamp-tz

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;
F443package_repo_filters tenant_idfk-nullable

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.

F444package_repo_versions tenant_idfk-nullable

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.

F445permission_sync_jobs repository_idfk-nullable

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.

F446permission_sync_jobs tenant_idfk-nullable

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.

F447permission_sync_jobs triggered_by_user_idfk-nullable

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.

F448permission_sync_jobs user_idfk-nullable

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.

F616permission_sync_jobs repository_id, tenant_id, triggered_by_user_id, user_idexclusive-arc

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);
F449permissions tenant_idfk-nullable

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.

F450phabricator_repos tenant_idfk-nullable

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.

F451product_licenses tenant_idfk-nullable

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.

F452product_subscriptions tenant_idfk-nullable

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.

F453prompts created_byfk-nullable

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.

F456prompts tenant_idfk-nullable

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.

F457prompts updated_byfk-nullable

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);
F459query_runner_state tenant_idfk-nullable

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.

F460redis_key_value tenant_idfk-nullable

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.

F461registry_extension_releases tenant_idfk-nullable

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.

F462registry_extensions publisher_org_idfk-nullable

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.

F463registry_extensions publisher_user_idfk-nullable

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.

F464registry_extensions tenant_idfk-nullable

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.

F618registry_extensions publisher_org_id, publisher_user_id, tenant_idexclusive-arc

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.

F655repoblast-radius

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.

F465repo_commits_changelists tenant_idfk-nullable

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.

F466repo_embedding_job_stats tenant_idfk-nullable

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.

F467repo_embedding_jobs tenant_idfk-nullable

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.

F468repo_kvps tenant_idfk-nullable

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.

F469repo_paths parent_idfk-nullable

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.

F470repo_paths tenant_idfk-nullable

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.

F619repo_paths parent_id, tenant_idexclusive-arc

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;
F471repo_pending_permissions tenant_idfk-nullable

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.

F472repo_permissions tenant_idfk-nullable

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.

F473repo_statistics tenant_idfk-nullable

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.

F474role_permissions tenant_idfk-nullable

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.

F476saved_searches created_byfk-nullable

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.

F477saved_searches org_idfk-nullable

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.

F478saved_searches tenant_idfk-nullable

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.

F479saved_searches updated_byfk-nullable

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.

F480saved_searches user_idfk-nullable

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.

F620saved_searches org_id, tenant_id, user_idexclusive-arc

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);
F481search_context_default tenant_idfk-nullable

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.

F482search_context_repos tenant_idfk-nullable

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.

F483search_context_stars tenant_idfk-nullable

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.

F484search_contexts namespace_org_idfk-nullable

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.

F485search_contexts namespace_user_idfk-nullable

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.

F486search_contexts tenant_idfk-nullable

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);
F487security_event_logs tenant_idfk-nullable

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.

F488settings author_user_idfk-nullable

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.

F489settings org_idfk-nullable

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.

F490settings tenant_idfk-nullable

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.

F491settings user_idfk-nullable

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);
F492sub_repo_permissions tenant_idfk-nullable

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.

F493survey_responses tenant_idfk-nullable

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.

F494survey_responses user_idfk-nullable

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.

F623survey_responses tenant_id, user_idexclusive-arc

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);
F495syntactic_scip_indexing_jobs tenant_idfk-nullable

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.

F496syntactic_scip_last_index_scan tenant_idfk-nullable

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.

F497team_members tenant_idfk-nullable

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);
F501telemetry_events_export_queue tenant_idfk-nullable

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.

F502temporary_settings tenant_idfk-nullable

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.

F653tenantsblast-radius

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.

F503user_credentials github_app_idfk-nullable

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.

F504user_credentials tenant_idfk-nullable

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.

F625user_credentials github_app_id, tenant_idexclusive-arc

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);
F505user_emails tenant_idfk-nullable

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.

F506user_external_accounts tenant_idfk-nullable

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.

F507user_onboarding_tour tenant_idfk-nullable

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.

F508user_onboarding_tour updated_byfk-nullable

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.

F638user_onboarding_tour created_attimestamp-tz

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;
F509user_pending_permissions tenant_idfk-nullable

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.

F510user_permissions tenant_idfk-nullable

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.

F511user_public_repos tenant_idfk-nullable

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.

F512user_repo_permissions tenant_idfk-nullable

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.

F513user_repo_permissions user_external_account_idfk-nullable

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.

F514user_repo_permissions user_idfk-nullable

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.

F626user_repo_permissions tenant_id, user_external_account_id, user_idexclusive-arc

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);
F515user_roles tenant_idfk-nullable

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.

F654usersblast-radius

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.

F650versionsorphan-table

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.

F516vulnerabilities tenant_idfk-nullable

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.

F517vulnerability_affected_packages tenant_idfk-nullable

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.

F518vulnerability_affected_symbols tenant_idfk-nullable

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.

F519vulnerability_matches tenant_idfk-nullable

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.

F520webhook_logs external_service_idfk-nullable

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.

F521webhook_logs tenant_idfk-nullable

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.

F522webhook_logs webhook_idfk-nullable

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.

F524webhooks tenant_idfk-nullable

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);
F526zoekt_repos tenant_idfk-nullable

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.

Schema

Schema map: 180 tables, 362 foreign keys, 31 domains. Click to open the 3D explorer.accessaccess_requestsaccess_tokensassignedassigned_ownersassigned_teamsbatchbatch_spec_execution_cache_entriesbatch_spec_workspace_execution_last_dequeuesbatch_specsbatch_changesbatch_spec_resolution_jobsbatch_spec_workspace_filesbatch_spec_workspacesbatch_changes_site_credentialsbatch_spec_workspace_execution_jobschangesetchangeset_specschangeset_eventschangeset_jobsreporeporepo_embedding_jobsrepo_pending_permissionsrepo_permissionsrepo_statisticsrepo_commits_changelistsrepo_embedding_job_statsrepo_kvpsrepo_pathscmcm_monitorscm_emailscm_last_searchedcm_queriescm_slack_webhookscm_webhookscm_recipientscm_trigger_jobscm_action_jobscodeintelcodeintel_autoindex_queuecodeintel_commit_datescodeintel_inference_scriptscodeintel_langugage_support_requestscodeintel_path_rankscodeintel_ranking_exportscodeintel_ranking_graph_keyscodeintel_ranking_path_counts_inputscodeintel_ranking_progresscodeintel_autoindexing_exceptionscodeintel_initial_path_rankscodeintel_ranking_definitionscodeintel_ranking_referencescodeintel_initial_path_ranks_processedcodeintel_ranking_references_processedlsiflsif_configuration_policieslsif_configuration_policies_repository_pattern_lookuplsif_dependency_reposlsif_dirty_repositorieslsif_indexeslsif_last_index_scanlsif_last_retention_scanlsif_nearest_uploadslsif_nearest_uploads_linkslsif_uploadslsif_uploads_audit_logslsif_uploads_visible_at_tiplsif_dependency_indexing_jobslsif_dependency_syncing_jobslsif_packageslsif_referenceslsif_uploads_reference_countslsif_uploads_vulnerability_scanlsif_index_configurationlsif_retention_configurationcodeownerscodeowners_ownerscodeownerscodeowners_individual_statsdiscussiondiscussion_threads_target_repodiscussion_threadsdiscussion_commentsdiscussion_mail_reply_tokenseventevent_logsevent_logs_export_allowlistevent_logs_scrape_stateevent_logs_scrape_state_ownexecutorexecutor_heartbeatsexecutor_job_tokensexecutor_secretsexecutor_secret_access_logsexhaustiveexhaustive_search_jobsexhaustive_search_repo_jobsexhaustive_search_repo_revision_jobsexternalexternal_servicesexternal_service_reposexternal_service_sync_jobsfeaturefeature_flagsfeature_flag_overridesgithubgithub_appsgithub_app_installsgitservergitserver_relocator_jobsgitserver_repos_statisticsgitserver_reposgitserver_repos_sync_outputinsightsinsights_query_runner_jobsinsights_settings_migration_jobsinsights_query_runner_jobs_dependenciesorgorg_invitationsorg_membersorg_statsorgsorgsorgs_open_beta_statsoutout_of_band_migrationsout_of_band_migrations_errorsoutboundoutbound_webhook_jobsoutbound_webhooksoutbound_webhook_event_typesoutbound_webhook_logsownown_background_jobsown_signal_configurationsown_aggregate_recent_contributionown_aggregate_recent_viewown_signal_recent_contributionpackagepackage_repo_filterspackage_repo_versionsproductproduct_subscriptionsproduct_licensesregistryregistry_extensionsregistry_extension_releasessearchsearch_contextssearch_context_defaultsearch_context_repossearch_context_starssyntacticsyntactic_scip_indexing_jobssyntactic_scip_last_index_scanuseruser_pending_permissionsuser_permissionsuser_emailsuser_external_accountsuser_onboarding_touruser_public_reposuser_rolesuser_repo_permissionsuser_credentialsvulnerabilityvulnerability_affected_packagesvulnerability_affected_symbolsvulnerability_matchesmisccritical_and_site_configtenantsversionscached_available_indexerscode_hostscommit_authorsconfiguration_policies_audit_logscontext_detection_embedding_jobsexplicit_permissions_bitbucket_projects_jobsglobal_statepermissionsphabricator_reposquery_runner_stateredis_key_valuerolessecurity_event_logstelemetry_events_export_queueusersvulnerabilitiesaggregated_user_statisticsnamespace_permissionsnotebookspermission_sync_jobspromptsrole_permissionssaved_searchessettingssub_repo_permissionssurvey_responsesteamstemporary_settingswebhookszoekt_reposnamesnotebook_starsownership_path_statsteam_memberswebhook_logschangesets

180 tables · 362 foreign keys · 31 domains. Open the 3D explorer: rotate, zoom, click any table or relationship.

access 2 tables

Tables whose names start with access_.

Entity-relationship diagram: access_requests, access_tokensaccess_requestsid PKintegercreated_attimestamptzupdated_attimestamptznametextemailtextadditional_infotextstatustextdecision_by_user_id FKintegertenant_id FKintegeraccess_tokensid PKbigintsubject_user_id FKintegervalue_sha256byteanotetextcreated_attimestamptzlast_used_attimestamptzdeleted_attimestamptzcreator_user_id FKintegerscopestext[]internalbooleanexpires_attimestamptztenant_id FKintegertenantsusersdecision_by_user_idtenant_idcreator_user_idsubject_user_idtenant_id

Relationships

access_requests

9 cols · no RLS · line 873View in 3D
  • Foreign key without index decision_by_user_id — 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.
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key decision_by_user_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Enum-like column with no CHECK status — 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.
  • Either/or foreign keys without a CHECK decision_by_user_id, tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
created_at timestamptzNOT NULLnow()
updated_at timestamptzNOT NULLnow()
name textNOT NULL
email UKtextNOT NULL
additional_info textnull
status textNOT NULL
decision_by_user_id integernullusers.id 1:N · SET NULL
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (created_at) access_requests_created_at
  • (status) access_requests_status

Referenced by

  • nothing

access_tokens

12 cols · no RLS · line 895View in 3D
  • Foreign key without index creator_user_id — 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.
  • Foreign key without index subject_user_id — 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.
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Soft delete collides with non-partial UNIQUE value_sha256 — 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.
ColumnTypeNullDefaultReferencesNotes
id PKbigintNOT NULL
subject_user_id integerNOT NULLusers.id 1:N
value_sha256 UKbyteaNOT NULL
note textNOT NULL
created_at timestamptzNOT NULLnow()
last_used_at timestamptznull
deleted_at timestamptznull
creator_user_id integerNOT NULLusers.id 1:N
scopes text[]NOT NULL
internal booleannullFALSE
expires_at timestamptznull
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (value_sha256) WHERE deleted_at IS NULL access_tokens_lookup
  • (digest(value_sha256, CAST('sha256' AS text))) WHERE deleted_at IS NULL access_tokens_lookup_double_hash

Referenced by

  • nothing

assigned 2 tables

Tables whose names start with assigned_.

Entity-relationship diagram: assigned_owners, assigned_teamsassigned_ownersid PKintegerowner_user_id FKintegerfile_path_id FKintegerwho_assigned_user_id FKintegerassigned_attimestamptenant_id FKintegerassigned_teamsid PKintegerowner_team_id FKintegerfile_path_id FKintegerwho_assigned_team_id FKintegerassigned_attimestamptenant_id FKintegerrepo_pathsteamstenantsusersfile_path_idowner_user_idtenant_idwho_assigned_user_idfile_path_idowner_team_idtenant_idwho_assigned_team_id

Relationships

assigned_owners

6 cols · no RLS · line 928View in 3D

Table for ownership assignments, one entry contains an assigned user ID, which repo_path is assigned and the date and user who assigned the owner.

  • Foreign key without index owner_user_id — 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.
  • Foreign key without index tenant_id — 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.
  • Foreign key without index who_assigned_user_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Nullable foreign key who_assigned_user_id — 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.
  • Either/or foreign keys without a CHECK tenant_id, who_assigned_user_id — 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.
  • TIMESTAMP without time zone assigned_at — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
owner_user_id integerNOT NULLusers.id 1:N · CASCADE
file_path_id integerNOT NULLrepo_paths.id 1:N
who_assigned_user_id integernullusers.id 1:N · SET NULL
assigned_at timestampNOT NULLnow()
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • UNIQUE (file_path_id, owner_user_id) assigned_owners_file_path_owner

Referenced by

  • nothing

assigned_teams

6 cols · no RLS · line 949View in 3D

Table for team ownership assignments, one entry contains an assigned team ID, which repo_path is assigned and the date and user who assigned the owner team.

  • Foreign key without index owner_team_id — 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.
  • Foreign key without index tenant_id — 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.
  • Foreign key without index who_assigned_team_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Nullable foreign key who_assigned_team_id — 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.
  • Either/or foreign keys without a CHECK tenant_id, who_assigned_team_id — 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.
  • TIMESTAMP without time zone assigned_at — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
owner_team_id integerNOT NULLteams.id 1:N · CASCADE
file_path_id integerNOT NULLrepo_paths.id 1:N
who_assigned_team_id integernullusers.id 1:N · SET NULL
assigned_at timestampNOT NULLnow()
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • UNIQUE (file_path_id, owner_team_id) assigned_teams_file_path_owner

Referenced by

  • nothing

batch 9 tables

Tables whose names start with batch_.

Entity-relationship diagram: batch_changes, batch_changes_site_credentials, batch_spec_execution_cache_entries, batch_spec_resolution_jobs, batch_spec_workspace_execution_jobs, batch_spec_workspace_execution_last_dequeues, batch_spec_workspace_files, batch_spec_workspaces, batch_specsbatch_changesid PKbigintnametextdescriptiontextcreator_id FKintegernamespace_user_id FKintegernamespace_org_id FKintegercreated_attimestamptzupdated_attimestamptzclosed_attimestamptzbatch_spec_id FKbigintlast_applier_id FKbigintlast_applied_attimestamptztenant_id FKintegerbatch_changes_site_credentialsid PKbigintexternal_service_typetextexternal_service_idtextcreated_attimestamptzupdated_attimestamptzcredentialbyteaencryption_key_idtextgithub_app_id FKintegertenant_id FKintegerbatch_spec_execution_cache_entriesid PKbigintkeytextvaluetextversionintegerlast_used_attimestamptzcreated_attimestamptzuser_id FKintegertenant_id FKintegerbatch_spec_resolution_jobsid PKbigintbatch_spec_id FKintegerstatetextfailure_messagetextstarted_attimestamptzfinished_attimestamptzprocess_aftertimestamptznum_resetsintegernum_failuresintegerexecution_logsjson[]worker_hostnametextlast_heartbeat_attimestamptzcreated_attimestamptzupdated_attimestamptzqueued_attimestamptzinitiator_id FKintegercancelbooleantenant_id FKintegerbatch_spec_workspace_execution_jobsid PKbigintbatch_spec_workspace_id FKintegerstatetextfailure_messagetextstarted_attimestamptzfinished_attimestamptzprocess_aftertimestamptznum_resetsintegernum_failuresintegerexecution_logsjson[]worker_hostnametextlast_heartbeat_attimestamptzcreated_attimestamptzupdated_attimestamptzcancelbooleanqueued_attimestamptzuser_idintegerversionintegertenant_id FKintegerbatch_spec_workspace_execution_last_dequeuesuser_id PKintegerlatest_dequeuetimestamptztenant_id FKintegerbatch_spec_workspace_filesid PKintegerrand_idtextbatch_spec_id FKbigintfilenametextpathtextsizebigintcontentbyteamodified_attimestamptzcreated_attimestamptzupdated_attimestamptztenant_id FKintegerbatch_spec_workspacesid PKbigintbatch_spec_id FKintegerchangeset_spec_idsjsonbrepo_id FKintegerbranchtextcommittextpathtextfile_matchestext[]only_fetch_workspacebooleancreated_attimestamptzupdated_attimestamptzignoredbooleanunsupportedbooleanskippedbooleancached_result_foundbooleanstep_cache_resultsjsonbtenant_id FKintegerbatch_specsid PKbigintrand_idtextraw_spectextspecjsonbnamespace_user_idintegernamespace_org_idintegeruser_id FKintegercreated_attimestamptzupdated_attimestamptzcreated_from_rawbooleanallow_unsupportedbooleanallow_ignoredbooleanno_cachebooleanbatch_change_id FKbiginttenant_id FKintegergithub_appsorgsrepotenantsusersbatch_spec_idcreator_idlast_applier_idnamespace_org_idnamespace_user_idtenant_idgithub_app_idtenant_idtenant_iduser_idtenant_iduser_idbatch_change_idtenant_iduser_idbatch_spec_idinitiator_idtenant_idbatch_spec_idtenant_idbatch_spec_idrepo_idtenant_idbatch_spec_workspace_idtenant_id

Relationships

batch_changes

13 cols · no RLS · line 970View in 3D
  • Foreign key without index batch_spec_id — 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.
  • Foreign key without index creator_id — 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.
  • Foreign key without index last_applier_id — 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.
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key creator_id — 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.
  • Nullable foreign key last_applier_id — 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.
  • Nullable foreign key namespace_org_id — 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.
  • Nullable foreign key namespace_user_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Either/or foreign keys without a CHECK creator_id, last_applier_id, namespace_org_id, namespace_user_id, tenant_id — 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.
  • 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.
ColumnTypeNullDefaultReferencesNotes
id PKbigintNOT NULL
name textNOT NULLCHECK name ~ CAST('^[\w.-]+$' AS text)
description textnull
creator_id integernullusers.id 1:N · SET NULL
namespace_user_id integernullusers.id 1:N · CASCADE
namespace_org_id integernullorgs.id 1:N · CASCADE
created_at timestamptzNOT NULLnow()
updated_at timestamptzNOT NULLnow()
closed_at timestamptznull
batch_spec_id bigintNOT NULLbatch_specs.id 1:N
last_applier_id bigintnullusers.id 1:N · SET NULL
last_applied_at timestamptznull
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (namespace_org_id) batch_changes_namespace_org_id
  • (namespace_user_id) batch_changes_namespace_user_id
  • UNIQUE (name, namespace_org_id) WHERE namespace_org_id IS NOT NULL batch_changes_unique_org_id
  • UNIQUE (name, namespace_user_id) WHERE namespace_user_id IS NOT NULL batch_changes_unique_user_id

Referenced by

Table checks: name ~ CAST('^[\w.-]+$' AS text); namespace_user_id IS NULL <> namespace_org_id IS NULL; name <> CAST('' AS text)

batch_changes_site_credentials

9 cols · no RLS · line 998View in 3D
  • Foreign key without index github_app_id — 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.
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key github_app_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Enum-like column with no CHECK external_service_type — 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.
  • Polymorphic reference without referential integrity external_service_type, external_service_id — 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.
  • Either/or foreign keys without a CHECK github_app_id, tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKbigintNOT NULL
external_service_type textNOT NULL
external_service_id textNOT NULL
created_at timestamptzNOT NULLnow()
updated_at timestamptzNOT NULLnow()
credential byteaNOT NULL
encryption_key_id textNOT NULLCAST('' AS text)
github_app_id integernullgithub_apps.id 1:N · CASCADE
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (encryption_key_id = ANY(ARRAY[CAST('' AS text), CAST('previously-migrated' AS text)])) batch_changes_site_credentials_credential_idx
  • UNIQUE (external_service_type, external_service_id) batch_changes_site_credentials_unique

Referenced by

  • nothing

Table checks: github_app_id IS NULL OR external_service_type = CAST('github' AS text)

batch_spec_execution_cache_entries

8 cols · no RLS · line 1020View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKbigintNOT NULL
key textNOT NULL
value textNOT NULL
version integerNOT NULL
last_used_at timestamptznull
created_at timestamptzNOT NULLnow()
user_id integerNOT NULLusers.id 1:N · CASCADE
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

  • nothing

batch_spec_resolution_jobs

18 cols · no RLS · line 1040View in 3D
  • Foreign key without index initiator_id — 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.
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Enum-like column with no CHECK state — 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.
ColumnTypeNullDefaultReferencesNotes
id PKbigintNOT NULL
batch_spec_id UKintegerNOT NULLbatch_specs.id 1:1 · CASCADE
state textNOT NULLCAST('queued' AS text)
failure_message textnull
started_at timestamptznull
finished_at timestamptznull
process_after timestamptznull
num_resets integerNOT NULL0
num_failures integerNOT NULL0
execution_logs json[]null
worker_hostname textNOT NULLCAST('' AS text)
last_heartbeat_at timestamptznull
created_at timestamptzNOT NULLnow()
updated_at timestamptzNOT NULLnow()
queued_at timestamptznullnow()
initiator_id integerNOT NULLusers.id 1:N · CASCADE
cancel booleanNOT NULLFALSE
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (state) batch_spec_resolution_jobs_state

Referenced by

  • nothing

batch_spec_workspace_execution_jobs

19 cols · no RLS · line 1070View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Enum-like column with no CHECK state — 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.
ColumnTypeNullDefaultReferencesNotes
id PKbigintNOT NULL
batch_spec_workspace_id integerNOT NULLbatch_spec_workspaces.id 1:N · CASCADE
state textNOT NULLCAST('queued' AS text)
failure_message textnull
started_at timestamptznull
finished_at timestamptznull
process_after timestamptznull
num_resets integerNOT NULL0
num_failures integerNOT NULL0
execution_logs json[]null
worker_hostname textNOT NULLCAST('' AS text)
last_heartbeat_at timestamptznull
created_at timestamptzNOT NULLnow()
updated_at timestamptzNOT NULLnow()
cancel booleanNOT NULLFALSE
queued_at timestamptznullnow()
user_id integerNOT NULL
version integerNOT NULL1
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (batch_spec_workspace_id) batch_spec_workspace_execution_jobs_batch_spec_workspace_id
  • (cancel) batch_spec_workspace_execution_jobs_cancel
  • (user_id, started_at) batch_spec_workspace_execution_jobs_last_dequeue
  • (state) batch_spec_workspace_execution_jobs_state

Referenced by

  • nothing

batch_spec_workspace_execution_last_dequeues

3 cols · no RLS · line 1101View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
user_id PKintegerNOT NULLusers.id 1:1 · CASCADE
latest_dequeue timestamptznull
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

  • nothing

batch_spec_workspace_files

11 cols · no RLS · line 1145View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
rand_id textNOT NULL
batch_spec_id bigintNOT NULLbatch_specs.id 1:N · CASCADE
filename textNOT NULL
path textNOT NULL
size bigintNOT NULL
content byteaNOT NULL
modified_at timestamptzNOT NULL
created_at timestamptzNOT NULLnow()
updated_at timestamptzNOT NULLnow()
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • UNIQUE (batch_spec_id, filename, path) batch_spec_workspace_files_batch_spec_id_filename_path
  • (rand_id) batch_spec_workspace_files_rand_id

Referenced by

  • nothing

batch_spec_workspaces

17 cols · no RLS · line 1169View in 3D
  • Foreign key without index repo_id — 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.
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKbigintNOT NULL
batch_spec_id integerNOT NULLbatch_specs.id 1:N · CASCADE
changeset_spec_ids jsonbNOT NULLCAST('{}' AS jsonb)
repo_id integerNOT NULLrepo.id 1:N
branch textNOT NULL
commit textNOT NULL
path textNOT NULL
file_matches text[]NOT NULL
only_fetch_workspace booleanNOT NULLFALSE
created_at timestamptzNOT NULLnow()
updated_at timestamptzNOT NULLnow()
ignored booleanNOT NULLFALSE
unsupported booleanNOT NULLFALSE
skipped booleanNOT NULLFALSE
cached_result_found booleanNOT NULLFALSE
step_cache_results jsonbNOT NULLCAST('{}' AS jsonb)
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (batch_spec_id) batch_spec_workspaces_batch_spec_id
  • (id, batch_spec_id) batch_spec_workspaces_id_batch_spec_id

Referenced by

batch_specs

15 cols · no RLS · line 1198View in 3D
  • Foreign key without index batch_change_id — 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.
  • Foreign key without index tenant_id — 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.
  • Foreign key without index user_id — 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.
  • Nullable foreign key batch_change_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Nullable foreign key user_id — 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.
  • Either/or foreign keys without a CHECK batch_change_id, tenant_id, user_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKbigintNOT NULL
rand_id textNOT NULL
raw_spec textNOT NULL
spec jsonbNOT NULLCAST('{}' AS jsonb)
namespace_user_id integernull
namespace_org_id integernull
user_id integernullusers.id 1:N · SET NULL
created_at timestamptzNOT NULLnow()
updated_at timestamptzNOT NULLnow()
created_from_raw booleanNOT NULLFALSE
allow_unsupported booleanNOT NULLFALSE
allow_ignored booleanNOT NULLFALSE
no_cache booleanNOT NULLFALSE
batch_change_id bigintnullbatch_changes.id 1:N · SET NULL
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • UNIQUE (rand_id) batch_specs_unique_rand_id

Referenced by

Table checks: namespace_user_id IS NULL <> namespace_org_id IS NULL

changeset 3 tables

Tables whose names start with changeset_.

Entity-relationship diagram: changeset_specs, changeset_events, changeset_jobschangeset_specsid PKbigintrand_idtextspecjsonbbatch_spec_id FKbigintrepo_id FKintegeruser_id FKintegerdiff_stat_addedintegerdiff_stat_deletedintegercreated_attimestamptzupdated_attimestamptzhead_reftexttitletextexternal_idtextfork_namespacecitextdiffbyteabase_revtextbase_reftextbodytextpublishedtextcommit_messagetextcommit_author_nametextcommit_author_emailtexttypetexttenant_id FKintegerchangeset_eventsid PKbigintchangeset_id FKbigintkindtextkeytextcreated_attimestamptzmetadatajsonbupdated_attimestamptztenant_id FKintegerchangeset_jobsid PKbigintbulk_grouptextuser_id FKintegerbatch_change_id FKintegerchangeset_id FKintegerjob_typetextpayloadjsonbstatetextfailure_messagetextstarted_attimestamptzfinished_attimestamptzprocess_aftertimestamptznum_resetsintegernum_failuresintegerexecution_logsjson[]created_attimestamptzupdated_attimestamptzworker_hostnametextlast_heartbeat_attimestamptzqueued_attimestamptzcancelbooleantenant_id FKintegerbatch_changesbatch_specschangesetsrepotenantsuserschangeset_idtenant_idbatch_change_idchangeset_idtenant_iduser_idbatch_spec_idrepo_idtenant_iduser_id

Relationships

changeset_specs

24 cols · no RLS · line 1226View in 3D
  • Foreign key without index repo_id — 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.
  • Foreign key without index tenant_id — 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.
  • Foreign key without index user_id — 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.
  • Nullable foreign key batch_spec_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Nullable foreign key user_id — 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.
  • Enum-like column with no CHECK type — 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.
  • Either/or foreign keys without a CHECK batch_spec_id, tenant_id, user_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKbigintNOT NULL
rand_id textNOT NULL
spec jsonbnullCAST('{}' AS jsonb)
batch_spec_id bigintnullbatch_specs.id 1:N · CASCADE
repo_id integerNOT NULLrepo.id 1:N
user_id integernullusers.id 1:N · SET NULL
diff_stat_added integernull
diff_stat_deleted integernull
created_at timestamptzNOT NULLnow()
updated_at timestamptzNOT NULLnow()
head_ref textnull
title textnull
external_id textnull
fork_namespace citextnull
diff byteanull
base_rev textnull
base_ref textnull
body textnull
published textnullCHECK published = CAST('true' AS text) OR published = CAST('false' AS text) OR published = CAST('"draft"' AS text) OR published IS NULL
commit_message textnull
commit_author_name textnull
commit_author_email textnull
type textNOT NULL
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (batch_spec_id) changeset_specs_batch_spec_id
  • (created_at) changeset_specs_created_at
  • (external_id) changeset_specs_external_id
  • (head_ref) changeset_specs_head_ref
  • (title) changeset_specs_title
  • UNIQUE (rand_id) changeset_specs_unique_rand_id

Referenced by

Table checks: published = CAST('true' AS text) OR published = CAST('false' AS text) OR published = CAST('"draft"' AS text) OR published IS NULL

changeset_events

8 cols · no RLS · line 1369View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKbigintNOT NULL
changeset_id bigintNOT NULLchangesets.id 1:N · CASCADE
kind textNOT NULLCHECK kind <> CAST('' AS text)
key textNOT NULLCHECK key <> CAST('' AS text)
created_at timestamptzNOT NULLnow()
metadata jsonbNOT NULLCAST('{}' AS jsonb)CHECK jsonb_typeof(metadata) = CAST('object' AS text)
updated_at timestamptzNOT NULLnow()
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

  • nothing

Table checks: key <> CAST('' AS text); kind <> CAST('' AS text); jsonb_typeof(metadata) = CAST('object' AS text)

changeset_jobs

22 cols · no RLS · line 1392View in 3D
  • Foreign key without index batch_change_id — 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.
  • Foreign key without index changeset_id — 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.
  • Foreign key without index tenant_id — 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.
  • Foreign key without index user_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Enum-like column with no CHECK job_type — 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.
  • Enum-like column with no CHECK state — 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.
ColumnTypeNullDefaultReferencesNotes
id PKbigintNOT NULL
bulk_group textNOT NULL
user_id integerNOT NULLusers.id 1:N · CASCADE
batch_change_id integerNOT NULLbatch_changes.id 1:N · CASCADE
changeset_id integerNOT NULLchangesets.id 1:N · CASCADE
job_type textNOT NULL
payload jsonbnullCAST('{}' AS jsonb)CHECK jsonb_typeof(payload) = CAST('object' AS text)
state textNOT NULLCAST('queued' AS text)
failure_message textnull
started_at timestamptznull
finished_at timestamptznull
process_after timestamptznull
num_resets integerNOT NULL0
num_failures integerNOT NULL0
execution_logs json[]null
created_at timestamptzNOT NULLnow()
updated_at timestamptzNOT NULLnow()
worker_hostname textNOT NULLCAST('' AS text)
last_heartbeat_at timestamptznull
queued_at timestamptznullnow()
cancel booleanNOT NULLFALSE
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (bulk_group) changeset_jobs_bulk_group_idx
  • (state) changeset_jobs_state_idx

Referenced by

  • nothing

Table checks: jsonb_typeof(payload) = CAST('object' AS text)

repo 9 tables

Tables whose names start with repo_.

Entity-relationship diagram: repo, repo_commits_changelists, repo_embedding_job_stats, repo_embedding_jobs, repo_kvps, repo_paths, repo_pending_permissions, repo_permissions, repo_statisticsrepoid PKintegernamecitextdescriptiontextforkbooleancreated_attimestamptzupdated_attimestamptzexternal_idtextexternal_service_typetextexternal_service_idtextarchivedbooleanuricitextdeleted_attimestamptzmetadatajsonbprivatebooleanstarsintegerblockedjsonbtopicstext[]tenant_id FKintegerrepo_commits_changelistsid PKintegerrepo_id FKintegercommit_shabyteaperforce_changelist_idintegercreated_attimestamptztenant_id FKintegerrepo_embedding_job_statsjob_id PKintegeris_incrementalbooleancode_files_totalintegercode_files_embeddedintegercode_chunks_embeddedintegercode_files_skippedjsonbcode_bytes_embeddedbiginttext_files_totalintegertext_files_embeddedintegertext_chunks_embeddedintegertext_files_skippedjsonbtext_bytes_embeddedbigintcode_chunks_excludedintegertext_chunks_excludedintegertenant_id FKintegerrepo_embedding_jobsid PKintegerstatetextfailure_messagetextqueued_attimestamptzstarted_attimestamptzfinished_attimestamptzprocess_aftertimestamptznum_resetsintegernum_failuresintegerlast_heartbeat_attimestamptzexecution_logsjson[]worker_hostnametextcancelbooleanrepo_idintegerrevisiontexttenant_id FKintegerrepo_kvpsrepo_id PKintegerkey PKtextvaluetexttenant_id FKintegerrepo_pathsid PKintegerrepo_id FKintegerabsolute_pathtextparent_id FKintegertree_files_countintegertree_files_counts_updated_attimestamptenant_id FKintegerrepo_pending_permissionsrepo_idintegerpermissiontextupdated_attimestamptzuser_ids_intsbigint[]tenant_id FKintegerrepo_permissionsrepo_idintegerpermissiontextupdated_attimestamptzsynced_attimestamptzuser_ids_intsinteger[]unrestrictedbooleantenant_id FKintegerrepo_statisticstotalbigintsoft_deletedbigintnot_clonedbigintcloningbigintclonedbigintfailed_fetchbigintcorruptedbiginttenant_id FKintegertenantsparent_idtenant_idtenant_idtenant_idtenant_idtenant_idrepo_idtenant_idjob_idtenant_idrepo_idtenant_idrepo_idtenant_id

Relationships

repo

18 cols · no RLS · line 1309View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Enum-like column with no CHECK external_service_type — 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.
  • Soft delete collides with non-partial UNIQUE external_service_type, external_service_id, external_id — 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.
  • Soft delete collides with non-partial UNIQUE name — 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.
  • Polymorphic reference without referential integrity external_service_type, external_service_id — 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.
  • 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
name UKcitextNOT NULLCHECK name <> CAST('' AS citext)
description textnull
fork booleanNOT NULLFALSE
created_at timestamptzNOT NULLnow()
updated_at timestamptznull
external_id textnull
external_service_type textnull
external_service_id textnull
archived booleanNOT NULLFALSE
uri citextnull
deleted_at timestamptznull
metadata jsonbNOT NULLCAST('{}' AS jsonb)CHECK jsonb_typeof(metadata) = CAST('object' AS text)
private booleanNOT NULLFALSE
stars integerNOT NULL0
blocked jsonbnull
topics text[]null
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (topics) idx_repo_topics
  • (archived) repo_archived
  • (blocked IS NOT NULL) repo_blocked_idx
  • (created_at) repo_created_at
  • (lower(description)) repo_description_trgm_idx
  • (stars) WHERE deleted_at IS NULL AND blocked IS NULL AND ((stars >= 5 AND NOT COALESCE(fork, FALSE) AND NOT archived) OR lower(CAST(name AS text)) ~ CAST('^(src\.fedoraproject\.org|maven|npm|jdk)' AS text)) repo_dotcom_indexable_repos_idx
  • UNIQUE (external_service_type, external_service_id, external_id) repo_external_unique_idx
  • (fork) repo_fork
  • (sha256(CAST(lower(CAST(name AS text)) AS bytea))) WHERE deleted_at IS NULL repo_hashed_name_idx
  • (blocked IS NULL) repo_is_not_blocked_idx
  • (metadata) repo_metadata_gin_idx
  • (CAST(name AS text)) repo_name_case_sensitive_trgm_idx
  • (lower(CAST(name AS text))) repo_name_idx
  • (lower(CAST(name AS text))) repo_name_trgm
  • (id, name) WHERE deleted_at IS NULL repo_non_deleted_id_name_idx
  • (private) repo_private
  • (stars, id) WHERE deleted_at IS NULL AND blocked IS NULL repo_stars_desc_id_desc_idx
  • (stars) repo_stars_idx
  • (uri) repo_uri_idx

Referenced by

Table checks: name <> CAST('' AS citext); jsonb_typeof(metadata) = CAST('object' AS text)

repo_commits_changelists

6 cols · no RLS · line 4480View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
repo_id integerNOT NULLrepo.id 1:N · CASCADE
commit_sha byteaNOT NULL
perforce_changelist_id integerNOT NULL
created_at timestamptzNOT NULLnow()
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • UNIQUE (repo_id, perforce_changelist_id) repo_id_perforce_changelist_id_unique

Referenced by

  • nothing

repo_embedding_job_stats

15 cols · no RLS · line 4499View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
job_id PKintegerNOT NULLrepo_embedding_jobs.id 1:1 · CASCADE
is_incremental booleanNOT NULLFALSE
code_files_total integerNOT NULL0
code_files_embedded integerNOT NULL0
code_chunks_embedded integerNOT NULL0
code_files_skipped jsonbNOT NULLCAST('{}' AS jsonb)
code_bytes_embedded bigintNOT NULL0
text_files_total integerNOT NULL0
text_files_embedded integerNOT NULL0
text_chunks_embedded integerNOT NULL0
text_files_skipped jsonbNOT NULLCAST('{}' AS jsonb)
text_bytes_embedded bigintNOT NULL0
code_chunks_excluded integerNOT NULL0
text_chunks_excluded integerNOT NULL0
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

  • nothing

repo_embedding_jobs

16 cols · no RLS · line 4517View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Enum-like column with no CHECK state — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
state textnullCAST('queued' AS text)
failure_message textnull
queued_at timestamptznullnow()
started_at timestamptznull
finished_at timestamptznull
process_after timestamptznull
num_resets integerNOT NULL0
num_failures integerNOT NULL0
last_heartbeat_at timestamptznull
execution_logs json[]null
worker_hostname textNOT NULLCAST('' AS text)
cancel booleanNOT NULLFALSE
repo_id integerNOT NULL
revision textNOT NULL
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (repo_id, revision) repo_embedding_jobs_repo

Referenced by

repo_kvps

4 cols · no RLS · line 4555View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
repo_id PKintegerNOT NULLrepo.id 1:N · CASCADE
key PKtextNOT NULL
value textnull
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (key, value) repo_kvps_trgm_idx

Referenced by

  • nothing

repo_paths

7 cols · no RLS · line 4562View in 3D
  • Foreign key without index parent_id — 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.
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key parent_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Either/or foreign keys without a CHECK parent_id, tenant_id — 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.
  • TIMESTAMP without time zone tree_files_counts_updated_at — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
repo_id integerNOT NULLrepo.id 1:N · CASCADE
absolute_path textNOT NULLAbsolute path does not start or end with forward slash. Example: "a/b/c". Root directory is empty path "".
parent_id integernullrepo_paths.id 1:N
tree_files_count integernullTotal count of files in the file tree rooted at the path. 1 for files.
tree_files_counts_updated_at timestampnullTimestamp of the job that updated the file counts
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • UNIQUE (repo_id, absolute_path) repo_paths_index_absolute_path

Referenced by

repo_pending_permissions

5 cols · no RLS · line 4588View in 3D
  • 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.
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
repo_id integerNOT NULL
permission textNOT NULL
updated_at timestamptzNOT NULL
user_ids_ints bigint[]NOT NULLCAST('{}' AS integer[])
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

  • nothing

repo_permissions

7 cols · no RLS · line 4596View in 3D
  • 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.
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
repo_id integerNOT NULL
permission textNOT NULL
updated_at timestamptzNOT NULL
synced_at timestamptznull
user_ids_ints integer[]NOT NULLCAST('{}' AS integer[])
unrestricted booleanNOT NULLFALSE
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (unrestricted) WHERE unrestricted repo_permissions_unrestricted_true_idx

Referenced by

  • nothing

repo_statistics

8 cols · no RLS · line 4606View in 3D
  • 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.
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
total bigintNOT NULL0Number of repositories that are not soft-deleted and not blocked
soft_deleted bigintNOT NULL0Number of repositories that are soft-deleted and not blocked
not_cloned bigintNOT NULL0Number of repositories that are NOT soft-deleted and not blocked and not cloned by gitserver
cloning bigintNOT NULL0Number of repositories that are NOT soft-deleted and not blocked and currently being cloned by gitserver
cloned bigintNOT NULL0Number of repositories that are NOT soft-deleted and not blocked and cloned by gitserver
failed_fetch bigintNOT NULL0Number of repositories that are NOT soft-deleted and not blocked and have last_error set in gitserver_repos table
corrupted bigintNOT NULL0Number of repositories that are NOT soft-deleted and not blocked and have corrupted_at set in gitserver_repos table
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

  • nothing

cm 9 tables

Tables whose names start with cm_.

Entity-relationship diagram: cm_action_jobs, cm_emails, cm_last_searched, cm_monitors, cm_queries, cm_recipients, cm_slack_webhooks, cm_trigger_jobs, cm_webhookscm_action_jobsid PKintegeremail FKbigintstatetextfailure_messagetextstarted_attimestamptzfinished_attimestamptzprocess_aftertimestamptznum_resetsintegernum_failuresintegerlog_contentstexttrigger_event FKintegerworker_hostnametextlast_heartbeat_attimestamptzexecution_logsjson[]webhook FKbigintslack_webhook FKbigintqueued_attimestamptzcancelbooleantenant_id FKintegercm_emailsid PKbigintmonitor FKbigintenabledbooleanprioritycm_email_priorityheadertextcreated_by FKintegercreated_attimestamptzchanged_by FKintegerchanged_attimestamptzinclude_resultsbooleantenant_id FKintegercm_last_searchedmonitor_id PKbigintcommit_oidstext[]repo_id PKintegertenant_id FKintegercm_monitorsid PKbigintcreated_by FKintegercreated_attimestamptzdescriptiontextchanged_attimestamptzchanged_by FKintegerenabledbooleannamespace_user_id FKintegernamespace_org_id FKintegertenant_id FKintegercm_queriesid PKbigintmonitor FKbigintquerytextcreated_by FKintegercreated_attimestamptzchanged_by FKintegerchanged_attimestamptznext_runtimestamptzlatest_resulttimestamptztenant_id FKintegercm_recipientsid PKbigintemail FKbigintnamespace_user_id FKintegernamespace_org_id FKintegertenant_id FKintegercm_slack_webhooksid PKbigintmonitor FKbiginturltextenabledbooleancreated_by FKintegercreated_attimestamptzchanged_by FKintegerchanged_attimestamptzinclude_resultsbooleantenant_id FKintegercm_trigger_jobsid PKintegerquery FKbigintstatetextfailure_messagetextstarted_attimestamptzfinished_attimestamptzprocess_aftertimestamptznum_resetsintegernum_failuresintegerlog_contentstextquery_stringtextworker_hostnametextlast_heartbeat_attimestamptzexecution_logsjson[]search_resultsjsonbqueued_attimestamptzcancelbooleanlogsjson[]tenant_id FKintegercm_webhooksid PKbigintmonitor FKbiginturltextenabledbooleancreated_by FKintegercreated_attimestamptzchanged_by FKintegerchanged_attimestamptzinclude_resultsbooleantenant_id FKintegerorgsrepotenantsuserschanged_bycreated_bynamespace_org_idtenant_idnamespace_user_idchanged_bycreated_bymonitortenant_idmonitor_idrepo_idtenant_idtenant_idchanged_bycreated_bymonitorchanged_bycreated_bymonitortenant_idchanged_bycreated_bymonitortenant_idemailnamespace_org_idtenant_idnamespace_user_idquerytenant_idemailslack_webhooktenant_idtrigger_eventwebhook

Relationships

cm_action_jobs

19 cols · no RLS · line 1445View in 3D
  • Foreign key without index email — 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.
  • Foreign key without index slack_webhook — 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.
  • Foreign key without index tenant_id — 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.
  • Foreign key without index webhook — 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.
  • Nullable foreign key email — 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.
  • Nullable foreign key slack_webhook — 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.
  • Nullable foreign key tenant_id — 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.
  • Nullable foreign key trigger_event — 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.
  • Nullable foreign key webhook — 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.
  • Enum-like column with no CHECK state — 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.
  • Either/or foreign keys without a CHECK email, slack_webhook, tenant_id, trigger_event, webhook — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
email bigintnullcm_emails.id 1:N · CASCADEThe ID of the cm_emails action to execute if this is an email job. Mutually exclusive with webhook and slack_webhook
state textnullCAST('queued' AS text)
failure_message textnull
started_at timestamptznull
finished_at timestamptznull
process_after timestamptznull
num_resets integerNOT NULL0
num_failures integerNOT NULL0
log_contents textnull
trigger_event integernullcm_trigger_jobs.id 1:N · CASCADE
worker_hostname textNOT NULLCAST('' AS text)
last_heartbeat_at timestamptznull
execution_logs json[]null
webhook bigintnullcm_webhooks.id 1:N · CASCADEThe ID of the cm_webhooks action to execute if this is a webhook job. Mutually exclusive with email and slack_webhook
slack_webhook bigintnullcm_slack_webhooks.id 1:N · CASCADEThe ID of the cm_slack_webhook action to execute if this is a slack webhook job. Mutually exclusive with email and webhook
queued_at timestamptznullnow()
cancel booleanNOT NULLFALSE
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (state) cm_action_jobs_state_idx
  • (trigger_event) cm_action_jobs_trigger_event

Referenced by

  • nothing

Table checks: ((CASE WHEN email IS NULL THEN 0 ELSE 1 END + CASE WHEN webhook IS NULL THEN 0 ELSE 1 END) + CASE WHEN slack_webhook IS NULL THEN 0 ELSE 1 END) = 1

cm_emails

11 cols · no RLS · line 1498View in 3D
  • Foreign key without index changed_by — 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.
  • Foreign key without index created_by — 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.
  • Foreign key without index monitor — 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.
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKbigintNOT NULL
monitor bigintNOT NULLcm_monitors.id 1:N · CASCADE
enabled booleanNOT NULL
priority cm_email_priorityNOT NULL
header textNOT NULL
created_by integerNOT NULLusers.id 1:N · CASCADE
created_at timestamptzNOT NULLnow()
changed_by integerNOT NULLusers.id 1:N · CASCADE
changed_at timestamptzNOT NULLnow()
include_results booleanNOT NULLFALSE
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

cm_last_searched

4 cols · no RLS · line 1521View in 3D

The last searched commit hashes for the given code monitor and unique set of search arguments

  • Foreign key without index repo_id — 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.
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
monitor_id PKbigintNOT NULLcm_monitors.id 1:N · CASCADE
commit_oids text[]NOT NULLThe set of commit OIDs that was previously successfully searched and should be excluded on the next run
repo_id PKintegerNOT NULLrepo.id 1:N · CASCADE
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

  • nothing

cm_monitors

10 cols · no RLS · line 1532View in 3D
  • Foreign key without index changed_by — 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.
  • Foreign key without index created_by — 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.
  • Foreign key without index namespace_org_id — 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.
  • Foreign key without index tenant_id — 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.
  • Foreign key without index namespace_user_id — 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.
  • Nullable foreign key namespace_org_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Either/or foreign keys without a CHECK namespace_org_id, tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKbigintNOT NULL
created_by integerNOT NULLusers.id 1:N · CASCADE
created_at timestamptzNOT NULLnow()
description textNOT NULL
changed_at timestamptzNOT NULLnow()
changed_by integerNOT NULLusers.id 1:N · CASCADE
enabled booleanNOT NULLTRUE
namespace_user_id integerNOT NULLusers.id 1:N · CASCADE
namespace_org_id integernullorgs.id 1:N · CASCADEDEPRECATED: code monitors cannot be owned by an org
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

cm_queries

10 cols · no RLS · line 1556View in 3D
  • Foreign key without index tenant_id — 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.
  • Foreign key without index changed_by — 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.
  • Foreign key without index created_by — 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.
  • Foreign key without index monitor — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKbigintNOT NULL
monitor bigintNOT NULLcm_monitors.id 1:N · CASCADE
query textNOT NULL
created_by integerNOT NULLusers.id 1:N · CASCADE
created_at timestamptzNOT NULLnow()
changed_by integerNOT NULLusers.id 1:N · CASCADE
changed_at timestamptzNOT NULLnow()
next_run timestamptznullnow()
latest_result timestamptznull
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

cm_recipients

5 cols · no RLS · line 1578View in 3D
  • Foreign key without index email — 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.
  • Foreign key without index namespace_org_id — 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.
  • Foreign key without index tenant_id — 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.
  • Foreign key without index namespace_user_id — 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.
  • Nullable foreign key namespace_org_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Nullable foreign key namespace_user_id — 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.
  • Either/or foreign keys without a CHECK namespace_org_id, tenant_id, namespace_user_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKbigintNOT NULL
email bigintNOT NULLcm_emails.id 1:N · CASCADE
namespace_user_id integernullusers.id 1:N · CASCADE
namespace_org_id integernullorgs.id 1:N · CASCADE
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

  • nothing

cm_slack_webhooks

10 cols · no RLS · line 1595View in 3D

Slack webhook actions configured on code monitors

  • Foreign key without index changed_by — 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.
  • Foreign key without index created_by — 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.
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKbigintNOT NULL
monitor bigintNOT NULLcm_monitors.id 1:N · CASCADEThe code monitor that the action is defined on
url textNOT NULLThe Slack webhook URL we send the code monitor event to
enabled booleanNOT NULL
created_by integerNOT NULLusers.id 1:N · CASCADE
created_at timestamptzNOT NULLnow()
changed_by integerNOT NULLusers.id 1:N · CASCADE
changed_at timestamptzNOT NULLnow()
include_results booleanNOT NULLFALSE
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (monitor) cm_slack_webhooks_monitor

Referenced by

cm_trigger_jobs

19 cols · no RLS · line 1623View in 3D
  • Foreign key without index query — 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.
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Enum-like column with no CHECK state — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
query bigintNOT NULLcm_queries.id 1:N · CASCADE
state textnullCAST('queued' AS text)
failure_message textnull
started_at timestamptznull
finished_at timestamptznull
process_after timestamptznull
num_resets integerNOT NULL0
num_failures integerNOT NULL0
log_contents textnull
query_string textnull
worker_hostname textNOT NULLCAST('' AS text)
last_heartbeat_at timestamptznull
execution_logs json[]null
search_results jsonbnullCHECK jsonb_typeof(search_results) = CAST('array' AS text)
queued_at timestamptznullnow()
cancel booleanNOT NULLFALSE
logs json[]null
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (finished_at) cm_trigger_jobs_finished_at
  • (state) cm_trigger_jobs_state_idx

Referenced by

Table checks: jsonb_typeof(search_results) = CAST('array' AS text)

cm_webhooks

10 cols · no RLS · line 1656View in 3D

Webhook actions configured on code monitors

  • Foreign key without index changed_by — 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.
  • Foreign key without index created_by — 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.
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKbigintNOT NULL
monitor bigintNOT NULLcm_monitors.id 1:N · CASCADEThe code monitor that the action is defined on
url textNOT NULLThe webhook URL we send the code monitor event to
enabled booleanNOT NULLWhether this Slack webhook action is enabled. When not enabled, the action will not be run when its code monitor generates events
created_by integerNOT NULLusers.id 1:N · CASCADE
created_at timestamptzNOT NULLnow()
changed_by integerNOT NULLusers.id 1:N · CASCADE
changed_at timestamptzNOT NULLnow()
include_results booleanNOT NULLFALSE
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (monitor) cm_webhooks_monitor

Referenced by

codeintel 15 tables

Tables whose names start with codeintel_.

Entity-relationship diagram: codeintel_autoindex_queue, codeintel_autoindexing_exceptions, codeintel_commit_dates, codeintel_inference_scripts, codeintel_initial_path_ranks, codeintel_initial_path_ranks_processed, codeintel_langugage_support_requests, codeintel_path_ranks, codeintel_ranking_definitions, codeintel_ranking_exports, codeintel_ranking_graph_keys, codeintel_ranking_path_counts_inputs, codeintel_ranking_progress, codeintel_ranking_references, codeintel_ranking_references_processedcodeintel_autoindex_queueid PKintegerrepository_idintegerrevtextqueued_attimestamptzprocessed_attimestamptztenant_id FKintegercodeintel_autoindexing_exceptionsid PKintegerrepository_id FKintegerdisable_schedulingbooleandisable_inferencebooleantenant_id FKintegercodeintel_commit_datesrepository_id PKintegercommit_bytea PKbyteacommitted_attimestamptztenant_id FKintegercodeintel_inference_scriptsinsert_timestamptimestamptzscripttexttenant_id FKintegercodeintel_initial_path_ranksid PKbigintdocument_pathtextgraph_keytextdocument_pathstext[]exported_upload_id FKintegertenant_id FKintegercodeintel_initial_path_ranks_processedid PKbigintgraph_keytextcodeintel_initial_path_ranks_id FKbiginttenant_id FKintegercodeintel_langugage_support_requestsidintegeruser_idintegerlanguage_idtexttenant_id FKintegercodeintel_path_ranksrepository_idintegerpayloadjsonbupdated_attimestamptzgraph_keytextnum_pathsintegerrefcount_logsumdouble precisionid PKbiginttenant_id FKintegercodeintel_ranking_definitionsid PKbigintsymbol_nametextdocument_pathtextgraph_keytextexported_upload_id FKintegersymbol_checksumbyteatenant_id FKintegercodeintel_ranking_exportsupload_id FKintegergraph_keytextlocked_attimestamptzid PKintegerlast_scanned_attimestamptzdeleted_attimestamptzupload_keytexttenant_id FKintegercodeintel_ranking_graph_keysid PKintegergraph_keytextcreated_attimestamptztenant_id FKintegercodeintel_ranking_path_counts_inputsid PKbigintcountintegergraph_keytextprocessedbooleandefinition_idbiginttenant_id FKintegercodeintel_ranking_progressid PKbigintgraph_keytextmappers_started_attimestamptzmapper_completed_attimestamptzseed_mapper_completed_attimestamptzreducer_started_attimestamptzreducer_completed_attimestamptznum_path_records_totalintegernum_reference_records_totalintegernum_count_records_totalintegernum_path_records_processedintegernum_reference_records_processedintegernum_count_records_processedintegermax_export_idbigintreference_cursor_export_deleted_attimestamptzreference_cursor_export_idintegerpath_cursor_deleted_export_attimestamptzpath_cursor_export_idintegertenant_id FKintegercodeintel_ranking_referencesid PKbigintsymbol_namestext[]graph_keytextexported_upload_id FKintegersymbol_checksumsbytea[]tenant_id FKintegercodeintel_ranking_references_processedgraph_keytextcodeintel_ranking_reference_id FKintegerid PKbiginttenant_id FKintegerlsif_uploadsrepotenantstenant_idrepository_idtenant_idtenant_idtenant_idtenant_idtenant_idtenant_idupload_idtenant_idtenant_idtenant_idexported_upload_idtenant_idexported_upload_idtenant_idexported_upload_idtenant_idtenant_idcodeintel_initial_path_ranks_idtenant_idcodeintel_ranking_reference_id

Relationships

codeintel_autoindex_queue

6 cols · no RLS · line 1709View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
repository_id integerNOT NULL
rev textNOT NULL
queued_at timestamptzNOT NULLnow()
processed_at timestamptznull
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • UNIQUE (repository_id, rev) codeintel_autoindex_queue_repository_id_commit

Referenced by

  • nothing

codeintel_autoindexing_exceptions

5 cols · no RLS · line 1728View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
repository_id UKintegerNOT NULLrepo.id 1:1 · CASCADE
disable_scheduling booleanNOT NULLFALSE
disable_inference booleanNOT NULLFALSE
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

  • nothing

codeintel_commit_dates

4 cols · no RLS · line 1746View in 3D

Maps commits within a repository to the commit date as reported by gitserver.

  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
repository_id PKintegerNOT NULLIdentifies a row in the `repo` table.
commit_bytea PKbyteaNOT NULLIdentifies the 40-character commit hash.
committed_at timestamptznullThe commit date (may be -infinity if unresolvable).
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

  • nothing

codeintel_inference_scripts

3 cols · no RLS · line 1836View in 3D

Contains auto-index job inference Lua scripts as an alternative to setting via environment variables.

  • 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.
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
insert_timestamp timestamptzNOT NULLnow()
script textNOT NULL
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

  • nothing

codeintel_initial_path_ranks

6 cols · no RLS · line 1844View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKbigintNOT NULL
document_path textNOT NULLCAST('' AS text)
graph_key textNOT NULL
document_paths text[]NOT NULLCAST('{}' AS text[])
exported_upload_id integerNOT NULLcodeintel_ranking_exports.id 1:N · CASCADE
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (exported_upload_id) codeintel_initial_path_ranks_exported_upload_id
  • (graph_key, id) codeintel_initial_path_ranks_graph_key_id

Referenced by

codeintel_initial_path_ranks_processed

4 cols · no RLS · line 1862View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKbigintNOT NULL
graph_key textNOT NULL
codeintel_initial_path_ranks_id bigintNOT NULLcodeintel_initial_path_ranks.id 1:N · CASCADE
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • UNIQUE (graph_key, codeintel_initial_path_ranks_id) codeintel_initial_path_ranks_processed_cgraph_key_codeintel_ini
  • (codeintel_initial_path_ranks_id) codeintel_initial_path_ranks_processed_codeintel_initial_path_r

Referenced by

  • nothing

codeintel_langugage_support_requests

4 cols · no RLS · line 1878View in 3D
  • 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.
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id integerNOT NULL
user_id integerNOT NULL
language_id textNOT NULL
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • UNIQUE (user_id, language_id) codeintel_langugage_support_requests_user_id_language

Referenced by

  • nothing

codeintel_path_ranks

8 cols · no RLS · line 1895View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
repository_id integerNOT NULL
payload jsonbNOT NULL
updated_at timestamptzNOT NULLnow()
graph_key textNOT NULL
num_paths integernull
refcount_logsum double precisionnull
id PKbigintNOT NULL
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (graph_key, updated_at, id) codeintel_path_ranks_graph_key
  • UNIQUE (graph_key, repository_id) codeintel_path_ranks_graph_key_repository_id
  • (repository_id, updated_at, id) codeintel_path_ranks_repository_id_updated_at_id

Referenced by

  • nothing

codeintel_ranking_definitions

7 cols · no RLS · line 1915View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKbigintNOT NULL
symbol_name textNOT NULL
document_path textNOT NULL
graph_key textNOT NULL
exported_upload_id integerNOT NULLcodeintel_ranking_exports.id 1:N · CASCADE
symbol_checksum byteaNOT NULLCAST('\x' AS bytea)
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (exported_upload_id) codeintel_ranking_definitions_exported_upload_id
  • (graph_key, symbol_checksum, exported_upload_id, document_path) codeintel_ranking_definitions_graph_key_symbol_checksum_search

Referenced by

  • nothing

codeintel_ranking_exports

8 cols · no RLS · line 1934View in 3D
  • Foreign key without index tenant_id — 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.
  • Foreign key without index upload_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Nullable foreign key upload_id — 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.
  • Soft delete collides with non-partial UNIQUE graph_key, upload_id — 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.
  • Either/or foreign keys without a CHECK tenant_id, upload_id — 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.
ColumnTypeNullDefaultReferencesNotes
upload_id integernulllsif_uploads.id 1:N · SET NULL
graph_key textNOT NULL
locked_at timestamptzNOT NULLnow()
id PKintegerNOT NULL
last_scanned_at timestamptznull
deleted_at timestamptznull
upload_key textnull
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (graph_key, deleted_at, id) codeintel_ranking_exports_graph_key_deleted_at_id
  • (graph_key, last_scanned_at, id) codeintel_ranking_exports_graph_key_last_scanned_at
  • UNIQUE (graph_key, upload_id) codeintel_ranking_exports_graph_key_upload_id

Referenced by

codeintel_ranking_graph_keys

4 cols · no RLS · line 1955View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
graph_key textNOT NULL
created_at timestamptznullnow()
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

  • nothing

codeintel_ranking_path_counts_inputs

6 cols · no RLS · line 1972View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKbigintNOT NULL
count integerNOT NULL
graph_key textNOT NULL
processed booleanNOT NULLFALSE
definition_id bigintnull
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (graph_key, id) codeintel_ranking_path_counts_inputs_graph_key_id
  • UNIQUE (graph_key, definition_id) WHERE NOT processed codeintel_ranking_path_counts_inputs_graph_key_unique_definitio

Referenced by

  • nothing

codeintel_ranking_progress

19 cols · no RLS · line 1990View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKbigintNOT NULL
graph_key UKtextNOT NULL
mappers_started_at timestamptzNOT NULL
mapper_completed_at timestamptznull
seed_mapper_completed_at timestamptznull
reducer_started_at timestamptznull
reducer_completed_at timestamptznull
num_path_records_total integernull
num_reference_records_total integernull
num_count_records_total integernull
num_path_records_processed integernull
num_reference_records_processed integernull
num_count_records_processed integernull
max_export_id bigintNOT NULL
reference_cursor_export_deleted_at timestamptznull
reference_cursor_export_id integernull
path_cursor_deleted_export_at timestamptznull
path_cursor_export_id integernull
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

  • nothing

codeintel_ranking_references

6 cols · no RLS · line 2021View in 3D

References for a given upload proceduced by background job consuming SCIP indexes.

  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKbigintNOT NULL
symbol_names text[]NOT NULL
graph_key textNOT NULL
exported_upload_id integerNOT NULLcodeintel_ranking_exports.id 1:N · CASCADE
symbol_checksums bytea[]NOT NULLCAST('{}' AS bytea[])
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (exported_upload_id) codeintel_ranking_references_exported_upload_id
  • (graph_key, id) codeintel_ranking_references_graph_key_id

Referenced by

codeintel_ranking_references_processed

4 cols · no RLS · line 2041View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
graph_key textNOT NULL
codeintel_ranking_reference_id integerNOT NULLcodeintel_ranking_references.id 1:N · CASCADE
id PKbigintNOT NULL
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • UNIQUE (graph_key, codeintel_ranking_reference_id) codeintel_ranking_references_processed_graph_key_codeintel_rank
  • (codeintel_ranking_reference_id) codeintel_ranking_references_processed_reference_id

Referenced by

  • nothing

lsif 20 tables

Tables whose names start with lsif_.

Entity-relationship diagram: lsif_configuration_policies, lsif_configuration_policies_repository_pattern_lookup, lsif_dependency_indexing_jobs, lsif_dependency_syncing_jobs, lsif_dependency_repos, lsif_dirty_repositories, lsif_uploads, lsif_index_configuration, lsif_indexes, lsif_last_index_scan, lsif_last_retention_scan, lsif_nearest_uploads, lsif_nearest_uploads_links, lsif_packages, lsif_references, lsif_retention_configuration, lsif_uploads_audit_logs, lsif_uploads_reference_counts, lsif_uploads_visible_at_tip, lsif_uploads_vulnerability_scanlsif_configuration_policiesid PKintegerrepository_idintegernametexttypetextpatterntextretention_enabledbooleanretention_duration_hoursintegerretain_intermediate_commitsbooleanindexing_enabledbooleanindex_commit_max_age_hoursintegerindex_intermediate_commitsbooleanprotectedbooleanrepository_patternstext[]last_resolved_attimestamptzembeddings_enabledbooleansyntactic_indexing_enabledbooleanlsif_configuration_policies_repository_pattern_lookuppolicy_id PKintegerrepo_id PKintegerlsif_dependency_indexing_jobsid PKintegerstatetextfailure_messagetextqueued_attimestamptzstarted_attimestamptzfinished_attimestamptzprocess_aftertimestamptznum_resetsintegernum_failuresintegerexecution_logsjson[]last_heartbeat_attimestamptzworker_hostnametextupload_id FKintegerexternal_service_kindtextexternal_service_synctimestamptzcancelbooleanlsif_dependency_syncing_jobsid PKintegerstatetextfailure_messagetextqueued_attimestamptzstarted_attimestamptzfinished_attimestamptzprocess_aftertimestamptznum_resetsintegernum_failuresintegerexecution_logsjson[]upload_id FKintegerworker_hostnametextlast_heartbeat_attimestamptzcancelbooleanlsif_dependency_reposid PKbigintnametextschemetextblockedbooleanlast_checked_attimestamptzlsif_dirty_repositoriesrepository_id PKintegerdirty_tokenintegerupdate_tokenintegerupdated_attimestamptzset_dirty_attimestamptzlsif_uploadsid PKintegercommittextroottextuploaded_attimestamptzstatetextfailure_messagetextstarted_attimestamptzfinished_attimestamptzrepository_idintegerindexertextnum_partsintegeruploaded_partsinteger[]process_aftertimestamptznum_resetsintegerupload_sizebigintnum_failuresintegerassociated_index_idbigintcommitted_attimestamptzcommit_last_checked_attimestamptzworker_hostnametextlast_heartbeat_attimestamptzexecution_logsjson[]num_referencesintegerexpiredbooleanlast_retention_scan_attimestamptzreference_countintegerindexer_versiontextqueued_attimestamptzcancelbooleanuncompressed_sizebigintlast_referenced_scan_attimestamptzlast_traversal_scan_attimestamptzlast_reconcile_attimestamptzcontent_typetextshould_reindexbooleanlsif_index_configurationid PKbigintrepository_id FKintegerdatabyteaautoindex_enabledbooleanlsif_indexesid PKbigintcommittextqueued_attimestamptzstatetextfailure_messagetextstarted_attimestamptzfinished_attimestamptzrepository_idintegerprocess_aftertimestamptznum_resetsintegernum_failuresintegerdocker_stepsjsonb[]roottextindexertextindexer_argstext[]outfiletextlog_contentstextexecution_logsjson[]local_stepstext[]commit_last_checked_attimestamptzworker_hostnametextlast_heartbeat_attimestamptzcancelbooleanshould_reindexbooleanrequested_envvarstext[]enqueuer_user_idintegerlsif_last_index_scanrepository_id PKintegerlast_index_scan_attimestamptzlsif_last_retention_scanrepository_id PKintegerlast_retention_scan_attimestamptzlsif_nearest_uploadsrepository_idintegercommit_byteabyteauploadsjsonblsif_nearest_uploads_linksrepository_idintegercommit_byteabyteaancestor_commit_byteabyteadistanceintegerlsif_packagesid PKintegerschemetextnametextversiontextdump_id FKintegermanagertextlsif_referencesid PKintegerschemetextnametextversiontextdump_id FKintegermanagertextlsif_retention_configurationid PKintegerrepository_id FKintegermax_age_for_non_stale_branches_secondsintegermax_age_for_non_stale_tags_secondsintegerlsif_uploads_audit_logslog_timestamptimestamptzrecord_deleted_attimestamptzupload_idintegercommittextroottextrepository_idintegeruploaded_attimestamptzindexertextindexer_versiontextupload_sizebigintassociated_index_idintegertransition_columnshstore[]reasontextsequencebigintoperationaudit_log_operationcontent_typetextlsif_uploads_reference_countsupload_id FKintegerreference_countintegerlsif_uploads_visible_at_tiprepository_idintegerupload_idintegerbranch_or_tag_nametextis_default_branchbooleanlsif_uploads_vulnerability_scanid PKbigintupload_id FKintegerlast_scanned_attimestamprepoupload_idupload_idrepository_iddump_iddump_idrepository_idupload_idupload_id

Relationships

lsif_configuration_policies

16 cols · no RLS · line 1761View in 3D
  • Enum-like column with no CHECK type — 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.
  • 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
repository_id integernullThe identifier of the repository to which this configuration policy applies. If absent, this policy is applied globally.
name textnull
type textNOT NULLThe type of Git object (e.g., COMMIT, BRANCH, TAG).
pattern textNOT NULLA pattern used to match` names of the associated Git object type.
retention_enabled booleanNOT NULLWhether or not this configuration policy affects data retention rules.
retention_duration_hours integernullThe max age of data retained by this configuration policy. If null, the age is unbounded.
retain_intermediate_commits booleanNOT NULLIf the matching Git object is a branch, setting this value to true will also retain all data used to resolve queries for any commit on the matching branches. Setting this value to false will only consider the tip of the branch.
indexing_enabled booleanNOT NULLWhether or not this configuration policy affects auto-indexing schedules.
index_commit_max_age_hours integernullThe max age of commits indexed by this configuration policy. If null, the age is unbounded.
index_intermediate_commits booleanNOT NULLIf the matching Git object is a branch, setting this value to true will also index all commits on the matching branches. Setting this value to false will only consider the tip of the branch.
protected booleanNOT NULLFALSEWhether or not this configuration policy is protected from modification of its data retention behavior (except for duration).
repository_patterns text[]nullThe name pattern matching repositories to which this configuration policy applies. If absent, all repositories are matched.
last_resolved_at timestamptznull
embeddings_enabled booleanNOT NULLFALSE
syntactic_indexing_enabled booleanNOT NULLFALSE

Indexes

  • (repository_id) lsif_configuration_policies_repository_id

Referenced by

  • nothing

lsif_configuration_policies_repository_pattern_lookup

2 cols · no RLS · line 1820View in 3D

A lookup table to get all the repository patterns by repository id that apply to a configuration policy.

  • 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.
ColumnTypeNullDefaultReferencesNotes
policy_id PKintegerNOT NULLThe policy identifier associated with the repository.
repo_id PKintegerNOT NULLThe repository identifier associated with the policy.

Indexes

  • none

Referenced by

  • nothing

lsif_dependency_indexing_jobs

16 cols · no RLS · line 3039View in 3D
  • Foreign key without index upload_id — 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.
  • Nullable foreign key upload_id — 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.
  • Enum-like column with no CHECK state — 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.
  • Enum-like column with no CHECK external_service_kind — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
state textNOT NULLCAST('queued' AS text)
failure_message textnull
queued_at timestamptzNOT NULLnow()
started_at timestamptznull
finished_at timestamptznull
process_after timestamptznull
num_resets integerNOT NULL0
num_failures integerNOT NULL0
execution_logs json[]null
last_heartbeat_at timestamptznull
worker_hostname textNOT NULLCAST('' AS text)
upload_id integernulllsif_uploads.id 1:N · CASCADE
external_service_kind textNOT NULLCAST('' AS text)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.
external_service_sync timestamptznullThe sync time after which external services of the given kind will have synced/created any repositories referenced by the LSIF upload that are resolvable.
cancel booleanNOT NULLFALSE

Indexes

  • (state) lsif_dependency_indexing_jobs_state

Referenced by

  • nothing

lsif_dependency_syncing_jobs

14 cols · no RLS · line 3062View in 3D

Tracks jobs that scan imports of indexes to schedule auto-index jobs.

  • Nullable foreign key upload_id — 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.
  • Enum-like column with no CHECK state — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
state textNOT NULLCAST('queued' AS text)
failure_message textnull
queued_at timestamptzNOT NULLnow()
started_at timestamptznull
finished_at timestamptznull
process_after timestamptznull
num_resets integerNOT NULL0
num_failures integerNOT NULL0
execution_logs json[]null
upload_id integernulllsif_uploads.id 1:N · CASCADEThe identifier of the triggering upload record.
worker_hostname textNOT NULLCAST('' AS text)
last_heartbeat_at timestamptznull
cancel booleanNOT NULLFALSE

Indexes

  • (upload_id) lsif_dependency_indexing_jobs_upload_id
  • (state) lsif_dependency_syncing_jobs_state

Referenced by

  • nothing

lsif_dependency_repos

5 cols · no RLS · line 3103View in 3D
ColumnTypeNullDefaultReferencesNotes
id PKbigintNOT NULL
name textNOT NULL
scheme textNOT NULL
blocked booleanNOT NULLFALSE
last_checked_at timestamptznull

Indexes

  • (blocked) lsif_dependency_repos_blocked
  • (last_checked_at) lsif_dependency_repos_last_checked_at
  • (name) lsif_dependency_repos_name_gin
  • (name, id) lsif_dependency_repos_name_id
  • (scheme, id) lsif_dependency_repos_scheme_id
  • UNIQUE (scheme, name) lsif_dependency_repos_unique_scheme_name

Referenced by

lsif_dirty_repositories

5 cols · no RLS · line 3120View in 3D

Stores whether or not the nearest upload data for a repository is out of date (when update_token > dirty_token).

  • 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.
ColumnTypeNullDefaultReferencesNotes
repository_id PKintegerNOT NULL
dirty_token integerNOT NULLSet to the value of update_token visible to the transaction that updates the commit graph. Updates of dirty_token during this time will cause a second update.
update_token integerNOT NULLThis value is incremented on each request to update the commit graph for the repository.
updated_at timestamptznullThe time the update_token value was last updated.
set_dirty_at timestamptzNOT NULLnow()

Indexes

  • none

Referenced by

  • nothing

lsif_uploads

35 cols · no RLS · line 3136View in 3D

Stores metadata about an LSIF index uploaded by a user.

  • Enum-like column with no CHECK state — 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.
  • Enum-like column with no CHECK content_type — 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.
  • 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULLUsed as a logical foreign key with the (disjoint) codeintel database.
commit textNOT NULLA 40-char revhash. Note that this commit may not be resolvable in the future.CHECK commit ~ CAST('^[a-z0-9]{40}$' AS text)
root textNOT NULLCAST('' AS text)The path for which the index can resolve code intelligence relative to the repository root.
uploaded_at timestamptzNOT NULLnow()
state textNOT NULLCAST('queued' AS text)
failure_message textnull
started_at timestamptznull
finished_at timestamptznull
repository_id integerNOT NULL
indexer textNOT NULLThe name of the indexer that produced the index file. If not supplied by the user it will be pulled from the index metadata.
num_parts integerNOT NULLThe number of parts src-cli split the upload file into.
uploaded_parts integer[]NOT NULLThe index of parts that have been successfully uploaded.
process_after timestamptznull
num_resets integerNOT NULL0
upload_size bigintnullThe size of the index file (in bytes).
num_failures integerNOT NULL0
associated_index_id bigintnull
committed_at timestamptznull
commit_last_checked_at timestamptznull
worker_hostname textNOT NULLCAST('' AS text)
last_heartbeat_at timestamptznull
execution_logs json[]null
num_references integernullDeprecated in favor of reference_count.
expired booleanNOT NULLFALSEWhether or not this upload data is no longer protected by any data retention policy.
last_retention_scan_at timestamptznullThe last time this upload was checked against data retention policies.
reference_count integernullThe number of references to this upload data from other upload records (via lsif_references).
indexer_version textnullThe version of the indexer that produced the index file. If not supplied by the user it will be pulled from the index metadata.
queued_at timestamptznull
cancel booleanNOT NULLFALSE
uncompressed_size bigintnull
last_referenced_scan_at timestamptznullThe last time this upload was known to be referenced by another (possibly expired) index.
last_traversal_scan_at timestamptznullThe last time this upload was known to be reachable by a non-expired index.
last_reconcile_at timestamptznull
content_type textNOT NULLCAST('application/x-ndjson+lsif' AS text)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.
should_reindex booleanNOT NULLFALSE

Indexes

  • (associated_index_id) lsif_uploads_associated_index_id
  • (commit_last_checked_at) WHERE state <> CAST('deleted' AS text) lsif_uploads_commit_last_checked_at
  • (committed_at) WHERE state = CAST('completed' AS text) lsif_uploads_committed_at
  • (last_reconcile_at, id) WHERE state = CAST('completed' AS text) lsif_uploads_last_reconcile_at
  • (repository_id, commit) lsif_uploads_repository_id_commit
  • UNIQUE (repository_id, commit, root, indexer) WHERE state = CAST('completed' AS text) lsif_uploads_repository_id_commit_root_indexer
  • (repository_id, indexer) lsif_uploads_repository_id_indexer
  • (state) lsif_uploads_state
  • (uploaded_at, id) WHERE state <> CAST('deleted' AS text) lsif_uploads_uploaded_at_id

Referenced by

Table checks: commit ~ CAST('^[a-z0-9]{40}$' AS text)

lsif_index_configuration

4 cols · no RLS · line 3270View in 3D

Stores the configuration used for code intel index jobs for a repository.

ColumnTypeNullDefaultReferencesNotes
id PKbigintNOT NULL
repository_id UKintegerNOT NULLrepo.id 1:1 · CASCADE
data byteaNOT NULLThe raw user-supplied [configuration](https://sourcegraph.com/github.com/sourcegraph/sourcegraph@3.23/-/blob/enterprise/internal/codeintel/autoindex/config/types.go#L3:6) (encoded in JSONC).
autoindex_enabled booleanNOT NULLTRUEWhether or not auto-indexing should be attempted on this repo. Index jobs may be inferred from the repository contents if data is empty.

Indexes

  • none

Referenced by

  • nothing

lsif_indexes

26 cols · no RLS · line 3292View in 3D

Stores metadata about a code intel index job.

  • Enum-like column with no CHECK state — 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.
  • 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.
ColumnTypeNullDefaultReferencesNotes
id PKbigintNOT NULL
commit textNOT NULLA 40-char revhash. Note that this commit may not be resolvable in the future.CHECK commit ~ CAST('^[a-z0-9]{40}$' AS text)
queued_at timestamptzNOT NULLnow()
state textNOT NULLCAST('queued' AS text)
failure_message textnull
started_at timestamptznull
finished_at timestamptznull
repository_id integerNOT NULL
process_after timestamptznull
num_resets integerNOT NULL0
num_failures integerNOT NULL0
docker_steps jsonb[]NOT NULLAn array of pre-index [steps](https://sourcegraph.com/github.com/sourcegraph/sourcegraph@3.23/-/blob/enterprise/internal/codeintel/stores/dbstore/docker_step.go#L9:6) to run.
root textNOT NULLThe working directory of the indexer image relative to the repository root.
indexer textNOT NULLThe docker image used to run the index command (e.g. sourcegraph/lsif-go).
indexer_args text[]NOT NULLThe command run inside the indexer image to produce the index file (e.g. ['lsif-node', '-p', '.'])
outfile textNOT NULLThe path to the index file produced by the index command relative to the working directory.
log_contents textnull**Column deprecated in favor of execution_logs.**
execution_logs json[]nullAn array of [log entries](https://sourcegraph.com/github.com/sourcegraph/sourcegraph@3.23/-/blob/internal/workerutil/store.go#L48:6) (encoded as JSON) from the most recent execution.
local_steps text[]NOT NULLA list of commands to run inside the indexer image prior to running the indexer command.
commit_last_checked_at timestamptznull
worker_hostname textNOT NULLCAST('' AS text)
last_heartbeat_at timestamptznull
cancel booleanNOT NULLFALSE
should_reindex booleanNOT NULLFALSE
requested_envvars text[]null
enqueuer_user_id integerNOT NULL0

Indexes

  • (commit_last_checked_at) WHERE state <> CAST('deleted' AS text) lsif_indexes_commit_last_checked_at
  • (enqueuer_user_id > 0, queued_at, id) WHERE state = CAST('queued' AS text) OR state = CAST('errored' AS text) lsif_indexes_dequeue_order_idx
  • (queued_at, id) lsif_indexes_queued_at_id
  • (repository_id, commit) lsif_indexes_repository_id_commit
  • (repository_id, indexer) lsif_indexes_repository_id_indexer
  • (state) lsif_indexes_state

Referenced by

  • nothing

Table checks: commit ~ CAST('^[a-z0-9]{40}$' AS text)

lsif_last_index_scan

2 cols · no RLS · line 3379View in 3D

Tracks the last time repository was checked for auto-indexing job scheduling.

  • 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.
ColumnTypeNullDefaultReferencesNotes
repository_id PKintegerNOT NULL
last_index_scan_at timestamptzNOT NULLThe last time uploads of this repository were considered for auto-indexing job scheduling.

Indexes

  • none

Referenced by

  • nothing

lsif_last_retention_scan

2 cols · no RLS · line 3388View in 3D

Tracks the last time uploads a repository were checked against data retention policies.

  • 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.
ColumnTypeNullDefaultReferencesNotes
repository_id PKintegerNOT NULL
last_retention_scan_at timestamptzNOT NULLThe last time uploads of this repository were checked against data retention policies.

Indexes

  • none

Referenced by

  • nothing

lsif_nearest_uploads

3 cols · no RLS · line 3397View in 3D

Associates commits with the complete set of uploads visible from that commit. Every commit with upload data is present in this table.

  • 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.
  • 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.
ColumnTypeNullDefaultReferencesNotes
repository_id integerNOT NULL
commit_bytea byteaNOT NULLA 40-char revhash. Note that this commit may not be resolvable in the future.
uploads jsonbNOT NULLEncodes an {upload_id => distance} map that includes an entry for every upload visible from the commit. There is always at least one entry with a distance of zero.

Indexes

  • (repository_id, commit_bytea) lsif_nearest_uploads_repository_id_commit_bytea
  • (uploads) lsif_nearest_uploads_uploads

Referenced by

  • nothing

lsif_packages

6 cols · no RLS · line 3424View in 3D

Associates an upload with the set of packages they provide within a given packages management scheme.

ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
scheme textNOT NULLThe (export) moniker scheme.
name textNOT NULLThe package name.
version textnullThe package version.
dump_id integerNOT NULLlsif_uploads.id 1:N · CASCADEThe identifier of the upload that provides the package.
manager textNOT NULLCAST('' AS text)The package manager name.

Indexes

  • (dump_id) lsif_packages_dump_id
  • (scheme, name, version, dump_id) lsif_packages_scheme_name_version_dump_id

Referenced by

  • nothing

lsif_references

6 cols · no RLS · line 3454View in 3D

Associates an upload with the set of packages they require within a given packages management scheme.

ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
scheme textNOT NULLThe (import) moniker scheme.
name textNOT NULLThe package name.
version textnullThe package version.
dump_id integerNOT NULLlsif_uploads.id 1:N · CASCADEThe identifier of the upload that references the package.
manager textNOT NULLCAST('' AS text)The package manager name.

Indexes

  • (dump_id) lsif_references_dump_id
  • (scheme, name, version, dump_id) lsif_references_scheme_name_version_dump_id

Referenced by

  • nothing

lsif_retention_configuration

4 cols · no RLS · line 3484View in 3D

Stores the retention policy of code intellience data for a repository.

ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
repository_id UKintegerNOT NULLrepo.id 1:1 · CASCADE
max_age_for_non_stale_branches_seconds integerNOT NULLThe number of seconds since the last modification of a branch until it is considered stale.
max_age_for_non_stale_tags_seconds integerNOT NULLThe nujmber of seconds since the commit date of a tagged commit until it is considered stale.

Indexes

  • none

Referenced by

  • nothing

lsif_uploads_audit_logs

16 cols · no RLS · line 3507View in 3D
  • 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.
  • Enum-like column with no CHECK content_type — 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.
  • 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.
ColumnTypeNullDefaultReferencesNotes
log_timestamp timestamptznullnow()Timestamp for this log entry.
record_deleted_at timestamptznullSet once the upload this entry is associated with is deleted. Once NOW() - record_deleted_at is above a certain threshold, this log entry will be deleted.
upload_id integerNOT NULL
commit textNOT NULL
root textNOT NULL
repository_id integerNOT NULL
uploaded_at timestamptzNOT NULL
indexer textNOT NULL
indexer_version textnull
upload_size bigintnull
associated_index_id integernull
transition_columns hstore[]nullArray of changes that occurred to the upload for this entry, in the form of {"column"=>"<column name>", "old"=>"<previous value>", "new"=>"<new value>"}.
reason textnullCAST('' AS text)The reason/source for this entry.
sequence bigintNOT NULL
operation audit_log_operationNOT NULL
content_type textNOT NULLCAST('application/x-ndjson+lsif' AS text)

Indexes

  • (log_timestamp) lsif_uploads_audit_logs_timestamp
  • (upload_id) lsif_uploads_audit_logs_upload_id

Referenced by

  • nothing

lsif_uploads_reference_counts

2 cols · no RLS · line 3543View in 3D

A less hot-path reference count for upload records.

  • 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.
ColumnTypeNullDefaultReferencesNotes
upload_id UKintegerNOT NULLlsif_uploads.id 1:1 · CASCADEThe identifier of the referenced upload.
reference_count integerNOT NULLThe number of references to the associated upload from other records (via lsif_references).

Indexes

  • none

Referenced by

  • nothing

lsif_uploads_visible_at_tip

4 cols · no RLS · line 3554View in 3D

Associates a repository with the set of LSIF upload identifiers that can serve intelligence for the tip of the default branch.

  • 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.
  • 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.
ColumnTypeNullDefaultReferencesNotes
repository_id integerNOT NULL
upload_id integerNOT NULLThe identifier of the upload visible from the tip of the specified branch or tag.
branch_or_tag_name textNOT NULLCAST('' AS text)The name of the branch or tag.
is_default_branch booleanNOT NULLFALSEWhether the specified branch is the default of the repository. Always false for tags.

Indexes

  • (upload_id) WHERE is_default_branch lsif_uploads_visible_at_tip_is_default_branch
  • (repository_id, upload_id) lsif_uploads_visible_at_tip_repository_id_upload_id

Referenced by

  • nothing

lsif_uploads_vulnerability_scan

3 cols · no RLS · line 3569View in 3D
  • TIMESTAMP without time zone last_scanned_at — 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.
ColumnTypeNullDefaultReferencesNotes
id PKbigintNOT NULL
upload_id integerNOT NULLlsif_uploads.id 1:1 · CASCADE
last_scanned_at timestampNOT NULLnow()

Indexes

  • UNIQUE (upload_id) lsif_uploads_vulnerability_scan_upload_id

Referenced by

  • nothing

codeowners 3 tables

Tables whose names start with codeowners_.

Entity-relationship diagram: codeowners, codeowners_individual_stats, codeowners_ownerscodeownersid PKintegercontentstextcontents_protobytearepo_id FKintegercreated_attimestamptzupdated_attimestamptztenant_id FKintegercodeowners_individual_statsfile_path_id PKintegerowner_id PKintegertree_owned_files_countintegerupdated_attimestamptenant_id FKintegercodeowners_ownersid PKintegerreferencetexttenant_id FKintegerreporepo_pathstenantsrepo_idtenant_idtenant_idfile_path_idowner_idtenant_id

Relationships

codeowners

7 cols · no RLS · line 2057View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
contents textNOT NULL
contents_proto byteaNOT NULL
repo_id UKintegerNOT NULLrepo.id 1:1 · CASCADE
created_at timestamptzNOT NULLnow()
updated_at timestamptzNOT NULLnow()
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

  • nothing

codeowners_individual_stats

5 cols · no RLS · line 2077View in 3D

Data on how many files in given tree are owned by given owner. As opposed to ownership-general `ownership_path_stats` table, the individual <path x owner> stats are stored in CODEOWNERS-specific table `codeowners_individual_stats`. The reason for that is that we are also indexing on owner_id which is CODEOWNERS-specific.

  • Foreign key without index owner_id — 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.
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
  • TIMESTAMP without time zone updated_at — 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.
ColumnTypeNullDefaultReferencesNotes
file_path_id PKintegerNOT NULLrepo_paths.id 1:N
owner_id PKintegerNOT NULLcodeowners_owners.id 1:N
tree_owned_files_count integerNOT NULLTotal owned file count by given owner at given file tree.
updated_at timestampNOT NULLWhen the last background job updating counts run.
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

  • nothing

codeowners_owners

3 cols · no RLS · line 2095View in 3D

Text reference in CODEOWNERS entry to use in codeowners_individual_stats. Reference is either email or handle without @ in front.

  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
reference textNOT NULLWe just keep the reference as opposed to splitting it to handle or email since the distinction is not relevant for query, and this makes indexing way easier.
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (reference) codeowners_owners_reference

Referenced by

discussion 4 tables

Tables whose names start with discussion_.

Entity-relationship diagram: discussion_comments, discussion_mail_reply_tokens, discussion_threads, discussion_threads_target_repodiscussion_commentsid PKbigintthread_id FKbigintauthor_user_id FKintegercontentstextcreated_attimestamptzupdated_attimestamptzdeleted_attimestamptzreportstext[]tenant_id FKintegerdiscussion_mail_reply_tokenstoken PKtextuser_id FKintegerthread_id FKbigintdeleted_attimestamptztenant_id FKintegerdiscussion_threadsid PKbigintauthor_user_id FKintegertitletexttarget_repo_id FKbigintcreated_attimestamptzarchived_attimestamptzupdated_attimestamptzdeleted_attimestamptztenant_id FKintegerdiscussion_threads_target_repoid PKbigintthread_id FKbigintrepo_id FKintegerpathtextbranchtextrevisiontextstart_lineintegerend_lineintegerstart_characterintegerend_characterintegerlines_beforetextlinestextlines_aftertexttenant_id FKintegerrepotenantsuserstarget_repo_idauthor_user_idtenant_idrepo_idtenant_idthread_idauthor_user_idtenant_idthread_idtenant_idthread_iduser_id

Relationships

discussion_comments

9 cols · no RLS · line 2208View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKbigintNOT NULL
thread_id bigintNOT NULLdiscussion_threads.id 1:N · CASCADE
author_user_id integerNOT NULLusers.id 1:N · RESTRICT
contents textNOT NULL
created_at timestamptzNOT NULLnow()
updated_at timestamptzNOT NULLnow()
deleted_at timestamptznull
reports text[]NOT NULLCAST('{}' AS text[])
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (author_user_id) discussion_comments_author_user_id_idx
  • (array_length(reports, 1)) discussion_comments_reports_array_length_idx
  • (thread_id) discussion_comments_thread_id_idx

Referenced by

  • nothing

discussion_mail_reply_tokens

5 cols · no RLS · line 2229View in 3D
  • Foreign key without index tenant_id — 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.
  • Foreign key without index thread_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
token PKtextNOT NULL
user_id integerNOT NULLusers.id 1:N · RESTRICT
thread_id bigintNOT NULLdiscussion_threads.id 1:N · CASCADE
deleted_at timestamptznull
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (user_id, thread_id) discussion_mail_reply_tokens_user_id_thread_id_idx

Referenced by

  • nothing

discussion_threads

9 cols · no RLS · line 2237View in 3D
  • Foreign key without index target_repo_id — 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.
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key target_repo_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Either/or foreign keys without a CHECK target_repo_id, tenant_id — 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.
  • 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.
ColumnTypeNullDefaultReferencesNotes
id PKbigintNOT NULL
author_user_id integerNOT NULLusers.id 1:N · RESTRICT
title textnull
target_repo_id bigintnulldiscussion_threads_target_repo.id 1:N · CASCADE
created_at timestamptzNOT NULLnow()
archived_at timestamptznull
updated_at timestamptzNOT NULLnow()
deleted_at timestamptznull
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (author_user_id) discussion_threads_author_user_id_idx

Referenced by

discussion_threads_target_repo

14 cols · no RLS · line 2258View in 3D
  • Foreign key without index tenant_id — 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.
  • Foreign key without index thread_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKbigintNOT NULL
thread_id bigintNOT NULLdiscussion_threads.id 1:N · CASCADE
repo_id integerNOT NULLrepo.id 1:N · CASCADE
path textnull
branch textnull
revision textnull
start_line integernull
end_line integernull
start_character integernull
end_character integernull
lines_before textnull
lines textnull
lines_after textnull
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (repo_id, path) discussion_threads_target_repo_repo_id_path_idx

Referenced by

event 4 tables

Tables whose names start with event_.

Entity-relationship diagram: event_logs, event_logs_export_allowlist, event_logs_scrape_state, event_logs_scrape_state_ownevent_logsid PKbigintnametexturltextuser_idintegeranonymous_user_idtextsourcetextargumentjsonbversiontexttimestamptimestamptzfeature_flagsjsonbcohort_iddatepublic_argumentjsonbfirst_source_urltextlast_source_urltextreferrertextdevice_idtextinsert_idtextbilling_product_categorytextbilling_event_idtextclienttexttenant_id FKintegerevent_logs_export_allowlistid PKintegerevent_nametexttenant_id FKintegerevent_logs_scrape_stateid PKintegerbookmark_idintegertenant_id FKintegerevent_logs_scrape_state_ownid PKintegerbookmark_idintegerjob_typeintegertenant_id FKintegertenantstenant_idtenant_idtenant_idtenant_id

Relationships

event_logs

21 cols · no RLS · line 2284View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Enum-like column with no CHECK billing_product_category — 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.
ColumnTypeNullDefaultReferencesNotes
id PKbigintNOT NULL
name textNOT NULLCHECK name <> CAST('' AS text)
url textNOT NULL
user_id integerNOT NULL
anonymous_user_id textNOT NULL
source textNOT NULLCHECK source <> CAST('' AS text)
argument jsonbNOT NULL
version textNOT NULLCHECK version <> CAST('' AS text)
timestamp timestamptzNOT NULL
feature_flags jsonbnull
cohort_id datenull
public_argument jsonbNOT NULLCAST('{}' AS jsonb)
first_source_url textnull
last_source_url textnull
referrer textnull
device_id textnull
insert_id textnull
billing_product_category textnull
billing_event_id textnull
client textnull
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (anonymous_user_id) event_logs_anonymous_user_id
  • (name, timestamp) event_logs_name_timestamp
  • (source) event_logs_source
  • (timestamp) event_logs_timestamp
  • (date(timezone(CAST('UTC' AS text), timestamp))) event_logs_timestamp_at_utc
  • (user_id, name) event_logs_user_id_name
  • (user_id, timestamp) event_logs_user_id_timestamp

Referenced by

  • nothing

Table checks: (user_id = 0 AND anonymous_user_id <> CAST('' AS text)) OR (user_id <> 0 AND anonymous_user_id = CAST('' AS text)) OR (user_id <> 0 AND anonymous_user_id <> CAST('' AS text)); name <> CAST('' AS text); source <> CAST('' AS text); version <> CAST('' AS text)

event_logs_export_allowlist

3 cols · no RLS · line 2312View in 3D

An allowlist of events that are approved for export if the scraping job is enabled

  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
event_name textNOT NULLName of the event that corresponds to event_logs.name
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • UNIQUE (event_name) event_logs_export_allowlist_event_name_idx

Referenced by

  • nothing

event_logs_scrape_state

3 cols · no RLS · line 2341View in 3D

Contains state for the periodic telemetry job that scrapes events if enabled.

  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
bookmark_id integerNOT NULLBookmarks the maximum most recent successful event_logs.id that was scraped
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

  • nothing

event_logs_scrape_state_own

4 cols · no RLS · line 2361View in 3D

Contains state for own jobs that scrape events if enabled.

  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
bookmark_id integerNOT NULLBookmarks the maximum most recent successful event_logs.id that was scraped
job_type integerNOT NULL
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

  • nothing

executor 4 tables

Tables whose names start with executor_.

Entity-relationship diagram: executor_heartbeats, executor_job_tokens, executor_secret_access_logs, executor_secretsexecutor_heartbeatsid PKintegerhostnametextqueue_nametextostextarchitecturetextdocker_versiontextexecutor_versiontextgit_versiontextignite_versiontextsrc_cli_versiontextfirst_seen_attimestamptzlast_seen_attimestamptzqueue_namestext[]tenant_id FKintegerexecutor_job_tokensid PKintegervalue_sha256byteajob_idbigintqueuetextrepo_idbigintcreated_attimestamptzupdated_attimestamptztenant_id FKintegerexecutor_secret_access_logsid PKintegerexecutor_secret_id FKintegeruser_id FKintegercreated_attimestamptzmachine_usertexttenant_id FKintegerexecutor_secretsid PKintegerkeytextvaluebyteascopetextencryption_key_idtextnamespace_user_id FKintegernamespace_org_id FKintegercreated_attimestamptzupdated_attimestamptzcreator_id FKintegertenant_id FKintegerorgstenantsuserstenant_idtenant_idcreator_idnamespace_org_idnamespace_user_idtenant_idexecutor_secret_idtenant_iduser_id

Relationships

executor_heartbeats

14 cols · no RLS · line 2382View in 3D

Tracks the most recent activity of executors attached to this Sourcegraph instance.

  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
hostname UKtextNOT NULLThe uniquely identifying name of the executor.
queue_name textnullThe queue name that the executor polls for work.
os textNOT NULLThe operating system running the executor.
architecture textNOT NULLThe machine architure running the executor.
docker_version textNOT NULLThe version of Docker used by the executor.
executor_version textNOT NULLThe version of the executor.
git_version textNOT NULLThe version of Git used by the executor.
ignite_version textNOT NULLThe version of Ignite used by the executor.
src_cli_version textNOT NULLThe version of src-cli used by the executor.
first_seen_at timestamptzNOT NULLnow()The first time a heartbeat from the executor was received.
last_seen_at timestamptzNOT NULLnow()The last time a heartbeat from the executor was received.
queue_names text[]nullThe list of queue names that the executor polls for work.
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

  • nothing

Table checks: (queue_name IS NOT NULL AND queue_names IS NULL) OR (queue_names IS NOT NULL AND queue_name IS NULL)

executor_job_tokens

8 cols · no RLS · line 2436View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
value_sha256 UKbyteaNOT NULL
job_id bigintNOT NULL
queue textNOT NULL
repo_id bigintNOT NULL
created_at timestamptzNOT NULLnow()
updated_at timestamptzNOT NULLnow()
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

  • nothing

executor_secret_access_logs

6 cols · no RLS · line 2457View in 3D
  • Foreign key without index executor_secret_id — 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.
  • Foreign key without index tenant_id — 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.
  • Foreign key without index user_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Nullable foreign key user_id — 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.
  • Either/or foreign keys without a CHECK tenant_id, user_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
executor_secret_id integerNOT NULLexecutor_secrets.id 1:N · CASCADE
user_id integernullusers.id 1:N · CASCADE
created_at timestamptzNOT NULLnow()
machine_user textNOT NULLCAST('' AS text)
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

  • nothing

Table checks: (user_id IS NULL AND machine_user <> CAST('' AS text)) OR (user_id IS NOT NULL AND machine_user = CAST('' AS text))

executor_secrets

11 cols · no RLS · line 2477View in 3D
  • Foreign key without index creator_id — 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.
  • Foreign key without index namespace_org_id — 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.
  • Foreign key without index namespace_user_id — 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.
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key creator_id — 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.
  • Nullable foreign key namespace_org_id — 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.
  • Nullable foreign key namespace_user_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Enum-like column with no CHECK scope — 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.
  • Either/or foreign keys without a CHECK creator_id, namespace_org_id, namespace_user_id, tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
key textNOT NULL
value byteaNOT NULL
scope textNOT NULL
encryption_key_id textnull
namespace_user_id integernullusers.id 1:N · CASCADE
namespace_org_id integernullorgs.id 1:N · CASCADE
created_at timestamptzNOT NULLnow()
updated_at timestamptzNOT NULLnow()
creator_id integernullusers.id 1:N · SET NULLNULL, if the user has been deleted.
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • UNIQUE (key, scope) WHERE namespace_user_id IS NULL AND namespace_org_id IS NULL executor_secrets_unique_key_global
  • UNIQUE (key, namespace_org_id, scope) WHERE namespace_org_id IS NOT NULL executor_secrets_unique_key_namespace_org
  • UNIQUE (key, namespace_user_id, scope) WHERE namespace_user_id IS NOT NULL executor_secrets_unique_key_namespace_user

Referenced by

exhaustive 3 tables

Tables whose names start with exhaustive_.

Entity-relationship diagram: exhaustive_search_jobs, exhaustive_search_repo_jobs, exhaustive_search_repo_revision_jobsexhaustive_search_jobsid PKintegerstatetextinitiator_id FKintegerquerytextfailure_messagetextstarted_attimestamptzfinished_attimestamptzprocess_aftertimestamptznum_resetsintegernum_failuresintegerlast_heartbeat_attimestamptzexecution_logsjson[]worker_hostnametextcancelbooleancreated_attimestamptzupdated_attimestamptzqueued_attimestamptzis_aggregatedbooleantenant_id FKintegerexhaustive_search_repo_jobsid PKintegerstatetextrepo_id FKintegerref_spectextsearch_job_id FKintegerfailure_messagetextstarted_attimestamptzfinished_attimestamptzprocess_aftertimestamptznum_resetsintegernum_failuresintegerlast_heartbeat_attimestamptzexecution_logsjson[]worker_hostnametextcancelbooleancreated_attimestamptzupdated_attimestamptzqueued_attimestamptztenant_id FKintegerexhaustive_search_repo_revision_jobsid PKintegerstatetextsearch_repo_job_id FKintegerrevisiontextfailure_messagetextstarted_attimestamptzfinished_attimestamptzprocess_aftertimestamptznum_resetsintegernum_failuresintegerlast_heartbeat_attimestamptzexecution_logsjson[]worker_hostnametextcancelbooleancreated_attimestamptzupdated_attimestamptzqueued_attimestamptztenant_id FKintegerrepotenantsusersinitiator_idtenant_idrepo_idsearch_job_idtenant_idsearch_repo_job_idtenant_id

Relationships

exhaustive_search_jobs

19 cols · no RLS · line 2503View in 3D
  • Foreign key without index initiator_id — 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.
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Enum-like column with no CHECK state — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
state textnullCAST('queued' AS text)
initiator_id integerNOT NULLusers.id 1:N · CASCADE
query textNOT NULL
failure_message textnull
started_at timestamptznull
finished_at timestamptznull
process_after timestamptznull
num_resets integerNOT NULL0
num_failures integerNOT NULL0
last_heartbeat_at timestamptznull
execution_logs json[]null
worker_hostname textNOT NULLCAST('' AS text)
cancel booleanNOT NULLFALSE
created_at timestamptzNOT NULLnow()
updated_at timestamptzNOT NULLnow()
queued_at timestamptznullnow()
is_aggregated booleanNOT NULLFALSE
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (state) exhaustive_search_jobs_state

Referenced by

exhaustive_search_repo_jobs

19 cols · no RLS · line 2535View in 3D
  • Foreign key without index repo_id — 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.
  • Foreign key without index search_job_id — 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.
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Enum-like column with no CHECK state — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
state textnullCAST('queued' AS text)
repo_id integerNOT NULLrepo.id 1:N · CASCADE
ref_spec textNOT NULL
search_job_id integerNOT NULLexhaustive_search_jobs.id 1:N · CASCADE
failure_message textnull
started_at timestamptznull
finished_at timestamptznull
process_after timestamptznull
num_resets integerNOT NULL0
num_failures integerNOT NULL0
last_heartbeat_at timestamptznull
execution_logs json[]null
worker_hostname textNOT NULLCAST('' AS text)
cancel booleanNOT NULLFALSE
created_at timestamptzNOT NULLnow()
updated_at timestamptzNOT NULLnow()
queued_at timestamptznullnow()
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (state) exhaustive_search_repo_jobs_state

Referenced by

exhaustive_search_repo_revision_jobs

18 cols · no RLS · line 2567View in 3D
  • Foreign key without index search_repo_job_id — 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.
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Enum-like column with no CHECK state — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
state textnullCAST('queued' AS text)
search_repo_job_id integerNOT NULLexhaustive_search_repo_jobs.id 1:N · CASCADE
revision textNOT NULL
failure_message textnull
started_at timestamptznull
finished_at timestamptznull
process_after timestamptznull
num_resets integerNOT NULL0
num_failures integerNOT NULL0
last_heartbeat_at timestamptznull
execution_logs json[]null
worker_hostname textNOT NULLCAST('' AS text)
cancel booleanNOT NULLFALSE
created_at timestamptzNOT NULLnow()
updated_at timestamptzNOT NULLnow()
queued_at timestamptznullnow()
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (state) exhaustive_search_repo_revision_jobs_state

Referenced by

  • nothing

external 3 tables

Tables whose names start with external_.

Entity-relationship diagram: external_service_repos, external_service_sync_jobs, external_servicesexternal_service_reposexternal_service_id FKbigintrepo_id FKintegerclone_urltextcreated_attimestamptztenant_id FKintegerexternal_service_sync_jobsidintegerstatetextfailure_messagetextstarted_attimestamptzfinished_attimestamptzprocess_aftertimestamptznum_resetsintegerexternal_service_id FKbigintnum_failuresintegerlog_contentstextexecution_logsjson[]worker_hostnametextlast_heartbeat_attimestamptzqueued_attimestamptzcancelbooleanrepos_syncedintegerrepo_sync_errorsintegerrepos_addedintegerrepos_deletedintegerrepos_modifiedintegerrepos_unmodifiedintegertenant_id FKintegerexternal_servicesid PKbigintkindtextdisplay_nametextconfigtextcreated_attimestamptzupdated_attimestamptzdeleted_attimestamptzlast_sync_attimestamptznext_sync_attimestamptzunrestrictedbooleancloud_defaultbooleanencryption_key_idtexthas_webhooksbooleantoken_expires_attimestamptzcode_host_id FKintegercreator_id FKintegerlast_updater_id FKintegertenant_id FKintegercode_hostsrepotenantsuserscode_host_idcreator_idlast_updater_idtenant_idexternal_service_idrepo_idtenant_idtenant_idexternal_service_id

Relationships

external_service_repos

5 cols · no RLS · line 2630View in 3D
  • 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.
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
external_service_id bigintNOT NULLexternal_services.id 1:N · CASCADE
repo_id integerNOT NULLrepo.id 1:N · CASCADE
clone_url textNOT NULL
created_at timestamptzNOT NULLtransaction_timestamp()
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (clone_url) external_service_repos_clone_url_idx
  • (external_service_id, repo_id) external_service_repos_idx

Referenced by

  • nothing

external_service_sync_jobs

22 cols · no RLS · line 2645View in 3D
  • 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.
  • Foreign key without index tenant_id — 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.
  • Foreign key without index external_service_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Enum-like column with no CHECK state — 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.
ColumnTypeNullDefaultReferencesNotes
id integerNOT NULLnextval(CAST('external_service_sync_jobs_id_seq' AS regclass))
state textNOT NULLCAST('queued' AS text)
failure_message textnull
started_at timestamptznull
finished_at timestamptznull
process_after timestamptznull
num_resets integerNOT NULL0
external_service_id bigintNOT NULLexternal_services.id 1:N · CASCADE
num_failures integerNOT NULL0
log_contents textnull
execution_logs json[]null
worker_hostname textNOT NULLCAST('' AS text)
last_heartbeat_at timestamptznull
queued_at timestamptznullnow()
cancel booleanNOT NULLFALSE
repos_synced integerNOT NULL0The number of repos synced during this sync job.
repo_sync_errors integerNOT NULL0The number of times an error occurred syncing a repo during this sync job.
repos_added integerNOT NULL0The number of new repos discovered during this sync job.
repos_deleted integerNOT NULL0The number of repos deleted as a result of this sync job.
repos_modified integerNOT NULL0The number of existing repos whose metadata has changed during this sync job.
repos_unmodified integerNOT NULL0The number of existing repos whose metadata did not change during this sync job.
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (state, external_service_id) external_service_sync_jobs_state_external_service_id

Referenced by

  • nothing

external_services

18 cols · no RLS · line 2682View in 3D
  • Foreign key without index code_host_id — 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.
  • Foreign key without index creator_id — 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.
  • Foreign key without index last_updater_id — 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.
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key code_host_id — 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.
  • Nullable foreign key creator_id — 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.
  • Nullable foreign key last_updater_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Enum-like column with no CHECK kind — 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.
  • Either/or foreign keys without a CHECK code_host_id, creator_id, last_updater_id, tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKbigintNOT NULL
kind textNOT NULL
display_name textNOT NULL
config textNOT NULLCHECK btrim(config) <> CAST('' AS text)
created_at timestamptzNOT NULLnow()
updated_at timestamptzNOT NULLnow()
deleted_at timestamptznull
last_sync_at timestamptznull
next_sync_at timestamptznull
unrestricted booleanNOT NULLFALSE
cloud_default booleanNOT NULLFALSE
encryption_key_id textNOT NULLCAST('' AS text)
has_webhooks booleannull
token_expires_at timestamptznull
code_host_id integernullcode_hosts.id 1:N · SET NULL
creator_id integernullusers.id 1:N · SET NULL
last_updater_id integernullusers.id 1:N · SET NULL
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (has_webhooks) external_services_has_webhooks_idx
  • UNIQUE (kind, cloud_default) WHERE cloud_default = TRUE AND deleted_at IS NULL kind_cloud_default

Referenced by

Table checks: btrim(config) <> CAST('' AS text)

feature 2 tables

Tables whose names start with feature_.

Entity-relationship diagram: feature_flag_overrides, feature_flagsfeature_flag_overridesnamespace_org_id FKintegernamespace_user_id FKintegerflag_nametextflag_valuebooleancreated_attimestamptzupdated_attimestamptzdeleted_attimestamptztenant_id FKintegerfeature_flagsflag_name PKtextflag_typefeature_flag_typebool_valuebooleanrolloutintegercreated_attimestamptzupdated_attimestamptzdeleted_attimestamptztenant_id FKintegerorgstenantsusersnamespace_org_idnamespace_user_idtenant_idtenant_id

Relationships

feature_flag_overrides

8 cols · no RLS · line 2729View in 3D
  • 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.
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key namespace_org_id — 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.
  • Nullable foreign key namespace_user_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Soft delete collides with non-partial UNIQUE namespace_org_id, flag_name — 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.
  • Soft delete collides with non-partial UNIQUE namespace_user_id, flag_name — 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.
  • Either/or foreign keys without a CHECK namespace_org_id, namespace_user_id, tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
namespace_org_id integernullorgs.id 1:N · CASCADE
namespace_user_id integernullusers.id 1:N · CASCADE
flag_name textNOT NULL
flag_value booleanNOT NULL
created_at timestamptzNOT NULLnow()
updated_at timestamptzNOT NULLnow()
deleted_at timestamptznull
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (namespace_org_id) WHERE namespace_org_id IS NOT NULL feature_flag_overrides_org_id
  • (namespace_user_id) WHERE namespace_user_id IS NOT NULL feature_flag_overrides_user_id

Referenced by

  • nothing

Table checks: namespace_org_id IS NOT NULL OR namespace_user_id IS NOT NULL

feature_flags

8 cols · no RLS · line 2741View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
flag_name PKtextNOT NULL
flag_type feature_flag_typeNOT NULL
bool_value booleannullBool value only defined when flag_type is bool
rollout integernullRollout only defined when flag_type is rollout. Increments of 0.01%CHECK rollout >= 0 AND rollout <= 10000
created_at timestamptzNOT NULLnow()
updated_at timestamptzNOT NULLnow()
deleted_at timestamptznull
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

  • nothing

Table checks: rollout >= 0 AND rollout <= 10000; 1 = CASE WHEN flag_type = CAST('bool' AS feature_flag_type) AND bool_value IS NULL THEN 0 WHEN flag_type <> CAST('bool' AS feature_flag_type) AND bool_value IS NOT NULL THEN 0 ELSE 1 END; 1 = CASE WHEN flag_type = CAST('rollout' AS feature_flag_type) AND rollout IS NULL THEN 0 WHEN flag_type <> CAST('rollout' AS feature_flag_type) AND rollout IS NOT NULL THEN 0 ELSE 1 END

github 2 tables

Tables whose names start with github_.

Entity-relationship diagram: github_app_installs, github_appsgithub_app_installsid PKintegerapp_id FKintegerinstallation_idintegercreated_attimestamptzurltextaccount_logintextaccount_avatar_urltextaccount_urltextaccount_typetextupdated_attimestamptztenant_id FKintegergithub_appsid PKintegerapp_idintegernametextslugtextbase_urltextclient_idtextclient_secrettextprivate_keytextencryption_key_idtextlogotextcreated_attimestamptzupdated_attimestamptzapp_urltextwebhook_id FKintegerdomaintextkindgithub_app_kindcreator_idbiginttenant_id FKintegertenantswebhookstenant_idwebhook_idapp_idtenant_id

Relationships

github_app_installs

11 cols · no RLS · line 2773View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Enum-like column with no CHECK account_type — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
app_id integerNOT NULLgithub_apps.id 1:N · CASCADE
installation_id integerNOT NULL
created_at timestamptzNOT NULLnow()
url textnull
account_login textnull
account_avatar_url textnull
account_url textnull
account_type textnull
updated_at timestamptzNOT NULLnow()
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (app_id) app_id_idx
  • (account_login) github_app_installs_account_login
  • (installation_id) installation_id_idx

Referenced by

  • nothing

github_apps

18 cols · no RLS · line 2797View in 3D
  • Foreign key without index tenant_id — 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.
  • Foreign key without index webhook_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Nullable foreign key webhook_id — 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.
  • Either/or foreign keys without a CHECK tenant_id, webhook_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
app_id integerNOT NULL
name textNOT NULL
slug textNOT NULL
base_url textNOT NULL
client_id textNOT NULL
client_secret textNOT NULL
private_key textNOT NULL
encryption_key_id textNOT NULL
logo textnull
created_at timestamptzNOT NULLnow()
updated_at timestamptzNOT NULLnow()
app_url textNOT NULLCAST('' AS text)
webhook_id integernullwebhooks.id 1:N · SET NULL
domain textNOT NULLCAST('repos' AS text)
kind github_app_kindNOT NULLCAST('REPO_SYNC' AS github_app_kind)
creator_id bigintNOT NULL0
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • UNIQUE (app_id, slug, base_url) github_apps_app_id_slug_base_url_unique

Referenced by

gitserver 4 tables

Tables whose names start with gitserver_.

Entity-relationship diagram: gitserver_relocator_jobs, gitserver_repos, gitserver_repos_statistics, gitserver_repos_sync_outputgitserver_relocator_jobsid PKintegerstatetextqueued_attimestamptzfailure_messagetextstarted_attimestamptzfinished_attimestamptzprocess_aftertimestamptznum_resetsintegernum_failuresintegerlast_heartbeat_attimestamptzexecution_logsjson[]worker_hostnametextrepo_idintegersource_hostnametextdest_hostnametextdelete_sourcebooleancancelbooleantenant_id FKintegergitserver_reposrepo_id PKintegerclone_statustextshard_idtextlast_errortextupdated_attimestamptzlast_fetchedtimestamptzlast_changedtimestamptzrepo_size_bytesbigintcorrupted_attimestamptzcorruption_logsjsonbcloning_progresstexttenant_id FKintegergitserver_repos_statisticsshard_idtexttotalbigintnot_clonedbigintcloningbigintclonedbigintfailed_fetchbigintcorruptedbiginttenant_id FKintegergitserver_repos_sync_outputrepo_id PKintegerlast_outputtextupdated_attimestamptztenant_id FKintegerrepotenantstenant_idrepo_idtenant_idtenant_idrepo_idtenant_id

Relationships

gitserver_relocator_jobs

18 cols · no RLS · line 2828View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Enum-like column with no CHECK state — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
state textnullCAST('queued' AS text)
queued_at timestamptznullnow()
failure_message textnull
started_at timestamptznull
finished_at timestamptznull
process_after timestamptznull
num_resets integerNOT NULL0
num_failures integerNOT NULL0
last_heartbeat_at timestamptznull
execution_logs json[]null
worker_hostname textNOT NULLCAST('' AS text)
repo_id integerNOT NULL
source_hostname textNOT NULL
dest_hostname textNOT NULL
delete_source booleanNOT NULLFALSE
cancel booleanNOT NULLFALSE
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (state) gitserver_relocator_jobs_state

Referenced by

  • nothing

gitserver_repos

12 cols · no RLS · line 2880View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Enum-like column with no CHECK clone_status — 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.
ColumnTypeNullDefaultReferencesNotes
repo_id PKintegerNOT NULLrepo.id 1:1 · CASCADE
clone_status textNOT NULLCAST('not_cloned' AS text)
shard_id textNOT NULL
last_error textnull
updated_at timestamptzNOT NULLnow()
last_fetched timestamptzNOT NULLnow()
last_changed timestamptzNOT NULLnow()
repo_size_bytes bigintnull
corrupted_at timestamptznullTimestamp of when repo corruption was detected
corruption_logs jsonbNOT NULLCAST('[]' AS jsonb)Log output of repo corruptions that have been detected - encoded as json
cloning_progress textnullCAST('' AS text)
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (repo_size_bytes) gitserver_repo_size_bytes
  • (repo_id) WHERE clone_status = CAST('cloned' AS text) gitserver_repos_cloned_status_idx
  • (repo_id) WHERE clone_status = CAST('cloning' AS text) gitserver_repos_cloning_status_idx
  • (last_changed, repo_id) gitserver_repos_last_changed_idx
  • (repo_id) WHERE last_error IS NOT NULL gitserver_repos_last_error_idx
  • (repo_id) WHERE clone_status = CAST('not_cloned' AS text) gitserver_repos_not_cloned_status_idx
  • (repo_id) WHERE clone_status <> CAST('cloned' AS text) gitserver_repos_not_explicitly_cloned_idx
  • (shard_id, repo_id) gitserver_repos_shard_id

Referenced by

  • nothing

gitserver_repos_statistics

8 cols · no RLS · line 2899View in 3D
  • 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.
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
shard_id textnullID of this gitserver shard. If an empty string then the repositories havent been assigned a shard.
total bigintNOT NULL0Number of repositories in gitserver_repos table on this shard
not_cloned bigintNOT NULL0Number of repositories in gitserver_repos table on this shard that are not cloned yet
cloning bigintNOT NULL0Number of repositories in gitserver_repos table on this shard that cloning
cloned bigintNOT NULL0Number of repositories in gitserver_repos table on this shard that are cloned
failed_fetch bigintNOT NULL0Number of repositories in gitserver_repos table on this shard where last_error is set
corrupted bigintNOT NULL0Number of repositories that are NOT soft-deleted and not blocked and have corrupted_at set in gitserver_repos table
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (shard_id) gitserver_repos_statistics_shard_id

Referenced by

  • nothing

gitserver_repos_sync_output

4 cols · no RLS · line 2924View in 3D

Contains the most recent output from gitserver repository sync jobs.

  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
repo_id PKintegerNOT NULLrepo.id 1:1 · CASCADE
last_output textNOT NULLCAST('' AS text)
updated_at timestamptzNOT NULLnow()
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

  • nothing

insights 3 tables

Tables whose names start with insights_.

Entity-relationship diagram: insights_query_runner_jobs, insights_query_runner_jobs_dependencies, insights_settings_migration_jobsinsights_query_runner_jobsid PKintegerseries_idtextsearch_querytextstatetextfailure_messagetextstarted_attimestamptzfinished_attimestamptzprocess_aftertimestamptznum_resetsintegernum_failuresintegerexecution_logsjson[]record_timetimestamptzworker_hostnametextlast_heartbeat_attimestamptzpriorityintegercostintegerpersist_modepersistmodequeued_attimestamptzcancelbooleantrace_idtexttenant_id FKintegerinsights_query_runner_jobs_dependenciesid PKintegerjob_id FKintegerrecording_timetimestamptenant_id FKintegerinsights_settings_migration_jobsidintegeruser_idintegerorg_idintegerglobalbooleansettings_idintegertotal_insightsintegermigrated_insightsintegertotal_dashboardsintegermigrated_dashboardsintegerrunsintegercompleted_attimestamptenant_id FKintegertenantstenant_idtenant_idjob_idtenant_id

Relationships

insights_query_runner_jobs

21 cols · no RLS · line 2939View in 3D

See [internal/insights/background/queryrunner/worker.go:Job](https://sourcegraph.com/search?q=repo:%5Egithub%5C.com/sourcegraph/sourcegraph%24+file:internal/insights/background/queryrunner/worker.go+type+Job&patternType=literal)

  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Enum-like column with no CHECK state — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
series_id textNOT NULL
search_query textNOT NULL
state textnullCAST('queued' AS text)
failure_message textnull
started_at timestamptznull
finished_at timestamptznull
process_after timestamptznull
num_resets integerNOT NULL0
num_failures integerNOT NULL0
execution_logs json[]null
record_time timestamptznull
worker_hostname textNOT NULLCAST('' AS text)
last_heartbeat_at timestamptznull
priority integerNOT NULL1Integer representing a category of priority for this query. Priority in this context is ambiguously defined for consumers to decide an interpretation.
cost integerNOT NULL500Integer representing a cost approximation of executing this search query.
persist_mode persistmodeNOT NULLCAST('record' AS persistmode)The persistence level for this query. This value will determine the lifecycle of the resulting value.
queued_at timestamptznullnow()
cancel booleanNOT NULLFALSE
trace_id textnull
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (finished_at) finished_at_insights_query_runner_jobs_idx
  • (cost) insights_query_runner_jobs_cost_idx
  • (priority) insights_query_runner_jobs_priority_idx
  • (priority, id) WHERE state = CAST('queued' AS text) OR state = CAST('errored' AS text) insights_query_runner_jobs_processable_priority_id
  • (series_id, state) insights_query_runner_jobs_series_id_state
  • (state) insights_query_runner_jobs_state_btree
  • (process_after) process_after_insights_query_runner_jobs_idx

Referenced by

insights_query_runner_jobs_dependencies

4 cols · no RLS · line 2971View in 3D

Stores data points for a code insight that do not need to be queried directly, but depend on the result of a query at a different point

  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
  • TIMESTAMP without time zone recording_time — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
job_id integerNOT NULLinsights_query_runner_jobs.id 1:N · CASCADEForeign key to the job that owns this record.
recording_time timestampNOT NULLThe time for which this dependency should be recorded at using the parents value.
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (job_id) insights_query_runner_jobs_dependencies_job_id_fk_idx

Referenced by

  • nothing

insights_settings_migration_jobs

12 cols · no RLS · line 3004View in 3D
  • 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.
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
  • TIMESTAMP without time zone completed_at — 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.
ColumnTypeNullDefaultReferencesNotes
id integerNOT NULL
user_id integernull
org_id integernull
global booleannull
settings_id integerNOT NULL
total_insights integerNOT NULL0
migrated_insights integerNOT NULL0
total_dashboards integerNOT NULL0
migrated_dashboards integerNOT NULL0
runs integerNOT NULL0
completed_at timestampnull
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

  • nothing

org 3 tables

Tables whose names start with org_.

Entity-relationship diagram: org_invitations, org_members, org_statsorg_invitationsid PKbigintorg_id FKintegersender_user_id FKintegerrecipient_user_id FKintegercreated_attimestamptznotified_attimestamptzresponded_attimestamptzresponse_typebooleanrevoked_attimestamptzdeleted_attimestamptzrecipient_emailcitextexpires_attimestamptztenant_id FKintegerorg_membersid PKintegerorg_id FKintegercreated_attimestamptzupdated_attimestamptzuser_id FKintegertenant_id FKintegerorg_statsorg_id PKintegercode_host_repo_countintegerupdated_attimestamptztenant_id FKintegerorgstenantsusersorg_idrecipient_user_idsender_user_idtenant_idorg_idtenant_iduser_idorg_idtenant_id

Relationships

org_invitations

13 cols · no RLS · line 3677View in 3D
  • Foreign key without index sender_user_id — 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.
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key recipient_user_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Either/or foreign keys without a CHECK recipient_user_id, tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKbigintNOT NULL
org_id integerNOT NULLorgs.id 1:N
sender_user_id integerNOT NULLusers.id 1:N
recipient_user_id integernullusers.id 1:N
created_at timestamptzNOT NULLnow()
notified_at timestamptznull
responded_at timestamptznull
response_type booleannull
revoked_at timestamptznull
deleted_at timestamptznull
recipient_email citextnull
expires_at timestamptznull
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (org_id) WHERE deleted_at IS NULL org_invitations_org_id
  • (recipient_user_id) WHERE deleted_at IS NULL org_invitations_recipient_user_id

Referenced by

  • nothing

Table checks: responded_at IS NULL = response_type IS NULL; (responded_at IS NULL AND response_type IS NULL) OR revoked_at IS NULL; recipient_user_id IS NULL <> recipient_email IS NULL

org_members

6 cols · no RLS · line 3705View in 3D
  • Foreign key without index tenant_id — 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.
  • Foreign key without index user_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
org_id integerNOT NULLorgs.id 1:N · RESTRICT
created_at timestamptzNOT NULLnow()
updated_at timestamptzNOT NULLnow()
user_id integerNOT NULLusers.id 1:N · RESTRICT
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

  • nothing

org_stats

4 cols · no RLS · line 3723View in 3D

Business statistics for organizations

  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
org_id PKintegerNOT NULLorgs.id 1:1 · CASCADEOrg ID that the stats relate to.
code_host_repo_count integernull0Count of repositories accessible on all code hosts for this organization.
updated_at timestamptzNOT NULLnow()
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

  • nothing

orgs 2 tables

Tables whose names start with orgs_.

Entity-relationship diagram: orgs, orgs_open_beta_statsorgsid PKintegernamecitextcreated_attimestamptzupdated_attimestamptzdisplay_nametextslack_webhook_urltextdeleted_attimestamptztenant_id FKintegerorgs_open_beta_statsid PKuuiduser_idintegerorg_idintegercreated_attimestamptzdatajsonbtenant_id FKintegertenantstenant_idtenant_id

Relationships

orgs

8 cols · no RLS · line 3736View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
name citextNOT NULLCHECK char_length(CAST(name AS text)) <= 255
created_at timestamptzNOT NULLnow()
updated_at timestamptzNOT NULLnow()
display_name textnullCHECK char_length(display_name) <= 255
slack_webhook_url textnull
deleted_at timestamptznull
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • UNIQUE (name) WHERE deleted_at IS NULL orgs_name

Referenced by

Table checks: char_length(display_name) <= 255; char_length(CAST(name AS text)) <= 255; name ~ CAST('^[a-zA-Z0-9](?:[a-zA-Z0-9]|[-.](?=[a-zA-Z0-9]))*-?$' AS citext)

orgs_open_beta_stats

6 cols · no RLS · line 3759View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKuuidNOT NULLgen_random_uuid()
user_id integernull
org_id integernull
created_at timestamptznullnow()
data jsonbNOT NULLCAST('{}' AS jsonb)
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

  • nothing

out 2 tables

Tables whose names start with out_.

Entity-relationship diagram: out_of_band_migrations, out_of_band_migrations_errorsout_of_band_migrationsid PKintegerteamtextcomponenttextdescriptiontextprogressdouble precisioncreatedtimestamptzlast_updatedtimestamptznon_destructivebooleanapply_reversebooleanis_enterprisebooleanintroduced_version_majorintegerintroduced_version_minorintegerdeprecated_version_majorintegerdeprecated_version_minorintegermetadatajsonbtenant_id FKintegerout_of_band_migrations_errorsid PKintegermigration_id FKintegermessagetextcreatedtimestamptztenant_id FKintegertenantstenant_idmigration_idtenant_id

Relationships

out_of_band_migrations

16 cols · no RLS · line 3768View in 3D

Stores metadata and progress about an out-of-band migration routine.

  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULLA globally unique primary key for this migration. The same key is used consistently across all Sourcegraph instances for the same migration.
team textNOT NULLThe name of the engineering team responsible for the migration.CHECK team <> CAST('' AS text)
component textNOT NULLThe name of the component undergoing a migration.CHECK component <> CAST('' AS text)
description textNOT NULLA brief description about the migration.CHECK description <> CAST('' AS text)
progress double precisionNOT NULL0The percentage progress in the up direction (0=0%, 1=100%).CHECK progress >= CAST(0 AS double precision) AND progress <= CAST(1 AS double precision)
created timestamptzNOT NULLThe date and time the migration was inserted into the database (via an upgrade).
last_updated timestamptznullThe date and time the migration was last updated.
non_destructive booleanNOT NULLWhether or not this migration alters data so it can no longer be read by the previous Sourcegraph instance.
apply_reverse booleanNOT NULLFALSEWhether this migration should run in the opposite direction (to support an upcoming downgrade).
is_enterprise booleanNOT NULLFALSEWhen true, these migrations are invisible to OSS mode.
introduced_version_major integerNOT NULLThe Sourcegraph version (major component) in which this migration was first introduced.
introduced_version_minor integerNOT NULLThe Sourcegraph version (minor component) in which this migration was first introduced.
deprecated_version_major integernullThe lowest Sourcegraph version (major component) that assumes the migration has completed.
deprecated_version_minor integernullThe lowest Sourcegraph version (minor component) that assumes the migration has completed.
metadata jsonbNOT NULLCAST('{}' AS jsonb)
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

Table checks: component <> CAST('' AS text); description <> CAST('' AS text); progress >= CAST(0 AS double precision) AND progress <= CAST(1 AS double precision); team <> CAST('' AS text)

out_of_band_migrations_errors

5 cols · no RLS · line 3821View in 3D

Stores errors that occurred while performing an out-of-band migration.

  • Foreign key without index migration_id — 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.
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULLA unique identifer.
migration_id integerNOT NULLout_of_band_migrations.id 1:N · CASCADEThe identifier of the migration.
message textNOT NULLThe error message.CHECK message <> CAST('' AS text)
created timestamptzNOT NULLnow()The date and time the error occurred.
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

  • nothing

Table checks: message <> CAST('' AS text)

outbound 4 tables

Tables whose names start with outbound_.

Entity-relationship diagram: outbound_webhook_event_types, outbound_webhook_jobs, outbound_webhook_logs, outbound_webhooksoutbound_webhook_event_typesid PKbigintoutbound_webhook_id FKbigintevent_typetextscopetexttenant_id FKintegeroutbound_webhook_jobsid PKbigintevent_typetextscopetextencryption_key_idtextpayloadbyteastatetextfailure_messagetextqueued_attimestamptzstarted_attimestamptzfinished_attimestamptzprocess_aftertimestamptznum_resetsintegernum_failuresintegerlast_heartbeat_attimestamptzexecution_logsjson[]worker_hostnametextcancelbooleantenant_id FKintegeroutbound_webhook_logsid PKbigintjob_id FKbigintoutbound_webhook_id FKbigintsent_attimestamptzstatus_codeintegerencryption_key_idtextrequestbytearesponsebyteaerrorbyteatenant_id FKintegeroutbound_webhooksid PKbigintcreated_by FKintegercreated_attimestamptzupdated_by FKintegerupdated_attimestamptzencryption_key_idtexturlbyteasecretbyteatenant_id FKintegertenantsuserstenant_idcreated_bytenant_idupdated_byoutbound_webhook_idtenant_idjob_idoutbound_webhook_idtenant_id

Relationships

outbound_webhook_event_types

5 cols · no RLS · line 3860View in 3D
  • Foreign key without index outbound_webhook_id — 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.
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Enum-like column with no CHECK event_type — 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.
  • Enum-like column with no CHECK scope — 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.
ColumnTypeNullDefaultReferencesNotes
id PKbigintNOT NULL
outbound_webhook_id bigintNOT NULLoutbound_webhooks.id 1:N · CASCADE
event_type textNOT NULL
scope textnull
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (event_type, scope) outbound_webhook_event_types_event_type_idx

Referenced by

  • nothing

outbound_webhook_jobs

18 cols · no RLS · line 3877View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Enum-like column with no CHECK event_type — 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.
  • Enum-like column with no CHECK scope — 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.
  • Enum-like column with no CHECK state — 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.
ColumnTypeNullDefaultReferencesNotes
id PKbigintNOT NULL
event_type textNOT NULL
scope textnull
encryption_key_id textnull
payload byteaNOT NULL
state textNOT NULLCAST('queued' AS text)
failure_message textnull
queued_at timestamptzNOT NULLnow()
started_at timestamptznull
finished_at timestamptznull
process_after timestamptznull
num_resets integerNOT NULL0
num_failures integerNOT NULL0
last_heartbeat_at timestamptznull
execution_logs json[]null
worker_hostname textNOT NULLCAST('' AS text)
cancel booleanNOT NULLFALSE
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (state) outbound_webhook_jobs_state_idx
  • (process_after) outbound_webhook_payload_process_after_idx

Referenced by

outbound_webhook_logs

10 cols · no RLS · line 3907View in 3D
  • Foreign key without index job_id — 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.
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKbigintNOT NULL
job_id bigintNOT NULLoutbound_webhook_jobs.id 1:N · CASCADE
outbound_webhook_id bigintNOT NULLoutbound_webhooks.id 1:N · CASCADE
sent_at timestamptzNOT NULLnow()
status_code integerNOT NULL
encryption_key_id textnull
request byteaNOT NULL
response byteaNOT NULL
error byteaNOT NULL
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (outbound_webhook_id) outbound_webhook_logs_outbound_webhook_id_idx
  • (status_code) outbound_webhooks_logs_status_code_idx

Referenced by

  • nothing

outbound_webhooks

9 cols · no RLS · line 3929View in 3D
  • Foreign key without index created_by — 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.
  • Foreign key without index tenant_id — 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.
  • Foreign key without index updated_by — 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.
  • Nullable foreign key created_by — 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.
  • Nullable foreign key tenant_id — 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.
  • Nullable foreign key updated_by — 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.
ColumnTypeNullDefaultReferencesNotes
id PKbigintNOT NULL
created_by integernullusers.id 1:N · SET NULL
created_at timestamptzNOT NULLnow()
updated_by integernullusers.id 1:N · SET NULL
updated_at timestamptzNOT NULLnow()
encryption_key_id textnull
url byteaNOT NULL
secret byteaNOT NULL
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

own 5 tables

Tables whose names start with own_.

Entity-relationship diagram: own_aggregate_recent_contribution, own_aggregate_recent_view, own_background_jobs, own_signal_configurations, own_signal_recent_contributionown_aggregate_recent_contributionid PKintegercommit_author_id FKintegerchanged_file_path_id FKintegercontributions_countintegertenant_id FKintegerown_aggregate_recent_viewid PKintegerviewer_id FKintegerviewed_file_path_id FKintegerviews_countintegertenant_id FKintegerown_background_jobsid PKintegerstatetextfailure_messagetextqueued_attimestamptzstarted_attimestamptzfinished_attimestamptzprocess_aftertimestamptznum_resetsintegernum_failuresintegerlast_heartbeat_attimestamptzexecution_logsjson[]worker_hostnametextcancelbooleanrepo_idintegerjob_typeintegertenant_id FKintegerown_signal_configurationsid PKintegernametextdescriptiontextexcluded_repo_patternstext[]enabledbooleantenant_id FKintegerown_signal_recent_contributionid PKintegercommit_author_id FKintegerchanged_file_path_id FKintegercommit_timestamptimestampcommit_idbyteatenant_id FKintegercommit_authorsrepo_pathstenantsuserschanged_file_path_idcommit_author_idtenant_idtenant_idviewed_file_path_idviewer_idtenant_idtenant_idchanged_file_path_idcommit_author_idtenant_id

Relationships

own_aggregate_recent_contribution

5 cols · no RLS · line 3964View in 3D
  • Foreign key without index commit_author_id — 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.
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
commit_author_id integerNOT NULLcommit_authors.id 1:N
changed_file_path_id integerNOT NULLrepo_paths.id 1:N
contributions_count integernull0
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • UNIQUE (changed_file_path_id, commit_author_id) own_aggregate_recent_contribution_file_author

Referenced by

  • nothing

own_aggregate_recent_view

5 cols · no RLS · line 3982View in 3D

One entry contains a number of views of a single file by a given viewer.

  • Foreign key without index tenant_id — 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.
  • Foreign key without index viewer_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
viewer_id integerNOT NULLusers.id 1:N · CASCADE
viewed_file_path_id integerNOT NULLrepo_paths.id 1:N
views_count integernull0
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • UNIQUE (viewed_file_path_id, viewer_id) own_aggregate_recent_view_viewer

Referenced by

  • nothing

own_background_jobs

16 cols · no RLS · line 4002View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Enum-like column with no CHECK state — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
state textnullCAST('queued' AS text)
failure_message textnull
queued_at timestamptznullnow()
started_at timestamptznull
finished_at timestamptznull
process_after timestamptznull
num_resets integerNOT NULL0
num_failures integerNOT NULL0
last_heartbeat_at timestamptznull
execution_logs json[]null
worker_hostname textNOT NULLCAST('' AS text)
cancel booleanNOT NULLFALSE
repo_id integerNOT NULL
job_type integerNOT NULL
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (repo_id) own_background_jobs_repo_id_idx
  • (state) own_background_jobs_state_idx

Referenced by

  • nothing

own_signal_configurations

6 cols · no RLS · line 4021View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
name textNOT NULL
description textNOT NULLCAST('' AS text)
excluded_repo_patterns text[]null
enabled booleanNOT NULLFALSE
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • UNIQUE (name) own_signal_configurations_name_uidx

Referenced by

  • nothing

own_signal_recent_contribution

6 cols · no RLS · line 4071View in 3D

One entry per file changed in every commit that classifies as a contribution signal.

  • Foreign key without index changed_file_path_id — 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.
  • Foreign key without index commit_author_id — 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.
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
  • TIMESTAMP without time zone commit_timestamp — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
commit_author_id integerNOT NULLcommit_authors.id 1:N
changed_file_path_id integerNOT NULLrepo_paths.id 1:N
commit_timestamp timestampNOT NULL
commit_id byteaNOT NULL
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

  • nothing

package 2 tables

Tables whose names start with package_.

Entity-relationship diagram: package_repo_filters, package_repo_versionspackage_repo_filtersid PKintegerbehaviourtextschemetextmatcherjsonbdeleted_attimestamptzupdated_attimestamptztenant_id FKintegerpackage_repo_versionsid PKbigintpackage_id FKbigintversiontextblockedbooleanlast_checked_attimestamptztenant_id FKintegerlsif_dependency_repostenantstenant_idpackage_idtenant_id

Relationships

package_repo_filters

7 cols · no RLS · line 4110View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Soft delete collides with non-partial UNIQUE scheme, matcher — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
behaviour textNOT NULLCHECK behaviour = ANY(CAST('{BLOCK,ALLOW}' AS text[]))
scheme textNOT NULLCHECK scheme = ANY(CAST('{semanticdb,npm,go,python,rust-analyzer,scip-ruby}' AS text[]))
matcher jsonbNOT NULLCHECK (matcher ? CAST('VersionGlob' AS text) AND (matcher ->> CAST('VersionGlob' AS text)) <> CAST('' AS text) AND (matcher ->> CAST('PackageName' AS text)) <> CAST('' AS text) AND NOT matcher ? CAST('PackageGlob' AS text)) OR (matcher ? CAST('PackageGlob' AS text) AND (matcher ->> CAST('PackageGlob' AS text)) <> CAST('' AS text) AND NOT matcher ? CAST('VersionGlob' AS text))
deleted_at timestamptznull
updated_at timestamptzNOT NULLstatement_timestamp()
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • UNIQUE (scheme, matcher) package_repo_filters_unique_matcher_per_scheme

Referenced by

  • nothing

Table checks: behaviour = ANY(CAST('{BLOCK,ALLOW}' AS text[])); scheme = ANY(CAST('{semanticdb,npm,go,python,rust-analyzer,scip-ruby}' AS text[])); (matcher ? CAST('VersionGlob' AS text) AND (matcher ->> CAST('VersionGlob' AS text)) <> CAST('' AS text) AND (matcher ->> CAST('PackageName' AS text)) <> CAST('' AS text) AND NOT matcher ? CAST('PackageGlob' AS text)) OR (matcher ? CAST('PackageGlob' AS text) AND (matcher ->> CAST('PackageGlob' AS text)) <> CAST('' AS text) AND NOT matcher ? CAST('VersionGlob' AS text))

package_repo_versions

6 cols · no RLS · line 4133View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKbigintNOT NULL
package_id bigintNOT NULLlsif_dependency_repos.id 1:N · CASCADE
version textNOT NULL
blocked booleanNOT NULLFALSE
last_checked_at timestamptznull
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (blocked) package_repo_versions_blocked
  • (last_checked_at) package_repo_versions_last_checked_at
  • UNIQUE (package_id, version) package_repo_versions_unique_version_per_package

Referenced by

  • nothing

product 2 tables

Tables whose names start with product_.

Entity-relationship diagram: product_licenses, product_subscriptionsproduct_licensesid PKuuidproduct_subscription_id FKuuidlicense_keytextcreated_attimestamptzlicense_versionintegerlicense_tagstext[]license_user_countintegerlicense_expires_attimestamptzaccess_token_enabledbooleansite_iduuidlicense_check_tokenbytearevoked_attimestamptzsalesforce_sub_idtextsalesforce_opp_idtextrevoke_reasontexttenant_id FKintegerproduct_subscriptionsid PKuuiduser_id FKintegerbilling_subscription_idtextcreated_attimestamptzupdated_attimestamptzarchived_attimestamptzaccount_numbertextcody_gateway_enabledbooleancody_gateway_chat_rate_limitbigintcody_gateway_chat_rate_interval_secondsintegercody_gateway_embeddings_api_rate_limitbigintcody_gateway_embeddings_api_rate_interval_secondsintegercody_gateway_embeddings_api_allowed_modelstext[]cody_gateway_chat_rate_limit_allowed_modelstext[]cody_gateway_code_rate_limitbigintcody_gateway_code_rate_interval_secondsintegercody_gateway_code_rate_limit_allowed_modelstext[]tenant_id FKintegertenantsuserstenant_iduser_idproduct_subscription_idtenant_id

Relationships

product_licenses

16 cols · no RLS · line 4240View in 3D
  • Foreign key without index product_subscription_id — 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.
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKuuidNOT NULL
product_subscription_id uuidNOT NULLproduct_subscriptions.id 1:N
license_key textNOT NULL
created_at timestamptzNOT NULLnow()
license_version integernull
license_tags text[]null
license_user_count integernull
license_expires_at timestamptznull
access_token_enabled booleanNOT NULLTRUEWhether this license key can be used as an access token to authenticate API requests
site_id uuidnull
license_check_token byteanull
revoked_at timestamptznull
salesforce_sub_id textnull
salesforce_opp_id textnull
revoke_reason textnull
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • UNIQUE (license_check_token) product_licenses_license_check_token_idx

Referenced by

  • nothing

product_subscriptions

18 cols · no RLS · line 4261View in 3D
  • Foreign key without index tenant_id — 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.
  • Foreign key without index user_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKuuidNOT NULL
user_id integerNOT NULLusers.id 1:N
billing_subscription_id textnull
created_at timestamptzNOT NULLnow()
updated_at timestamptzNOT NULLnow()
archived_at timestamptznull
account_number textnull
cody_gateway_enabled booleanNOT NULLFALSE
cody_gateway_chat_rate_limit bigintnull
cody_gateway_chat_rate_interval_seconds integernull
cody_gateway_embeddings_api_rate_limit bigintnullCustom requests per time interval allowed for embeddings
cody_gateway_embeddings_api_rate_interval_seconds integernullCustom time interval over which the embeddings rate limit is applied
cody_gateway_embeddings_api_allowed_models text[]nullCustom override for the set of models allowed for embedding
cody_gateway_chat_rate_limit_allowed_models text[]null
cody_gateway_code_rate_limit bigintnull
cody_gateway_code_rate_interval_seconds integernull
cody_gateway_code_rate_limit_allowed_models text[]null
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

registry 2 tables

Tables whose names start with registry_.

Entity-relationship diagram: registry_extension_releases, registry_extensionsregistry_extension_releasesid PKbigintregistry_extension_id FKintegercreator_user_id FKintegerrelease_versioncitextrelease_tagcitextmanifestjsonbbundletextcreated_attimestamptzdeleted_attimestamptzsource_maptexttenant_id FKintegerregistry_extensionsid PKintegeruuiduuidpublisher_user_id FKintegerpublisher_org_id FKintegernamecitextmanifesttextcreated_attimestamptzupdated_attimestamptzdeleted_attimestamptztenant_id FKintegerorgstenantsuserspublisher_org_idpublisher_user_idtenant_idcreator_user_idregistry_extension_idtenant_id

Relationships

registry_extension_releases

11 cols · no RLS · line 4432View in 3D
  • Foreign key without index creator_user_id — 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.
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKbigintNOT NULL
registry_extension_id integerNOT NULLregistry_extensions.id 1:N · CASCADE
creator_user_id integerNOT NULLusers.id 1:N
release_version citextnull
release_tag citextNOT NULL
manifest jsonbNOT NULL
bundle textnull
created_at timestamptzNOT NULLnow()
deleted_at timestamptznull
source_map textnull
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (registry_extension_id, release_tag, created_at) WHERE deleted_at IS NULL registry_extension_releases_registry_extension_id
  • (registry_extension_id, created_at) WHERE deleted_at IS NULL registry_extension_releases_registry_extension_id_created_at
  • UNIQUE (registry_extension_id, release_version) WHERE release_version IS NOT NULL registry_extension_releases_version

Referenced by

  • nothing

registry_extensions

10 cols · no RLS · line 4455View in 3D
  • Foreign key without index publisher_org_id — 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.
  • Foreign key without index publisher_user_id — 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.
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key publisher_org_id — 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.
  • Nullable foreign key publisher_user_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Soft delete collides with non-partial UNIQUE uuid — 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.
  • Either/or foreign keys without a CHECK publisher_org_id, publisher_user_id, tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
uuid uuidNOT NULL
publisher_user_id integernullusers.id 1:N
publisher_org_id integernullorgs.id 1:N
name citextNOT NULLCHECK char_length(CAST(name AS text)) > 0 AND char_length(CAST(name AS text)) <= 128
manifest textnull
created_at timestamptzNOT NULLnow()
updated_at timestamptzNOT NULLnow()
deleted_at timestamptznull
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • UNIQUE (COALESCE(publisher_user_id, 0), COALESCE(publisher_org_id, 0), name) WHERE deleted_at IS NULL registry_extensions_publisher_name
  • UNIQUE (uuid) registry_extensions_uuid

Referenced by

Table checks: char_length(CAST(name AS text)) > 0 AND char_length(CAST(name AS text)) <= 128; name ~ CAST('^[a-zA-Z0-9](?:[a-zA-Z0-9]|[_.-](?=[a-zA-Z0-9]))*$' AS citext); publisher_user_id IS NULL <> publisher_org_id IS NULL

syntactic 2 tables

Tables whose names start with syntactic_.

Entity-relationship diagram: syntactic_scip_indexing_jobs, syntactic_scip_last_index_scansyntactic_scip_indexing_jobsid PKbigintcommittextqueued_attimestamptzstatetextfailure_messagetextstarted_attimestamptzfinished_attimestamptzrepository_idintegerprocess_aftertimestamptznum_resetsintegernum_failuresintegerexecution_logsjson[]commit_last_checked_attimestamptzworker_hostnametextlast_heartbeat_attimestamptzcancelbooleanshould_reindexbooleanenqueuer_user_idintegertenant_id FKintegersyntactic_scip_last_index_scanrepository_id PKintegerlast_index_scan_attimestamptztenant_id FKintegertenantstenant_idtenant_id

Relationships

syntactic_scip_indexing_jobs

19 cols · no RLS · line 4843View in 3D

Stores metadata about a code intel syntactic index job.

  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Enum-like column with no CHECK state — 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.
ColumnTypeNullDefaultReferencesNotes
id PKbigintNOT NULL
commit textNOT NULLA 40-char revhash. Note that this commit may not be resolvable in the future.CHECK commit ~ CAST('^[a-f0-9]{40}$' AS text)
queued_at timestamptzNOT NULLnow()
state textNOT NULLCAST('queued' AS text)
failure_message textnull
started_at timestamptznull
finished_at timestamptznull
repository_id integerNOT NULL
process_after timestamptznull
num_resets integerNOT NULL0
num_failures integerNOT NULL0
execution_logs json[]nullAn array of [log entries](https://sourcegraph.com/github.com/sourcegraph/sourcegraph@3.23/-/blob/internal/workerutil/store.go#L48:6) (encoded as JSON) from the most recent execution.
commit_last_checked_at timestamptznull
worker_hostname textNOT NULLCAST('' AS text)
last_heartbeat_at timestamptznull
cancel booleanNOT NULLFALSE
should_reindex booleanNOT NULLFALSE
enqueuer_user_id integerNOT NULL0ID of the user who scheduled this index. Records with a non-NULL user ID are prioritised over the rest
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (enqueuer_user_id > 0, queued_at, id) WHERE state = CAST('queued' AS text) OR state = CAST('errored' AS text) syntactic_scip_indexing_jobs_dequeue_order_idx
  • (queued_at, id) syntactic_scip_indexing_jobs_queued_at_id
  • (repository_id, commit) syntactic_scip_indexing_jobs_repository_id_commit
  • (state) syntactic_scip_indexing_jobs_state

Referenced by

  • nothing

Table checks: commit ~ CAST('^[a-f0-9]{40}$' AS text)

syntactic_scip_last_index_scan

3 cols · no RLS · line 4903View in 3D

Tracks the last time repository was checked for syntactic indexing job scheduling.

  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
repository_id PKintegerNOT NULL
last_index_scan_at timestamptzNOT NULLThe last time uploads of this repository were considered for syntactic indexing job scheduling.
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

  • nothing

user 9 tables

Tables whose names start with user_.

Entity-relationship diagram: user_credentials, user_emails, user_external_accounts, user_onboarding_tour, user_pending_permissions, user_permissions, user_public_repos, user_repo_permissions, user_rolesuser_credentialsid PKbigintdomaintextuser_id FKintegerexternal_service_typetextexternal_service_idtextcreated_attimestamptzupdated_attimestamptzcredentialbyteassh_migration_appliedbooleanencryption_key_idtextgithub_app_id FKintegertenant_id FKintegeruser_emailsuser_id FKintegeremailcitextcreated_attimestamptzverification_codetextverified_attimestamptzlast_verification_sent_attimestamptzis_primarybooleantenant_id FKintegeruser_external_accountsid PKintegeruser_id FKintegerservice_typetextservice_idtextaccount_idtextauth_datatextaccount_datatextcreated_attimestamptzupdated_attimestamptzdeleted_attimestamptzclient_idtextexpired_attimestamptzlast_valid_attimestamptzencryption_key_idtexttenant_id FKintegeruser_onboarding_tourid PKintegerraw_jsontextcreated_attimestampupdated_by FKintegertenant_id FKintegeruser_pending_permissionsidbigintbind_idtextpermissiontextobject_typetextupdated_attimestamptzservice_typetextservice_idtextobject_ids_intsinteger[]tenant_id FKintegeruser_permissionsuser_idintegerpermissiontextobject_typetextupdated_attimestamptzsynced_attimestamptzobject_ids_intsinteger[]migratedbooleantenant_id FKintegeruser_public_reposuser_id FKintegerrepo_uritextrepo_id FKintegertenant_id FKintegeruser_repo_permissionsid PKbigintuser_id FKintegerrepo_id FKintegeruser_external_account_id FKintegercreated_attimestamptzupdated_attimestamptzsourcetexttenant_id FKintegeruser_rolesuser_id PKintegerrole_id PKintegercreated_attimestamptztenant_id FKintegergithub_appsreporolestenantsusersgithub_app_idtenant_iduser_idtenant_iduser_idtenant_iduser_idtenant_idupdated_bytenant_idtenant_idrepo_idtenant_iduser_idrole_idtenant_iduser_idrepo_idtenant_iduser_external_account_iduser_id

Relationships

user_credentials

12 cols · no RLS · line 5010View in 3D
  • Foreign key without index github_app_id — 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.
  • Foreign key without index tenant_id — 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.
  • Foreign key without index user_id — 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.
  • Nullable foreign key github_app_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Enum-like column with no CHECK external_service_type — 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.
  • Polymorphic reference without referential integrity external_service_type, external_service_id — 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.
  • Either/or foreign keys without a CHECK github_app_id, tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKbigintNOT NULL
domain textNOT NULL
user_id integerNOT NULLusers.id 1:N · CASCADE
external_service_type textNOT NULL
external_service_id textNOT NULL
created_at timestamptzNOT NULLnow()
updated_at timestamptzNOT NULLnow()
credential byteaNOT NULL
ssh_migration_applied booleanNOT NULLFALSE
encryption_key_id textNOT NULLCAST('' AS text)
github_app_id integernullgithub_apps.id 1:N · CASCADE
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (encryption_key_id = ANY(ARRAY[CAST('' AS text), CAST('previously-migrated' AS text)])) user_credentials_credential_idx

Referenced by

  • nothing

Table checks: github_app_id IS NULL OR external_service_type = CAST('github' AS text)

user_emails

8 cols · no RLS · line 5035View in 3D
  • 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.
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
user_id integerNOT NULLusers.id 1:N
email citextNOT NULL
created_at timestamptzNOT NULLnow()
verification_code textnull
verified_at timestamptznull
last_verification_sent_at timestamptznull
is_primary booleanNOT NULLFALSE
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • UNIQUE (user_id, is_primary) WHERE is_primary = TRUE user_emails_user_id_is_primary_idx

Referenced by

  • nothing

user_external_accounts

15 cols · no RLS · line 5046View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Enum-like column with no CHECK service_type — 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.
  • Polymorphic reference without referential integrity service_type, service_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
user_id integerNOT NULLusers.id 1:N
service_type textNOT NULL
service_id textNOT NULL
account_id textNOT NULL
auth_data textnull
account_data textnull
created_at timestamptzNOT NULLnow()
updated_at timestamptzNOT NULLnow()
deleted_at timestamptznull
client_id textNOT NULL
expired_at timestamptznull
last_valid_at timestamptznull
encryption_key_id textNOT NULLCAST('' AS text)
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • UNIQUE (service_type, service_id, client_id, account_id) WHERE deleted_at IS NULL user_external_accounts_account
  • (user_id) WHERE deleted_at IS NULL user_external_accounts_user_id
  • UNIQUE (user_id, service_type) WHERE service_type = CAST('scim' AS text) AND deleted_at IS NULL user_external_accounts_user_id_scim_service_type

Referenced by

user_onboarding_tour

5 cols · no RLS · line 5073View in 3D
  • Foreign key without index tenant_id — 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.
  • Foreign key without index updated_by — 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.
  • Nullable foreign key tenant_id — 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.
  • Nullable foreign key updated_by — 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.
  • TIMESTAMP without time zone created_at — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
raw_json textNOT NULL
created_at timestampNOT NULLnow()
updated_by integernullusers.id 1:N
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

  • nothing

user_pending_permissions

9 cols · no RLS · line 5091View in 3D
  • 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.
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Enum-like column with no CHECK object_type — 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.
  • Enum-like column with no CHECK service_type — 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.
  • Polymorphic reference without referential integrity service_type, service_id — 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.
ColumnTypeNullDefaultReferencesNotes
id bigintNOT NULL
bind_id textNOT NULL
permission textNOT NULL
object_type textNOT NULL
updated_at timestamptzNOT NULL
service_type textNOT NULL
service_id textNOT NULL
object_ids_ints integer[]NOT NULLCAST('{}' AS integer[])
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

  • nothing

user_permissions

8 cols · no RLS · line 5112View in 3D
  • 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.
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Enum-like column with no CHECK object_type — 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.
ColumnTypeNullDefaultReferencesNotes
user_id integerNOT NULL
permission textNOT NULL
object_type textNOT NULL
updated_at timestamptzNOT NULL
synced_at timestamptznull
object_ids_ints integer[]NOT NULLCAST('{}' AS integer[])
migrated booleannullTRUE
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

  • nothing

user_public_repos

4 cols · no RLS · line 5123View in 3D
  • 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.
  • Foreign key without index repo_id — 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.
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
user_id integerNOT NULLusers.id 1:N · CASCADE
repo_uri textNOT NULL
repo_id integerNOT NULLrepo.id 1:N · CASCADE
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

  • nothing

user_repo_permissions

8 cols · no RLS · line 5130View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Nullable foreign key user_external_account_id — 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.
  • Nullable foreign key user_id — 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.
  • Either/or foreign keys without a CHECK tenant_id, user_external_account_id, user_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKbigintNOT NULL
user_id integernullusers.id 1:N · CASCADE
repo_id integerNOT NULLrepo.id 1:N · CASCADE
user_external_account_id integernulluser_external_accounts.id 1:N · CASCADE
created_at timestamptzNOT NULLnow()
updated_at timestamptzNOT NULLnow()
source textNOT NULLCAST('sync' AS text)
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • UNIQUE (user_id, user_external_account_id, repo_id) user_repo_permissions_perms_unique_idx
  • (repo_id) user_repo_permissions_repo_id_idx
  • (source) user_repo_permissions_source_idx
  • (updated_at) user_repo_permissions_updated_at_idx
  • (user_external_account_id) user_repo_permissions_user_external_account_id_idx

Referenced by

  • nothing

user_roles

4 cols · no RLS · line 5150View in 3D
  • Foreign key without index role_id — 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.
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
user_id PKintegerNOT NULLusers.id 1:N · CASCADE
role_id PKintegerNOT NULLroles.id 1:N · CASCADE
created_at timestamptzNOT NULLnow()
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

  • nothing

vulnerability 3 tables

Tables whose names start with vulnerability_.

Entity-relationship diagram: vulnerability_affected_packages, vulnerability_affected_symbols, vulnerability_matchesvulnerability_affected_packagesid PKintegervulnerability_id FKintegerpackage_nametextlanguagetextnamespacetextversion_constrainttext[]fixedbooleanfixed_intexttenant_id FKintegervulnerability_affected_symbolsid PKintegervulnerability_affected_package_id FKintegerpathtextsymbolstext[]tenant_id FKintegervulnerability_matchesid PKintegerupload_id FKintegervulnerability_affected_package_id FKintegertenant_id FKintegerlsif_uploadstenantsvulnerabilitiesvulnerability_idtenant_idvulnerability_affected_package_idtenant_idupload_idvulnerability_affected_package_idtenant_id

Relationships

vulnerability_affected_packages

9 cols · no RLS · line 5204View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
vulnerability_id integerNOT NULLvulnerabilities.id 1:N · CASCADE
package_name textNOT NULL
language textNOT NULL
namespace textNOT NULL
version_constraint text[]NOT NULL
fixed booleanNOT NULL
fixed_in textnull
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • UNIQUE (vulnerability_id, package_name) vulnerability_affected_packages_vulnerability_id_package_name

Referenced by

vulnerability_affected_symbols

5 cols · no RLS · line 5226View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
vulnerability_affected_package_id integerNOT NULLvulnerability_affected_packages.id 1:N · CASCADE
path textNOT NULL
symbols text[]NOT NULL
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • UNIQUE (vulnerability_affected_package_id, path) vulnerability_affected_symbols_vulnerability_affected_package_i

Referenced by

  • nothing

vulnerability_matches

4 cols · no RLS · line 5244View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
upload_id integerNOT NULLlsif_uploads.id 1:N · CASCADE
vulnerability_affected_package_id integerNOT NULLvulnerability_affected_packages.id 1:N · CASCADE
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • UNIQUE (upload_id, vulnerability_affected_package_id) vulnerability_matches_upload_id_vulnerability_affected_package_
  • (vulnerability_affected_package_id) vulnerability_matches_vulnerability_affected_package_id

Referenced by

  • nothing

misc 39 tables

Tables whose name prefix is theirs alone.

Entity-relationship diagram: aggregated_user_statistics, changesets, cached_available_indexers, code_hosts, commit_authors, configuration_policies_audit_logs, context_detection_embedding_jobs, critical_and_site_config, explicit_permissions_bitbucket_projects_jobs, global_state, names, namespace_permissions, notebook_stars, notebooks, ownership_path_stats, permission_sync_jobs, permissions, phabricator_repos, prompts, users, query_runner_state, redis_key_value, role_permissions, roles, saved_searches, security_event_logs, settings, sub_repo_permissions, survey_responses, team_members, teams, telemetry_events_export_queue, temporary_settings, tenants, versions, vulnerabilities, webhook_logs, webhooks, zoekt_reposaggregated_user_statisticsuser_id PKbigintcreated_attimestamptzupdated_attimestamptzuser_last_active_attimestamptzuser_events_countbiginttenant_id FKintegerchangesetsid PKbigintbatch_change_idsjsonbrepo_id FKintegercreated_attimestamptzupdated_attimestamptzmetadatajsonbexternal_idtextexternal_service_typetextexternal_deleted_attimestamptzexternal_branchtextexternal_updated_attimestamptzexternal_statetextexternal_review_statetextexternal_check_statetextdiff_stat_addedintegerdiff_stat_deletedintegersync_statejsonbcurrent_spec_id FKbigintprevious_spec_id FKbigintpublication_statetextowned_by_batch_change_id FKbigintreconciler_statetextfailure_messagetextstarted_attimestamptzfinished_attimestamptzprocess_aftertimestamptznum_resetsintegerclosingbooleannum_failuresintegerlog_contentstextexecution_logsjson[]syncer_errortextexternal_titletextworker_hostnametextui_publication_statebatch_changes_changeset_ui_publication_statelast_heartbeat_attimestamptzexternal_fork_namespacecitextqueued_attimestamptzcancelbooleandetached_attimestamptzcomputed_statetextexternal_fork_namecitextprevious_failure_messagetextcommit_verificationjsonbtenant_id FKintegercached_available_indexersid PKintegerrepository_idintegernum_eventsintegeravailable_indexersjsonbtenant_id FKintegercode_hostsid PKintegerkindtexturltextapi_rate_limit_quotaintegerapi_rate_limit_interval_secondsintegergit_rate_limit_quotaintegergit_rate_limit_interval_secondsintegercreated_attimestamptzupdated_attimestamptztenant_id FKintegercommit_authorsid PKintegeremailtextnametexttenant_id FKintegerconfiguration_policies_audit_logslog_timestamptimestamptzrecord_deleted_attimestamptzpolicy_idintegertransition_columnshstore[]sequencebigintoperationaudit_log_operationtenant_id FKintegercontext_detection_embedding_jobsid PKintegerstatetextfailure_messagetextqueued_attimestamptzstarted_attimestamptzfinished_attimestamptzprocess_aftertimestamptznum_resetsintegernum_failuresintegerlast_heartbeat_attimestamptzexecution_logsjson[]worker_hostnametextcancelbooleantenant_id FKintegercritical_and_site_configid PKintegertypecritical_or_sitecontentstextcreated_attimestamptzupdated_attimestamptzauthor_user_idintegerredacted_contentstextexplicit_permissions_bitbucket_projects_jobsid PKintegerstatetextfailure_messagetextqueued_attimestamptzstarted_attimestamptzfinished_attimestamptzprocess_aftertimestamptznum_resetsintegernum_failuresintegerlast_heartbeat_attimestamptzexecution_logsjson[]worker_hostnametextproject_keytextexternal_service_idintegerpermissionsjson[]unrestrictedbooleancancelbooleantenant_id FKintegerglobal_statesite_id PKuuidinitializedbooleantenant_id FKintegernamesname PKcitextuser_id FKintegerorg_id FKintegerteam_id FKintegertenant_id FKintegernamespace_permissionsid PKintegernamespacetextresource_idintegeruser_id FKintegercreated_attimestamptztenant_id FKintegernotebook_starsnotebook_id PKintegeruser_id PKintegercreated_attimestamptztenant_id FKintegernotebooksid PKbiginttitletextblocksjsonbpublicbooleancreator_user_id FKintegercreated_attimestamptzupdated_attimestamptzblocks_tsvectortsvectornamespace_user_id FKintegernamespace_org_id FKintegerupdater_user_id FKintegerpattern_typepattern_typetenant_id FKintegerownership_path_statsfile_path_id PKintegertree_codeowned_files_countintegerlast_updated_attimestamptree_assigned_ownership_files_countintegertree_any_ownership_files_countintegertenant_id FKintegerpermission_sync_jobsid PKintegerstatetextreasontextfailure_messagetextqueued_attimestamptzstarted_attimestamptzfinished_attimestamptzprocess_aftertimestamptznum_resetsintegernum_failuresintegerlast_heartbeat_attimestamptzexecution_logsjson[]worker_hostnametextcancelbooleanrepository_id FKintegeruser_id FKintegertriggered_by_user_id FKintegerpriorityintegerinvalidate_cachesbooleancancellation_reasontextno_permsbooleanpermissions_addedintegerpermissions_removedintegerpermissions_foundintegercode_host_statesjson[]is_partial_successbooleantenant_id FKintegerpermissionsid PKintegernamespacetextactiontextcreated_attimestamptztenant_id FKintegerphabricator_reposid PKintegercallsigncitextrepo_namecitextcreated_attimestamptzupdated_attimestamptzdeleted_attimestamptzurltexttenant_id FKintegerpromptsid PKintegernamecitextdescriptiontextdefinition_texttextdraftbooleanvisibility_secretbooleanowner_user_id FKintegerowner_org_id FKintegercreated_by FKintegercreated_attimestamptzupdated_by FKintegerupdated_attimestamptztenant_id FKintegerusersid PKintegerusernamecitextdisplay_nametextavatar_urltextcreated_attimestamptzupdated_attimestamptzdeleted_attimestamptzinvite_quotaintegerpasswdtextpasswd_reset_codetextpasswd_reset_timetimestamptzsite_adminbooleanpage_viewsintegersearch_queriesintegerbilling_customer_idtextinvalidated_sessions_attimestamptztos_acceptedbooleancompletions_quotaintegercode_completions_quotaintegercompleted_post_signupbooleancody_pro_enabled_attimestamptztenant_id FKintegerquery_runner_statequerytextlast_executedtimestamptzlatest_resulttimestamptzexec_duration_nsbiginttenant_id FKintegerredis_key_valuenamespace PKtextkey PKtextvaluebyteatenant_id FKintegerrole_permissionsrole_id PKintegerpermission_id PKintegercreated_attimestamptztenant_id FKintegerrolesid PKintegercreated_attimestamptzsystembooleannamecitexttenant_id FKintegersaved_searchesid PKintegerdescriptiontextquerytextcreated_attimestamptzupdated_attimestamptznotify_ownerbooleannotify_slackbooleanuser_id FKintegerorg_id FKintegerslack_webhook_urltextcreated_by FKintegerupdated_by FKintegerdraftbooleanvisibility_secretbooleantenant_id FKintegersecurity_event_logsid PKbigintnametexturltextuser_idintegeranonymous_user_idtextsourcetextargumentjsonbversiontexttimestamptimestamptztenant_id FKintegersettingsid PKintegerorg_id FKintegercontentstextcreated_attimestamptzuser_id FKintegerauthor_user_id FKintegertenant_id FKintegersub_repo_permissionsrepo_id FKintegeruser_id FKintegerversionintegerupdated_attimestamptzpathstext[]ipstext[]tenant_id FKintegersurvey_responsesid PKbigintuser_id FKintegeremailtextscoreintegerreasontextbettertextcreated_attimestamptzuse_casestext[]other_use_casetexttenant_id FKintegerteam_membersteam_id PKintegeruser_id PKintegercreated_attimestamptzupdated_attimestamptztenant_id FKintegerteamsid PKintegernamecitextdisplay_nametextreadonlybooleanparent_team_id FKintegercreator_id FKintegercreated_attimestamptzupdated_attimestamptztenant_id FKintegertelemetry_events_export_queueid PKtexttimestamptimestamptzpayload_pbbyteaexported_attimestamptztenant_id FKintegertemporary_settingsid PKintegeruser_id FKintegercontentsjsonbcreated_attimestamptzupdated_attimestamptztenant_id FKintegertenantsid PKbigintnametextcreated_attimestamptzupdated_attimestamptzversionsservice PKtextversiontextupdated_attimestamptzfirst_versiontextauto_upgradebooleanvulnerabilitiesid PKintegersource_idtextsummarytextdetailstextcpestext[]cwestext[]aliasestext[]relatedtext[]data_sourcetexturlstext[]severitytextcvss_vectortextcvss_scoretextpublished_attimestamptzmodified_attimestamptzwithdrawn_attimestamptztenant_id FKintegerwebhook_logsid PKbigintreceived_attimestamptzexternal_service_id FKintegerstatus_codeintegerrequestbytearesponsebyteaencryption_key_idtextwebhook_id FKintegertenant_id FKintegerwebhooksid PKintegercode_host_kindtextcode_host_urntextsecrettextcreated_attimestamptzupdated_attimestamptzencryption_key_idtextuuiduuidcreated_by_user_id FKintegerupdated_by_user_id FKintegernametexttenant_id FKintegerzoekt_reposrepo_id PKintegerbranchesjsonbindex_statustextupdated_attimestamptzcreated_attimestamptzlast_indexed_attimestamptztenant_id FKintegerbatch_changeschangeset_specsexternal_servicesorgsreporepo_pathsparent_team_idtenant_idcurrent_spec_idowned_by_batch_change_idprevious_spec_idrepo_idtenant_idtenant_idtenant_idtenant_idtenant_idtenant_idtenant_idfile_path_idtenant_idtenant_idtenant_idtenant_idtenant_idtenant_idtenant_idtenant_idtenant_idtenant_idrepo_idtenant_idtenant_iduser_idtenant_iduser_idcreator_user_idnamespace_org_idnamespace_user_idtenant_idupdater_user_idrepository_idtenant_idtriggered_by_user_iduser_idcreated_byowner_org_idowner_user_idtenant_idupdated_bypermission_idrole_idtenant_idcreated_byorg_idtenant_idupdated_byuser_idauthor_user_idorg_idtenant_iduser_idrepo_idtenant_iduser_idtenant_iduser_idcreator_idtenant_idtenant_iduser_idcreated_by_user_idtenant_idupdated_by_user_idorg_idteam_idtenant_iduser_idnotebook_idtenant_iduser_idteam_idtenant_iduser_idexternal_service_idtenant_idwebhook_id

Relationships

aggregated_user_statistics

6 cols · no RLS · line 919View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
user_id PKbigintNOT NULLusers.id 1:1 · CASCADE
created_at timestamptzNOT NULLnow()
updated_at timestamptzNOT NULLnow()
user_last_active_at timestamptznull
user_events_count bigintnull
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

  • nothing

changesets

45 cols · no RLS · line 1254View in 3D
  • Foreign key without index owned_by_batch_change_id — 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.
  • Foreign key without index previous_spec_id — 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.
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key current_spec_id — 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.
  • Nullable foreign key owned_by_batch_change_id — 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.
  • Nullable foreign key previous_spec_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Enum-like column with no CHECK external_state — 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.
  • Enum-like column with no CHECK external_review_state — 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.
  • Enum-like column with no CHECK external_check_state — 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.
  • Enum-like column with no CHECK publication_state — 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.
  • Enum-like column with no CHECK reconciler_state — 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.
  • Enum-like column with no CHECK computed_state — 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.
  • Either/or foreign keys without a CHECK current_spec_id, owned_by_batch_change_id, previous_spec_id, tenant_id — 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.
  • 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.
ColumnTypeNullDefaultReferencesNotes
id PKbigintNOT NULL
batch_change_ids jsonbNOT NULLCAST('{}' AS jsonb)CHECK jsonb_typeof(batch_change_ids) = CAST('object' AS text)
repo_id integerNOT NULLrepo.id 1:N · CASCADE
created_at timestamptzNOT NULLnow()
updated_at timestamptzNOT NULLnow()
metadata jsonbnullCAST('{}' AS jsonb)CHECK jsonb_typeof(metadata) = CAST('object' AS text)
external_id textnullCHECK external_id <> CAST('' AS text)
external_service_type textNOT NULLCHECK external_service_type <> CAST('' AS text)
external_deleted_at timestamptznull
external_branch textnullCHECK external_branch ~~ CAST('refs/heads/%' AS text)
external_updated_at timestamptznull
external_state textnull
external_review_state textnull
external_check_state textnull
diff_stat_added integernull
diff_stat_deleted integernull
sync_state jsonbNOT NULLCAST('{}' AS jsonb)
current_spec_id bigintnullchangeset_specs.id 1:N
previous_spec_id bigintnullchangeset_specs.id 1:N
publication_state textnullCAST('UNPUBLISHED' AS text)
owned_by_batch_change_id bigintnullbatch_changes.id 1:N · SET NULL
reconciler_state textnullCAST('queued' AS text)
failure_message textnull
started_at timestamptznull
finished_at timestamptznull
process_after timestamptznull
num_resets integerNOT NULL0
closing booleanNOT NULLFALSE
num_failures integerNOT NULL0
log_contents textnull
execution_logs json[]null
syncer_error textnull
external_title textnullNormalized property generated on save using Changeset.Title()
worker_hostname textNOT NULLCAST('' AS text)
ui_publication_state batch_changes_changeset_ui_publication_statenull
last_heartbeat_at timestamptznull
external_fork_namespace citextnull
queued_at timestamptznullnow()
cancel booleanNOT NULLFALSE
detached_at timestamptznull
computed_state textNOT NULL
external_fork_name citextnull
previous_failure_message textnull
commit_verification jsonbNOT NULLCAST('{}' AS jsonb)
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (batch_change_ids) changesets_batch_change_ids
  • (((metadata -> CAST('source' AS text)) -> CAST('commit' AS text)) ->> CAST('hash' AS text)) changesets_bitbucket_cloud_metadata_source_commit_idx
  • (current_spec_id, previous_spec_id) changesets_changeset_specs
  • (computed_state) changesets_computed_state
  • (detached_at) changesets_detached_at
  • (external_state) changesets_external_state_idx
  • (external_title) changesets_external_title_idx
  • (publication_state) changesets_publication_state_idx
  • (reconciler_state) changesets_reconciler_state_idx

Referenced by

Table checks: jsonb_typeof(batch_change_ids) = CAST('object' AS text); external_id <> CAST('' AS text); external_service_type <> CAST('' AS text); jsonb_typeof(metadata) = CAST('object' AS text); external_branch ~~ CAST('refs/heads/%' AS text)

cached_available_indexers

5 cols · no RLS · line 1351View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
repository_id integerNOT NULL
num_events integerNOT NULL
available_indexers jsonbNOT NULL
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (num_events) WHERE CAST(available_indexers AS text) <> CAST('{}' AS text) cached_available_indexers_num_events
  • UNIQUE (repository_id) cached_available_indexers_repository_id

Referenced by

  • nothing

code_hosts

10 cols · no RLS · line 1686View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Enum-like column with no CHECK kind — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
kind textNOT NULL
url UKtextNOT NULL
api_rate_limit_quota integernull
api_rate_limit_interval_seconds integernull
git_rate_limit_quota integernull
git_rate_limit_interval_seconds integernull
created_at timestamptzNOT NULLnow()
updated_at timestamptzNOT NULLnow()
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

commit_authors

4 cols · no RLS · line 2116View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
email textNOT NULL
name textNOT NULL
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • UNIQUE (email, name) commit_authors_email_name

Referenced by

configuration_policies_audit_logs

7 cols · no RLS · line 2133View in 3D
  • 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.
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
log_timestamp timestamptznullclock_timestamp()Timestamp for this log entry.
record_deleted_at timestamptznullSet once the upload this entry is associated with is deleted. Once NOW() - record_deleted_at is above a certain threshold, this log entry will be deleted.
policy_id integerNOT NULL
transition_columns hstore[]nullArray of changes that occurred to the upload for this entry, in the form of {"column"=>"<column name>", "old"=>"<previous value>", "new"=>"<new value>"}.
sequence bigintNOT NULL
operation audit_log_operationNOT NULL
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (policy_id) configuration_policies_audit_logs_policy_id
  • (log_timestamp) configuration_policies_audit_logs_timestamp

Referenced by

  • nothing

context_detection_embedding_jobs

14 cols · no RLS · line 2158View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Enum-like column with no CHECK state — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
state textnullCAST('queued' AS text)
failure_message textnull
queued_at timestamptznullnow()
started_at timestamptznull
finished_at timestamptznull
process_after timestamptznull
num_resets integerNOT NULL0
num_failures integerNOT NULL0
last_heartbeat_at timestamptznull
execution_logs json[]null
worker_hostname textNOT NULLCAST('' AS text)
cancel booleanNOT NULLFALSE
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

  • nothing

critical_and_site_config

7 cols · no RLS · line 2185View in 3D
  • 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
type critical_or_siteNOT NULL
contents textNOT NULL
created_at timestamptzNOT NULLnow()
updated_at timestamptzNOT NULLnow()
author_user_id integernullA null value indicates that this config was most likely added by code on the start-up path, for example from the SITE_CONFIG_FILE unless the config itself was added before this column existed in which case it could also have been a user.
redacted_contents textnullThis column stores the contents but redacts all secrets. The redacted form is a sha256 hash of the secret appended to the REDACTED string. This is used to generate diffs between two subsequent changes in a way that allows us to detect changes to any secrets while also ensuring that we do not leak it in the diff. A null value indicates that this config was added before this column was added or redacting the secrets during write failed so we skipped writing to this column instead of a hard failure.

Indexes

  • UNIQUE (id, type) critical_and_site_config_unique

Referenced by

  • nothing

explicit_permissions_bitbucket_projects_jobs

18 cols · no RLS · line 2598View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Enum-like column with no CHECK state — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
state textnullCAST('queued' AS text)
failure_message textnull
queued_at timestamptznullnow()
started_at timestamptznull
finished_at timestamptznull
process_after timestamptznull
num_resets integerNOT NULL0
num_failures integerNOT NULL0
last_heartbeat_at timestamptznull
execution_logs json[]null
worker_hostname textNOT NULLCAST('' AS text)
project_key textNOT NULL
external_service_id integerNOT NULL
permissions json[]null
unrestricted booleanNOT NULLFALSE
cancel booleanNOT NULLFALSE
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (project_key, external_service_id, state) explicit_permissions_bitbucket_projects_jobs_project_key_extern
  • (queued_at) explicit_permissions_bitbucket_projects_jobs_queued_at_idx
  • (state) explicit_permissions_bitbucket_projects_jobs_state_idx

Referenced by

  • nothing

Table checks: (permissions IS NOT NULL AND …(unrestricted)) OR (permissions IS NULL AND …(unrestricted))

global_state

3 cols · no RLS · line 2933View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
site_id PKuuidNOT NULL
initialized booleanNOT NULLFALSE
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

  • nothing

names

5 cols · no RLS · line 3614View in 3D
  • Foreign key without index org_id — 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.
  • Foreign key without index team_id — 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.
  • Foreign key without index tenant_id — 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.
  • Foreign key without index user_id — 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.
  • Nullable foreign key org_id — 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.
  • Nullable foreign key team_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Nullable foreign key user_id — 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.
  • Either/or foreign keys without a CHECK org_id, team_id, tenant_id, user_id — 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.
ColumnTypeNullDefaultReferencesNotes
name PKcitextNOT NULL
user_id integernullusers.id 1:N · CASCADE
org_id integernullorgs.id 1:N · CASCADE
team_id integernullteams.id 1:N · CASCADE
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

  • nothing

Table checks: user_id IS NOT NULL OR org_id IS NOT NULL OR team_id IS NOT NULL

namespace_permissions

6 cols · no RLS · line 3623View in 3D
  • Foreign key without index tenant_id — 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.
  • Foreign key without index user_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
namespace textNOT NULLCHECK namespace <> CAST('' AS text)
resource_id integerNOT NULL
user_id integerNOT NULLusers.id 1:N · CASCADE
created_at timestamptzNOT NULLnow()
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • UNIQUE (namespace, resource_id, user_id) unique_resource_permission

Referenced by

  • nothing

Table checks: namespace <> CAST('' AS text)

notebook_stars

4 cols · no RLS · line 3643View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
notebook_id PKintegerNOT NULLnotebooks.id 1:N · CASCADE
user_id PKintegerNOT NULLusers.id 1:N · CASCADE
created_at timestamptzNOT NULLnow()
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (user_id) notebook_stars_user_id_idx

Referenced by

  • nothing

notebooks

13 cols · no RLS · line 3650View in 3D
  • Foreign key without index creator_user_id — 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.
  • Foreign key without index tenant_id — 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.
  • Foreign key without index updater_user_id — 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.
  • Nullable foreign key creator_user_id — 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.
  • Nullable foreign key namespace_org_id — 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.
  • Nullable foreign key namespace_user_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Nullable foreign key updater_user_id — 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.
  • Either/or foreign keys without a CHECK creator_user_id, namespace_org_id, namespace_user_id, tenant_id, updater_user_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKbigintNOT NULL
title textNOT NULL
blocks jsonbNOT NULLCAST('[]' AS jsonb)CHECK jsonb_typeof(blocks) = CAST('array' AS text)
public booleanNOT NULL
creator_user_id integernullusers.id 1:N · SET NULL
created_at timestamptzNOT NULLnow()
updated_at timestamptzNOT NULLnow()
blocks_tsvector tsvectornull
namespace_user_id integernullusers.id 1:N · SET NULL
namespace_org_id integernullorgs.id 1:N · SET NULL
updater_user_id integernullusers.id 1:N · SET NULL
pattern_type pattern_typeNOT NULLCAST('keyword' AS pattern_type)
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (blocks_tsvector) notebooks_blocks_tsvector_idx
  • (namespace_org_id) notebooks_namespace_org_id_idx
  • (namespace_user_id) notebooks_namespace_user_id_idx
  • (title) notebooks_title_trgm_idx

Referenced by

Table checks: jsonb_typeof(blocks) = CAST('array' AS text); (namespace_user_id IS NULL AND namespace_org_id IS NULL) OR namespace_user_id IS NULL <> namespace_org_id IS NULL

ownership_path_stats

6 cols · no RLS · line 4092View in 3D

Data on how many files in given tree are owned by anyone. We choose to have a table for `ownership_path_stats` - more general than for CODEOWNERS, with a specific tree_codeowned_files_count CODEOWNERS column. The reason for that is that we aim at expanding path stats by including total owned files (via CODEOWNERS or assigned ownership), and perhaps files count by assigned ownership only.

  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
  • TIMESTAMP without time zone last_updated_at — 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.
ColumnTypeNullDefaultReferencesNotes
file_path_id PKintegerNOT NULLrepo_paths.id 1:1
tree_codeowned_files_count integernull
last_updated_at timestampNOT NULLWhen the last background job updating counts run.
tree_assigned_ownership_files_count integernull
tree_any_ownership_files_count integernull
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

  • nothing

permission_sync_jobs

27 cols · no RLS · line 4151View in 3D
  • Foreign key without index tenant_id — 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.
  • Foreign key without index triggered_by_user_id — 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.
  • Nullable foreign key repository_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Nullable foreign key triggered_by_user_id — 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.
  • Nullable foreign key user_id — 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.
  • Enum-like column with no CHECK state — 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.
  • Either/or foreign keys without a CHECK repository_id, tenant_id, triggered_by_user_id, user_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
state textnullCAST('queued' AS text)
reason textNOT NULLSpecifies why permissions sync job was triggered.
failure_message textnull
queued_at timestamptznullnow()
started_at timestamptznull
finished_at timestamptznull
process_after timestamptznull
num_resets integerNOT NULL0
num_failures integerNOT NULL0
last_heartbeat_at timestamptznull
execution_logs json[]null
worker_hostname textNOT NULLCAST('' AS text)
cancel booleanNOT NULLFALSE
repository_id integernullrepo.id 1:N · CASCADE
user_id integernullusers.id 1:N · CASCADE
triggered_by_user_id integernullusers.id 1:N · SET NULLSpecifies an ID of a user who triggered a sync.
priority integerNOT NULL0Specifies numeric priority for the permissions sync job.
invalidate_caches booleanNOT NULLFALSE
cancellation_reason textnullSpecifies why permissions sync job was cancelled.
no_perms booleanNOT NULLFALSE
permissions_added integerNOT NULL0
permissions_removed integerNOT NULL0
permissions_found integerNOT NULL0
code_host_states json[]null
is_partial_success booleannullFALSE
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (process_after) permission_sync_jobs_process_after
  • (repository_id) permission_sync_jobs_repository_id
  • (state) permission_sync_jobs_state
  • UNIQUE (priority, user_id, repository_id, cancel, process_after) WHERE state = CAST('queued' AS text) permission_sync_jobs_unique
  • (user_id) permission_sync_jobs_user_id

Referenced by

  • nothing

Table checks: user_id IS NULL <> repository_id IS NULL

permissions

5 cols · no RLS · line 4200View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
namespace textNOT NULLCHECK namespace <> CAST('' AS text)
action textNOT NULLCHECK action <> CAST('' AS text)
created_at timestamptzNOT NULLnow()
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • UNIQUE (namespace, action) permissions_unique_namespace_action

Referenced by

Table checks: action <> CAST('' AS text); namespace <> CAST('' AS text)

phabricator_repos

8 cols · no RLS · line 4220View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Soft delete collides with non-partial UNIQUE repo_name — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
callsign citextNOT NULL
repo_name UKcitextNOT NULL
created_at timestamptzNOT NULLnow()
updated_at timestamptzNOT NULLnow()
deleted_at timestamptznull
url textNOT NULLCAST('' AS text)
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

  • nothing

prompts

13 cols · no RLS · line 4288View in 3D
  • Foreign key without index created_by — 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.
  • Foreign key without index tenant_id — 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.
  • Foreign key without index updated_by — 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.
  • Nullable foreign key created_by — 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.
  • Nullable foreign key owner_org_id — 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.
  • Nullable foreign key owner_user_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Nullable foreign key updated_by — 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.
  • Either/or foreign keys without a CHECK owner_org_id, owner_user_id, tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
name citextNOT NULLCHECK char_length(CAST(name AS text)) <= 255
description textNOT NULLCHECK char_length(description) <= (1024 * 50)
definition_text textNOT NULLCHECK char_length(definition_text) <= (1024 * 100)
draft booleanNOT NULLFALSE
visibility_secret booleanNOT NULLTRUE
owner_user_id integernullusers.id 1:N · CASCADE
owner_org_id integernullorgs.id 1:N · CASCADE
created_by integernullusers.id 1:N · SET NULL
created_at timestamptzNOT NULLnow()
updated_by integernullusers.id 1:N · SET NULL
updated_at timestamptzNOT NULLnow()
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • UNIQUE (owner_org_id, name) WHERE owner_org_id IS NOT NULL prompts_name_is_unique_in_owner_org
  • UNIQUE (owner_user_id, name) WHERE owner_user_id IS NOT NULL prompts_name_is_unique_in_owner_user

Referenced by

  • nothing

Table checks: char_length(definition_text) <= (1024 * 100); char_length(description) <= (1024 * 50); (owner_user_id IS NOT NULL AND owner_org_id IS NULL) OR (owner_org_id IS NOT NULL AND owner_user_id IS NULL); char_length(CAST(name AS text)) <= 255; name ~ CAST('^[a-zA-Z0-9](?:[a-zA-Z0-9]|[-.](?=[a-zA-Z0-9]))*-?$' AS citext)

users

22 cols · no RLS · line 4319View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
  • 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
username citextNOT NULLCHECK char_length(CAST(username AS text)) <= 255
display_name textnullCHECK char_length(display_name) <= 255
avatar_url textnull
created_at timestamptzNOT NULLnow()
updated_at timestamptzNOT NULLnow()
deleted_at timestamptznull
invite_quota integerNOT NULL100
passwd textnull
passwd_reset_code textnull
passwd_reset_time timestamptznull
site_admin booleanNOT NULLFALSE
page_views integerNOT NULL0
search_queries integerNOT NULL0
billing_customer_id textnull
invalidated_sessions_at timestamptzNOT NULLnow()
tos_accepted booleanNOT NULLFALSE
completions_quota integernull
code_completions_quota integernull
completed_post_signup booleanNOT NULLFALSE
cody_pro_enabled_at timestamptznull
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • UNIQUE (billing_customer_id) WHERE deleted_at IS NULL users_billing_customer_id
  • (created_at) users_created_at_idx
  • UNIQUE (username) WHERE deleted_at IS NULL users_username

Referenced by

Table checks: char_length(display_name) <= 255; char_length(CAST(username AS text)) <= 255; username ~ CAST('^\w(?:\w|[-.](?=\w))*-?$' AS citext)

query_runner_state

5 cols · no RLS · line 4365View in 3D
  • 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.
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
query textnull
last_executed timestamptznull
latest_result timestamptznull
exec_duration_ns bigintnull
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

  • nothing

redis_key_value

4 cols · no RLS · line 4425View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
namespace PKtextNOT NULL
key PKtextNOT NULL
value byteaNOT NULL
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

  • nothing

role_permissions

4 cols · no RLS · line 4631View in 3D
  • Foreign key without index role_id — 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.
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
role_id PKintegerNOT NULLroles.id 1:N · CASCADE
permission_id PKintegerNOT NULLpermissions.id 1:N · CASCADE
created_at timestamptzNOT NULLnow()
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

  • nothing

roles

5 cols · no RLS · line 4638View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
created_at timestamptzNOT NULLnow()
system booleanNOT NULLFALSEThis is used to indicate whether a role is read-only or can be modified.
name citextNOT NULL
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • UNIQUE (name) unique_role_name

Referenced by

saved_searches

15 cols · no RLS · line 4658View in 3D
  • Foreign key without index created_by — 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.
  • Foreign key without index org_id — 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.
  • Foreign key without index tenant_id — 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.
  • Foreign key without index updated_by — 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.
  • Foreign key without index user_id — 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.
  • Nullable foreign key created_by — 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.
  • Nullable foreign key org_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Nullable foreign key updated_by — 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.
  • Nullable foreign key user_id — 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.
  • Either/or foreign keys without a CHECK org_id, tenant_id, user_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
description textNOT NULL
query textNOT NULL
created_at timestamptzNOT NULLnow()
updated_at timestamptzNOT NULLnow()
notify_owner booleanNOT NULLFALSE
notify_slack booleanNOT NULLFALSE
user_id integernullusers.id 1:N
org_id integernullorgs.id 1:N
slack_webhook_url textnull
created_by integernullusers.id 1:N · SET NULL
updated_by integernullusers.id 1:N · SET NULL
draft booleanNOT NULLFALSE
visibility_secret booleanNOT NULLTRUE
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

  • nothing

Table checks: notify_owner = FALSE AND notify_slack = FALSE; (user_id IS NOT NULL AND org_id IS NULL) OR (org_id IS NOT NULL AND user_id IS NULL)

security_event_logs

10 cols · no RLS · line 4737View in 3D

Contains security-relevant events with a long time horizon for storage.

  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKbigintNOT NULL
name textNOT NULLThe event name as a CAPITALIZED_SNAKE_CASE string.CHECK name <> CAST('' AS text)
url textNOT NULLThe URL within the Sourcegraph app which generated the event.
user_id integerNOT NULLThe ID of the actor associated with the event.
anonymous_user_id textNOT NULLThe UUID of the actor associated with the event.
source textNOT NULLThe site section (WEB, BACKEND, etc.) that generated the event.CHECK source <> CAST('' AS text)
argument jsonbNOT NULLAn arbitrary JSON blob containing event data.
version textNOT NULLThe version of Sourcegraph which generated the event.CHECK version <> CAST('' AS text)
timestamp timestamptzNOT NULL
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (timestamp) security_event_logs_timestamp

Referenced by

  • nothing

Table checks: (user_id = 0 AND anonymous_user_id <> CAST('' AS text)) OR (user_id <> 0 AND anonymous_user_id = CAST('' AS text)) OR (user_id <> 0 AND anonymous_user_id <> CAST('' AS text)); name <> CAST('' AS text); source <> CAST('' AS text); version <> CAST('' AS text)

settings

7 cols · no RLS · line 4779View in 3D
  • Foreign key without index author_user_id — 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.
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key author_user_id — 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.
  • Nullable foreign key org_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Nullable foreign key user_id — 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.
  • Either/or foreign keys without a CHECK author_user_id, org_id, tenant_id, user_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
org_id integernullorgs.id 1:N · RESTRICT
contents textNOT NULLCAST('{}' AS text)CHECK contents <> CAST('' AS text)
created_at timestamptzNOT NULLnow()
user_id integernullusers.id 1:N · RESTRICT
author_user_id integernullusers.id 1:N · RESTRICT
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (id) WHERE user_id IS NULL AND org_id IS NULL settings_global_id
  • (org_id) settings_org_id_idx
  • (user_id) settings_user_id_idx

Referenced by

  • nothing

Table checks: contents <> CAST('' AS text)

sub_repo_permissions

7 cols · no RLS · line 4804View in 3D

Responsible for storing permissions at a finer granularity than repo

  • 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.
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
repo_id integerNOT NULLrepo.id 1:N · CASCADE
user_id integerNOT NULLusers.id 1:N · CASCADE
version integerNOT NULL1
updated_at timestamptzNOT NULLnow()
paths text[]nullPaths that begin with a minus sign (-) are exclusion paths.
ips text[]nullIP addresses corresponding to each path. IP in slot 0 in the array corresponds to path the in slot 0 of the path array, etc. NULL if not yet migrated, empty array for no IP restrictions.
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • UNIQUE (repo_id, user_id, version) sub_repo_permissions_repo_id_user_id_version_uindex
  • (user_id) sub_repo_perms_user_id

Referenced by

  • nothing

Table checks: ips IS NULL OR (array_length(ips, 1) = array_length(paths, 1) AND NOT CAST('' AS text) = ANY(ips))

survey_responses

10 cols · no RLS · line 4821View in 3D
  • Foreign key without index tenant_id — 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.
  • Foreign key without index user_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Nullable foreign key user_id — 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.
  • Either/or foreign keys without a CHECK tenant_id, user_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKbigintNOT NULL
user_id integernullusers.id 1:N
email textnull
score integerNOT NULL
reason textnull
better textnull
created_at timestamptzNOT NULLnow()
use_cases text[]null
other_use_case textnull
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

  • nothing

team_members

5 cols · no RLS · line 4913View in 3D
  • Foreign key without index tenant_id — 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.
  • Foreign key without index user_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
team_id PKintegerNOT NULLteams.id 1:N · CASCADE
user_id PKintegerNOT NULLusers.id 1:N · CASCADE
created_at timestamptzNOT NULLnow()
updated_at timestamptzNOT NULLnow()
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

  • nothing

teams

9 cols · no RLS · line 4921View in 3D
  • Foreign key without index creator_id — 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.
  • Foreign key without index parent_team_id — 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.
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key creator_id — 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.
  • Nullable foreign key parent_team_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Either/or foreign keys without a CHECK creator_id, parent_team_id, tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
name citextNOT NULLCHECK char_length(CAST(name AS text)) <= 255
display_name textnullCHECK char_length(display_name) <= 255
readonly booleanNOT NULLFALSE
parent_team_id integernullteams.id 1:N · CASCADE
creator_id integernullusers.id 1:N · SET NULL
created_at timestamptzNOT NULLnow()
updated_at timestamptzNOT NULLnow()
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • UNIQUE (name) teams_name

Referenced by

Table checks: char_length(display_name) <= 255; char_length(CAST(name AS text)) <= 255; name ~ CAST('^[a-zA-Z0-9](?:[a-zA-Z0-9]|[-.](?=[a-zA-Z0-9]))*-?$' AS citext)

telemetry_events_export_queue

5 cols · no RLS · line 4946View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKtextNOT NULL
timestamp timestamptzNOT NULL
payload_pb byteaNOT NULL
exported_at timestamptznull
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

  • nothing

temporary_settings

6 cols · no RLS · line 4954View in 3D

Stores per-user temporary settings used in the UI, for example, which modals have been dimissed or what theme is preferred.

  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
user_id UKintegerNOT NULLusers.id 1:1 · CASCADEThe ID of the user the settings will be saved for.
contents jsonbnullJSON-encoded temporary settings.
created_at timestamptzNOT NULLnow()
updated_at timestamptzNOT NULLnow()
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

  • nothing

tenants

4 cols · no RLS · line 4979View in 3D

The table that holds all tenants known to the instance. In enterprise instances, this table will only contain the "default" tenant.

  • 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.
ColumnTypeNullDefaultReferencesNotes
id PKbigintNOT NULLThe ID of the tenant. To keep tenants globally addressable, and be able to move them aronud instances more easily, the ID is NOT a serial and has to be specified explicitly. The creator of the tenant is responsible for choosing a unique ID, if it cares.
name UKtextNOT NULLThe name of the tenant. This may be displayed to the user and must be unique.CHECK char_length(name) <= 32 AND char_length(name) >= 3
created_at timestamptzNOT NULLnow()
updated_at timestamptzNOT NULLnow()

Indexes

  • none

Referenced by

Table checks: char_length(name) <= 32 AND char_length(name) >= 3; name ~ CAST('^[a-z](?:[a-z0-9\_-])*[a-z0-9]$' AS text)

versions

5 cols · no RLS · line 5166View in 3D
  • 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.
ColumnTypeNullDefaultReferencesNotes
service PKtextNOT NULL
version textNOT NULL
updated_at timestamptzNOT NULLnow()
first_version textNOT NULL
auto_upgrade booleanNOT NULLFALSE

Indexes

  • none

Referenced by

  • nothing

vulnerabilities

17 cols · no RLS · line 5174View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
source_id textNOT NULL
summary textNOT NULL
details textNOT NULL
cpes text[]NOT NULL
cwes text[]NOT NULL
aliases text[]NOT NULL
related text[]NOT NULL
data_source textNOT NULL
urls text[]NOT NULL
severity textNOT NULL
cvss_vector textNOT NULL
cvss_score textNOT NULL
published_at timestamptzNOT NULL
modified_at timestamptznull
withdrawn_at timestamptznull
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • UNIQUE (source_id) vulnerabilities_source_id

Referenced by

webhook_logs

9 cols · no RLS · line 5261View in 3D
  • Foreign key without index tenant_id — 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.
  • Foreign key without index webhook_id — 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.
  • Nullable foreign key external_service_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Nullable foreign key webhook_id — 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.
  • Either/or foreign keys without a CHECK external_service_id, tenant_id, webhook_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKbigintNOT NULL
received_at timestamptzNOT NULLnow()
external_service_id integernullexternal_services.id 1:N · CASCADE
status_code integerNOT NULL
request byteaNOT NULL
response byteaNOT NULL
encryption_key_id textNOT NULL
webhook_id integernullwebhooks.id 1:N · CASCADE
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (external_service_id) webhook_logs_external_service_id_idx
  • (received_at) webhook_logs_received_at_idx
  • (status_code) webhook_logs_status_code_idx

Referenced by

  • nothing

webhooks

12 cols · no RLS · line 5282View in 3D

Webhooks registered in Sourcegraph instance.

  • Foreign key without index created_by_user_id — 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.
  • Foreign key without index tenant_id — 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.
  • Foreign key without index updated_by_user_id — 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.
  • Nullable foreign key created_by_user_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Nullable foreign key updated_by_user_id — 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.
  • Enum-like column with no CHECK code_host_kind — 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.
  • Either/or foreign keys without a CHECK created_by_user_id, tenant_id, updated_by_user_id — 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.
ColumnTypeNullDefaultReferencesNotes
id PKintegerNOT NULL
code_host_kind textNOT NULLKind of an external service for which webhooks are registered.
code_host_urn textNOT NULLURN of a code host. This column maps to external_service_id column of repo table.
secret textnullSecret used to decrypt webhook payload (if supported by the code host).
created_at timestamptzNOT NULLnow()
updated_at timestamptzNOT NULLnow()
encryption_key_id textnull
uuid UKuuidNOT NULLgen_random_uuid()
created_by_user_id integernullusers.id 1:N · SET NULLID of a user, who created the webhook. If NULL, then the user does not exist (never existed or was deleted).
updated_by_user_id integernullusers.id 1:N · SET NULLID of a user, who updated the webhook. If NULL, then the user does not exist (never existed or was deleted).
name textNOT NULLDescriptive name of a webhook.
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • none

Referenced by

zoekt_repos

7 cols · no RLS · line 5321View in 3D
  • Foreign key without index tenant_id — 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.
  • Nullable foreign key tenant_id — 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.
  • Enum-like column with no CHECK index_status — 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.
ColumnTypeNullDefaultReferencesNotes
repo_id PKintegerNOT NULLrepo.id 1:1 · CASCADE
branches jsonbNOT NULLCAST('[]' AS jsonb)
index_status textNOT NULLCAST('not_indexed' AS text)
updated_at timestamptzNOT NULLnow()
created_at timestamptzNOT NULLnow()
last_indexed_at timestamptznull
tenant_id integernulltenants.id 1:N · CASCADE

Indexes

  • (index_status) zoekt_repos_index_status

Referenced by

  • nothing