Contents
Changes
2.0.1
Upgrading from 2.0.0 or 1.3.x: ALTER EXTENSION postcode UPDATE;
is_valid(text)andis_valid(text, text)are replaced by one function,is_valid_postal_code(postcode text, cc text DEFAULT NULL). Same arguments and results; the country is now optional. Callers ofis_valid(...)must change. A bareis_validcollided with another extension’sis_valid(text)(gsscode, installed in the first production database 2.0.0 was tried on) and, wherever theisnextension is installed, was ambiguous for an untyped literal (is_valid('US-90210')failed with “function is_valid(unknown) is not unique”).- A database still on 1.3.x upgrades directly to 2.0.1 and never creates
is_valid; a database on 2.0.0 has the two old functions dropped and the new one created. Nothing else changes: the data, the types and the other functions are as in 2.0.0.
2.0.0
Upgrading from 1.3.x: ALTER EXTENSION postcode UPDATE;. The postcode and dps types, their
functions, operators and operator classes are unchanged, and stored values are untouched. 2.0.0 only adds.
New: postal_code, a 64-bit type for the postal codes of any country (see README.md).
- Written
CC-codeas the UPU recommends:US-90210,GB-SW1A 1AA,FR-75008. Values sort by country in ISO 3166-1 order, then by their own code; a coarser value sorts just before the finer ones that extend it (an outcode, a ZIP5 before its ZIP+4s). - Formats for 194 of the 250 ISO countries and territories; the other 56 have no postal codes
(
SELECT * FROM postal_code_world). Eight formats are compiled (US, CA, FR, BR, CZ, LU, GB, IE); the rest are patterns – a template or a bounded regular expression that defines the whole set of a country’s codes (a code is stored as its rank in that set, so order, validity and prefix ranges are exact) – and a new country is added with SQL:add_country_template('PL', 'NN-NNN')oradd_country_template('NL', '/[1-9]\d{3}( [A-Z]{2})?/'). Country rules narrower than a code’s shape are built in (NARROWING.md); the UK format enforces Royal Mail’s letter rules. - Prefix matching as a btree range scan:
postal_prefix(),to_postal_prefix(),lower_bound(),upper_bound(), thepostal_code_rangetype, and the%/!%operators.outcode()/district(). is_valid()(renamedis_valid_postal_code()in 2.0.1) and the NULL-returningto_postal_code()for loading dirty feeds. Input tolerates the spellings real data uses (other scripts' digits, Unicode dashes and spaces, missing hyphens, the country’s own letters in front) and nothing else.- A column can be locked to a country:
pc postal_code('US'). - Country assignments are data (
postal_code_country_formats,add_country_format(),remove_country_format()), with shipped and user assignments kept apart so that a dump carries yours.
Compatibility: PostgreSQL 14 or later. The regression suite passes on 14, 15, 16, 17 and 18 (built from
the release archive); .github/workflows/test.yml runs it on all five. One difference: from PostgreSQL 17
the planner turns pc <@ postal_prefix(...) into an index scan; on 14 to 16 it is correct but a filter,
and pc % '...' or pc >= lower_bound(...) AND pc < upper_bound(...) is the indexed form (see README.md).
Things to know
- Restoring a dump that contains
postal_codevalues for countries you assigned needs the extension’s own configuration tables loaded first;pg_restoreloads data in name order. A reordered restore list is documented in the README. Values for the built-in countries restore with no special handling. postal_code_inand the two-argument constructor areSTABLE, notIMMUTABLE, because they depend on the country table, so they cannot be used in an index expression. Indexing apostal_codecolumn is unaffected.- A pattern is permanent once a value has been written under it (each country has 51 versions); to change a
country’s rules, assign it a new pattern. See
README.md. - Where separators are dropped from UK outcode or sector text,
SW11is read as the outcodeSW11, notSW1 1; a full postcode is unambiguous. - Validated on 117 million OpenStreetMap postcodes, 1.8 million GeoNames codes and 24,000 Companies House
addresses;
VALIDATION.mdhas the method, results, and the known gaps.
1.3.5 and earlier
See the upstream project, and this fork’s git history.