Contents
2.2
Release date: 2026-09-10
Bug Fixes
- Fixed queries not being cancellable during an HTTP request:
CURLOPT_XFERINFOFUNCTIONwas registered without clearingCURLOPT_NOPROGRESS, which defaults to1and disables libcurl’s progress machinery entirely - the callback was therefore never invoked and theCHECK_FOR_INTERRUPTS()inside it never ran. Anominatim_search,nominatim_lookupornominatim_reversecall could not be interrupted withpg_cancel_backend()orCtrl+Cand blocked the backend until the server replied orconnect_timeout(default300seconds) expired.CURLOPT_NOPROGRESSis now explicitly set to0. - Fixed leak of the libcurl handle on interrupted requests: the progress callback raised the interrupt itself via
CHECK_FOR_INTERRUPTS(), soereport(ERROR)wouldlongjmpout of libcurl’s own call stack, skippingcurl_easy_cleanup()and leaking the easy handle and its socket for the lifetime of the backend. The callback now returns a non-zero value instead, which aborts the transfer withCURLE_ABORTED_BY_CALLBACK, and the pending interrupt is processed after the handle has been released. The request/retry loop is additionally wrapped inPG_TRY()/PG_CATCH()so that the handle is also released when an error is raised from a write callback (e.g. on out-of-memory). Aborted transfers and interrupts arriving during the inter-retry sleep no longer consume themax_connect_retryattempts. - Fixed missing libxml2 linkage: the Makefile passed
xml2-config --cflagsto the compiler but neverxml2-config --libsto the linker, sonominatim_fdw.sowas built with eleven unresolved libxml2 symbols and nolibxml2entry in its dynamic dependencies. This went unnoticed because the symbols happen to be provided by the backend itself whenever PostgreSQL was built with--with-libxml; against a PostgreSQL built without it,CREATE EXTENSIONfailed with an undefined-symbol error while loading the module.libxml2is now linked explicitly. - Fixed
max_connect_redirectof0allowing unlimited redirects: the redirect limit was only handed to libcurl when the configured value was non-zero, so0- the one value that unmistakably means “do not follow any redirect” - was the only value that leftCURLOPT_MAXREDIRSat its default of-1. A server redirecting in a loop was followed for 30 hops instead of none. The limit is now always applied, and0makes the request fail on the first redirect. - Fixed the server’s error message being discarded on failed requests:
CURLOPT_FAILONERRORmade libcurl throw away the response body of a4xx/5xxanswer, which is precisely where Nominatim explains what it objected to. A rejected request surfaced asThe requested URL returned error: 400and nothing else. HTTP status handling is now done by the wrapper, and the response body is reported as part of the error detail, truncated to 512 bytes so that a large HTML error page cannot flood the logs. - Fixed
<error>responses being silently reported as an empty result: Nominatim answers some requests with HTTP 200 and an<error>element - an un-geocodable coordinate, for instance, yieldsUnable to geocode. The parsers ignored that element, so the caller saw zero rows and no explanation. Such responses now raise aWARNINGcarrying the server’s message. - Fixed
Retry-Afterbeing ignored on rate-limited requests: response headers were collected on every request and then discarded without being read. A429 Too Many Requestsanswer is now retried after the delay the server asks for, capped at 30 seconds, instead of after a fixed one-second pause. Retries are also no longer attempted for client errors other than429, since an identical request would only be rejected again. - Fixed the inter-retry pause not reacting to query cancellation: the one-second
pg_usleep()between attempts neither processes nor notices interrupts, so a cancellation could sit unnoticed for up to one second per retry. The pause is now taken in 100 ms slices and abandoned as soon as an interrupt arrives. - Fixed
nominatim_fdw_handler()returning anFdwRoutinewith no callbacks: every planner callback was leftNULL, so a foreign table reaching the planner would have dereferenced a NULL function pointer. The handler now raises the same “FOREIGN TABLE not supported” error the validator does. No released version allowed such a table to be created, so this is a hardening fix. - Fixed use of
strtok()inIsLayerValid():strtok()keeps its parsing state in a process-wide static buffer, which is not safe in backend code. Replaced withstrtok_r(); the working copy of the string is also freed on the rejection path. - Fixed server options being parsed differently by the validator and at request time:
connect_timeout,max_connect_retryandmax_connect_redirectwere validated withstrtol()base0innominatim_fdw_validator()but read back with base10inInitSession(), so the two disagreed on any value that is not plain decimal. Aconnect_timeoutof'0x10'was accepted as 16 byCREATE SERVERand then silently used as0, and'010'was validated as 8 but used as 10. Both readings now go through a singleParseNonNegativeLong()helper, which also rejects values that overflowlong- previously accepted and clamped toLONG_MAX. Hexadecimal and octal notation are no longer accepted; values are always interpreted as decimal. - Fixed the endpoint URL being joined naively: a
urlwritten with a trailing slash - a natural way to write it - produced request URLs such ashttps://host//search?.... Trailing slashes are now trimmed before the request path is appended. - Fixed a negative
limit_resultbeing silently ignored:nominatim_search()dropped the parameter instead of complaining, so a caller passing a negative limit got the server default with no indication. Negative values are now rejected, consistent with how out-of-range coordinates are handled. - Fixed the libxml2 document leaking when parsing fails: the response document lives in libxml2’s heap rather than in a palloc context, so an error raised part-way through parsing - on a node that cannot be dumped, or on out-of-memory - abandoned it for the lifetime of the backend. Parsing is now wrapped so the document is released on the error path as well.
- Fixed libxml2 parse diagnostics going to stderr: an unparsable response body made libxml2 write directly to stderr, producing unstructured noise in the server log. The parser is now called with
XML_PARSE_NOERROR | XML_PARSE_NOWARNING; these are per-call options, so no global libxml2 error handler is installed and other users of the library in the same process are unaffected.
Breaking changes
extratags,namedetails,addressdetailsandentrancesare nowNULLwhen they were not requested: these columns were previously always populated, so a caller who leftextratagsat its default offalsestill got an empty{}back - indistinguishable from having asked for extra tags and the place having none. The empty object now carries that second meaning only, and “not requested” is reported asNULL. Queries that relied on these columns never beingNULL- ajsonboperator applied directly to the column, for instance, or aNOT NULLassumption - need to set the corresponding parameter totrue, or handleNULL.
Security
- Pinned
CURLOPT_UNRESTRICTED_AUTHto0, so that credentials from aUSER MAPPINGare never forwarded to a host the request was redirected to. This has always been libcurl’s default; setting it explicitly makes the intent visible and keeps it from changing underneath the wrapper.
Improvements
- Changed
nominatim_search(),nominatim_lookup()andnominatim_reverse()fromPARALLEL SAFEtoPARALLEL RESTRICTED: they perform HTTP requests, and the previous marking allowed PostgreSQL to run them inside parallel workers, so one query could issue several concurrent requests to the endpoint - something public Nominatim instances explicitly ask clients not to do. Plans that previously parallelised over these functions will now run serially. - Marked
nominatim_fdw_settings()asPARALLEL SAFE, matchingnominatim_fdw_version(). It only reports build information. - Removed the unused
custom_paramsandproxy_typefields from the internal session state.custom_paramswas never assigned at all, andproxy_typeonly ever held one value, making the test that guarded the proxy protocol always true. No behaviour changes. - Removed a redundant
text_to_cstring()call from each of the three query functions: theaccept_languageargument was converted twice per call. - Removed the unused
request_redirectfield from the internal session state. It was hardcoded totrueand never configurable, yet the code read as though redirects could be switched off independently ofmax_connect_redirect. Redirect behaviour is governed solely bymax_connect_redirect, where0means “do not follow any redirect”. No behaviour changes.
2.1
Release date: 2026-07-24
Enhancements
- Add HTTP basic authentication in
USER MAPPING: This feature defines a mapping of a PostgreSQL user to an user in the target Nominatim server -userandpassword, so that the user can be authenticated. - Add
request_timeoutserver option: sets the maximum time in seconds allowed for a complete HTTP request (CURLOPT_TIMEOUT), defaulting to0(no limit). The pre-existingconnect_timeoutonly bounds the connection phase, so a Nominatim server that accepted the connection and then stalled would occupy the backend indefinitely.
Bug fixes
- Fixed invalid libcurl lifecycle: Initialize libcurl’s global state once per backend via
_PG_init()(curl_global_init). Previously the wrapper relied on the implicit initialization performed bycurl_easy_init(), which libcurl documents as not thread-safe and unsafe when the address space is shared with other libcurl-using extensions (e.g.rdf_fdw).
2.0
Release date: 2026-07-07
Enhancements
- Add error message for invalid coordinate pairs: this adds a check on the reverse call to reject invalid coordinate pairs before sending the request to the server, therefore avoiding a HTTP request that is doomed to fail.
- Add
emailandpolygon_thresholdparameters to reverse function. - Add support to PostgreSQL 10 and 11 (EOL’d versions).
- Add system view
nominatim_fdw_settingsto list all library dependencies.
Bug fixes
- Fixed memory leaks in XML parsing:
xmlGetProp()andxmlNodeGetContent()return libxml2-heap-allocated strings that were never freed withxmlFree(). Introducedxml_get_prop()andxml_node_content()helper functions that copy the result into palloc’d memory and immediately free the libxml2 string, making ownership clear at a glance. - Fixed JSON injection in
extratags,namedetails,addressdetails, andaddresspartsfields: XML values from the Nominatim response were embedded into hand-crafted JSON strings without escaping, so values containing",\, or control characters produced malformedjsonbor allowed content injection from a malicious server. PostgreSQL’s ownescape_json()(fromutils/json.h) is now used to escape all keys and values before they are appended. - Fixed
nominatim_search,nominatim_lookup, andnominatim_reverseincorrectly declared asIMMUTABLE, which allowed PostgreSQL to cache or optimize away repeated calls and return stale results. Functions are now correctly declaredVOLATILE. - Fixed build failure when specifying a custom
PG_CONFIGpointing to a PostgreSQL installation built without--with-libxml. The Makefile now usesPG_CPPFLAGS(instead ofCFLAGS) and explicitly includesxml2-config --cflags, so libxml2 include paths are always passed to the compiler regardless of whichpg_configis used. - Add missing
typeattribute: the custom data typeNominatimRecordwas missing the attributetype. Thid has been now fixed. - Fix
DEFAULTvalue foraddressdetails: it now defaults totrue, as defined in the API spec. - Set
DEFAULTvalue of reverse’szoomto-1(disabled): the previous value was 0, which is a valid zoom level. - Fix parsing of
KMLgeometries iun reverse calls: the parser was ignoring this format and returningNULLforpolygon_kmlrequests. This is now fixed.
Breaking changes
- Add
entrancescolumn to lookup, search, and reverse calls. - Rename reverse’s column
resulttodisplay_name: the previous name was mimicing the xml node retrieved from the API, which was inconsistent with the lookup and search functions. - For simplicity,
nominatim_fdw_version()now omits ssl, zblib, libSSH, and ngt http2 versions. - Rename
addresspartscolumn from reverse function toaddressdetails, so that it aligns with search and lookup.
1.3
Release date: 2026-04-12
Breaking Changes
Proxy authentication credentials moved to USER MAPPING: For improved security, proxy authentication credentials (proxy_user and proxy_password) must now be specified in USER MAPPING instead of SERVER options. This change prevents proxy passwords from being visible to all users with USAGE privilege on the foreign server, as PostgreSQL automatically hides USER MAPPING passwords from non-owners.
1.2.0
Release date: 2026-04-05
Bug fixes
- Fixed
lon/latvalues of0.0being omitted from reverse geocoding requests. - Fixed memory leaks in
curl_easy_escapecalls. - Fixed undefined behaviour from
xmlFreeNodeon document-owned nodes; replaced withxmlFreeDoc. - Fixed
IsLayerValidrejecting valid comma-separated layer lists. - Fixed duplicate
state->amenityassignment innominatim_fdw_search. - Fixed redundant
palloc0forstateinnominatim_fdw_searchandnominatim_fdw_lookup. - Fixed early (incomplete) assignment of
place->addresspartsinParseNominatimReverseData.
Security
- Enabled TLS peer verification (
CURLOPT_SSL_VERIFYPEER).
Improvements
- Moved
curl_global_init/curl_global_cleanupto_PG_init/_PG_fini— called once per backend instead of once per request. - Made attribute name lookup in
GetAttributeValueconsistently useNameStr.
1.1.0
Release date: 2024-11-01
Enhancements
- Add support to PostgreSQL 17 and 18.