Database Naming
PostgreSQL index and constraint names in Baander follow one scheme: a type prefix, then the table, then the columns. The prefixes are the lower-case forms of those Doctrine generates (IDX_, UNIQ_, FK_). A name therefore says what it covers without a catalog lookup.
Rules
| Object | Name | Example |
|---|---|---|
| Index | idx_<table>_<columns> |
idx_party_sessions_host_user_id |
| Unique index or unique constraint | uniq_<table>_<columns> |
uniq_users_email |
| Foreign key | fk_<table>_<columns> |
fk_notifications_user_id |
| Index with a non-B-tree access method or an extension operator class | idx_<table>_<columns>_<suffix> |
idx_songs_title_pgroonga, idx_songs_public_id_trgm |
| Migration-only partial, expression or polymorphic index | idx_<table>_<purpose>, or uniq_<table>_<purpose> when unique |
idx_albums_cover_image_null, idx_images_imageable, uniq_genres_name_lower |
| Primary key | <table>_pkey (PostgreSQL default) |
songs_pkey |
| Check constraint | chk_<table>_<purpose> |
chk_oauth_device_codes_single_decision |
- List columns in index order, separated by underscores:
idx_notifications_user_id_created_at. - A B-tree index takes no suffix. The suffixes for other indexes are defined in Extension Indexes.
- A partial index declared on an entity with
options: ['where' => ...]is not migration-only and takes the column name:idx_scheduled_jobs_recovery_after_id. - Renaming a unique constraint also renames the index that backs it, so both carry the
uniq_name. - PostgreSQL truncates identifiers longer than 63 bytes. When a name would exceed that, shorten the column part consistently instead of letting PostgreSQL cut the name. Collapse each polymorphic
<x>_type,<x>_idpair to<x>: the unique index onrecommendations (source_type, source_id, target_type, target_id, name, user_id)isuniq_recommendations_source_target_name_user_id.
Extension Indexes
The initial migration enables citext, ltree, pg_stat_statements, pg_trgm, pgcrypto, pgroonga and uuid-ossp. Only some of them affect index or constraint names.
| Extension | Index use in Baander | Naming |
|---|---|---|
pgroonga |
Full-text search on catalog titles and names | _pgroonga suffix; see PGroonga |
pg_trgm |
GIN gin_trgm_ops indexes on public_id columns |
_trgm suffix |
citext |
users.email is CITEXT |
Ordinary idx_/uniq_ names; a unique index on a CITEXT column is case-insensitive |
ltree |
Enabled; no column uses it | _ltree suffix for an index using an ltree operator class |
pgcrypto, uuid-ossp, pg_stat_statements |
Functions and statistics only | No index naming |
Core access methods without an extension take the method as the suffix: _gin, _gist, _brin, _hash, _spgist. No such index exists today.
The _trgm suffix is functional, not only descriptive. BaanderPostgreSQLSchemaManager hides every index whose name ends in _trgm from schema comparison, because ORM SchemaTool drops a non-unique trigram index when a unique constraint covers the same column. A trigram index with another name would therefore surface as drift. Other migration-only indexes that Doctrine cannot model are hidden by name: add a case to UnmanagedCustomIndex when you create one.
PGroonga
A PGroonga index name records the search it serves, because the access method alone does not: PGroonga provides different operator classes for full-text, prefix and regular-expression search, and an index's tokenizer options change what it matches.
The database image installs PGroonga package 4.0.5-1. The operator classes and options below are those of PGroonga 4; check a new option against that release before using it, and confirm the installed version with pg_extension.extversion on the target database.
| Index | Name | Operator class | Options |
|---|---|---|---|
| Full-text search (the project standard) | idx_<table>_<columns>_pgroonga |
Default for the column type (pgroonga_text_full_text_search_ops_v2 for text) |
plugins='token_filters/stem', tokenizer='TokenNgram', normalizer='NormalizerAuto', token_filters='TokenFilterStem' |
| Prefix or exact-term search | idx_<table>_<columns>_pgroonga_prefix |
pgroonga_text_term_search_ops_v2 |
No project standard; set per index |
| Regular-expression search | idx_<table>_<columns>_pgroonga_regexp |
pgroonga_text_regexp_ops_v2 |
No project standard; set per index |
| Full-text search with non-standard tokenizer options | idx_<table>_<columns>_pgroonga_<tokenizer> |
Default for the column type | Named by the tokenizer, e.g. _pgroonga_mecab |
- The unsuffixed
_pgroonganame means exactly the standard full-text index. The five existing catalog indexes (idx_albums_title_pgroonga,idx_artists_name_pgroonga,idx_genres_name_pgroonga,idx_movies_title_pgroonga,idx_songs_title_pgroonga) follow it. - Use the operator class for the actual column type.
varchar, array andjsonbcolumns have their own operator classes; see the PGroonga reference. - Declare the index on the entity with the same name,
flags: ['pgroonga']and the options underwith, asAlbumEntitydoes.BaanderPostgreSQLPlatformrenders the flag asUSING pgroonga,options.operator_classas the operator class, andoptions.withas theWITH (...)clause. - Schema comparison does not capture every PGroonga option. A clean
doctrine:schema:validatedoes not prove the tokenizer or normalizer; inspectpg_get_indexdef()to verify them.
Search describes how repositories query these indexes.
Where Names Are Set
- Migrations name every index and constraint explicitly. Do not rely on PostgreSQL's generated names (
<table>_<column>_key,<table>_<column>_fkey) or on Doctrine's hashed names (IDX_…,FK_…). - Doctrine mappings name indexes and unique constraints with the same value as the migration (
#[ORM\Index(name: 'idx_…')],#[ORM\UniqueConstraint(name: 'uniq_…')]). Schema comparison matches indexes by name. A mapping that omits the migration's name therefore makesdoctrine:schema:validateand migration diffs report a rename. - Cross-context foreign keys on scalar UUID columns are not visible to Doctrine. Their owning context declares each one in a
ForeignKeyDeclarationProviderInterfaceservice with the constraint's real name, so schema comparison keeps the database FK. Seesrc/Notification/Infrastructure/Doctrine/NotificationForeignKeys.php. - Association foreign keys cannot be named in a mapping. DBAL matches foreign keys by definition, so a migration-chosen
fk_name does not show up as drift. - Implicit foreign-key indexes. For every mapped or declared foreign key, DBAL adds an index named
IDX_<hash>unless an index on exactly the same columns already exists. When a migration creates that single-column index itself, declare it on the entity with itsidx_name, asPushSubscriptionEntitydoes; otherwise schema comparison reports a rename toIDX_<hash>. Do not create that index when another index already leads with the foreign-key column, such as a unique index on(user_id, media_type): the composite index serves the lookups, and because DBAL then adds the sameIDX_<hash>to both the mapped and the introspected table, schema comparison stays clean.
tests/Integration/DatabaseNamingConventionTest.php reads the migrated catalog and fails on any foreign key, unique constraint or index whose name breaks these rules. It also checks that schema comparison is clean.
See Also
- Coding Conventions — column types and primary keys
- Search — querying PGroonga indexes