Repository navigation
Support the encoding, locale and template options of CREATE DATABASE in the Database CR #76
Description
Current behavior
DatabaseService.createDatabase() runs dsl.createDatabase(name) without options. PostgreSQL then copies the encoding and the locale of template1. The DatabaseSpec has no fields to set them.
PostgreSQL sets the encoding and the locale only at CREATE DATABASE. ALTER DATABASE cannot change them later. The only fix is a new database and a full dump and restore.
On a CloudNativePG cluster, template1 uses the locale C by default. Every database that the operator creates thus gets the C collation and the C character type:
ORDER BYon text sorts by byte value. All uppercase letters come before all lowercase letters, andÄ,ÖandÜcome afterz.lower(),upper()andILIKEchange the case of ASCII letters only. A search forötztaldoes not findÖtztal.
We found this in a migration from the official postgres image, which uses en_US.utf8, to CloudNativePG. The Database CR of CloudNativePG supports these options, but it works only with a CloudNativePG cluster. On AWS RDS, Cloud SQL or Azure, no operator can set them.
Proposal
Add these optional fields to DatabaseSpec. Each field maps to the option of CREATE DATABASE with the same name. The names are the same as in the Database CR of CloudNativePG.
| Field | CREATE DATABASE option |
Minimum PostgreSQL version |
|---|---|---|
template |
TEMPLATE |
all |
encoding |
ENCODING |
all |
locale |
LOCALE |
13 |
localeCollate |
LC_COLLATE |
all |
localeCType |
LC_CTYPE |
all |
localeProvider |
LOCALE_PROVIDER |
15 |
icuLocale |
ICU_LOCALE |
15 |
icuRules |
ICU_RULES |
16 |
builtinLocale |
BUILTIN_LOCALE |
17 |
Example:
apiVersion: postgresql.aboutbits.it/v1
kind: Database
metadata:
name: my-database
spec:
clusterRef:
name: my-postgres-connection
name: my_database
owner: my_role
template: template0
encoding: UTF8
localeProvider: icu
icuLocale: undRules
- Immutable: The fields are immutable like
name. Use a@ValidationRulewithself == oldSelf, so that the API server rejects a change before the operator sees it. - Cross-field validation:
icuLocaleandicuRulesrequirelocaleProvider: icu.builtinLocalerequireslocaleProvider: builtin.
- Server version: Before
CREATE DATABASE, compare the server version with the table above. If the server is too old, set an error in the status that names the field and the minimum version. Do not send the SQL. - Existing database: If the database exists, do not alter or drop it. Compare the set fields with
pg_databaseand report a difference in a status condition, for exampleLocaleMismatch. This agrees withreclaimPolicy: Retain. pg_databasecolumns: The ICU locale is indaticulocaleon PostgreSQL 15 and 16, and indatlocaleon PostgreSQL 17 and later. The comparison must select the column by server version.- No
nullin the manifest: Like the other optional fields, the operator must not default these fields. Omitted fields stay omitted. Otherwise Terraform shows a change on every plan, seedocs/terraform.md.
Documentation
docs/database.md: Add the fields and the version table.docs/database.md: Explain that a locale other than the locale oftemplate1requirestemplate: template0. Without it, PostgreSQL rejects the command, and its error message does not mentiontemplate0.docs/terraform.md: Add the new fields to the table "Affected fields".
Acceptance criteria
- A
Databasewithtemplate: template0,encoding: UTF8,localeProvider: icuandicuLocale: undcreates a database with these values inpg_database. - A
Databasewithout the new fields creates the database as before. - The API server rejects a change of a field that is set.
- The API server rejects
icuLocalewithoutlocaleProvider: icu, andbuiltinLocalewithoutlocaleProvider: builtin. - On a server that is too old for a field, the status shows an error that names the field and the minimum version.
- For an existing database with a different locale, the status shows the difference, and the operator does not change the database.
- Tests cover PostgreSQL 15, 16 and 17 or later, because of the
pg_databasecolumn change.
Out of scope
These are possible follow-ups, each in its own issue:
connectionLimitandallowConnections. These options can change withALTER DATABASE, so the operator can reconcile them like the owner.- An
ExtensionCR.pg_dump --schemadoes not dump extensions, so a restore needs them in the target database first.
Activity