Compare commits

...

7 Commits

Author SHA1 Message Date
Sarah Hoffmann
e39e51e531 prepare release 4.3.2 2023-11-17 10:36:42 +01:00
Sarah Hoffmann
d545554615 adapt typing to newest version of SQLAlchemy 2023-11-17 10:08:54 +01:00
Sarah Hoffmann
07120f9af5 improve code to collect the PostGIS version
The SQL contained an unchecked string literal, which may in theory be
used to attack the database.
2023-11-17 10:06:39 +01:00
Sarah Hoffmann
2a5c6b1570 php frontend: fix on-the-fly lookup of postcode areas 2023-11-17 10:06:18 +01:00
Sarah Hoffmann
6f6ebbd7be python deployment: add systemd service for the socket 2023-11-17 10:05:37 +01:00
Sarah Hoffmann
2635389a87 adapt typing for newer version of mypy 2023-11-17 10:02:37 +01:00
Sarah Hoffmann
e022d41a01 reduce influence of viewbox
Perfectly matching city names should still get priority.
2023-11-17 10:02:04 +01:00
11 changed files with 48 additions and 25 deletions

View File

@@ -20,7 +20,7 @@ project(nominatim)
set(NOMINATIM_VERSION_MAJOR 4) set(NOMINATIM_VERSION_MAJOR 4)
set(NOMINATIM_VERSION_MINOR 3) set(NOMINATIM_VERSION_MINOR 3)
set(NOMINATIM_VERSION_PATCH 1) set(NOMINATIM_VERSION_PATCH 2)
set(NOMINATIM_VERSION "${NOMINATIM_VERSION_MAJOR}.${NOMINATIM_VERSION_MINOR}.${NOMINATIM_VERSION_PATCH}") set(NOMINATIM_VERSION "${NOMINATIM_VERSION_MAJOR}.${NOMINATIM_VERSION_MINOR}.${NOMINATIM_VERSION_PATCH}")

View File

@@ -1,3 +1,9 @@
4.3.2
* fix potential SQL injection issue for 'nominatim admin --collect-os-info'
* PHP frontend: fix on-the-fly lookup of postcode areas near boundaries
* Python frontend: improve handling of viewbox
* Python frontend: correct deployment instructions
4.3.1 4.3.1
* reintroduce result rematching * reintroduce result rematching
* improve search of multi-part names * improve search of multi-part names

View File

@@ -43,6 +43,22 @@ virtualenv /srv/nominatim-venv
Next you need to set up the service that runs the Nominatim frontend. This is Next you need to set up the service that runs the Nominatim frontend. This is
easiest done with a systemd job. easiest done with a systemd job.
First you need to tell systemd to create a socket file to be used by
hunicorn. Crate the following file `/etc/systemd/system/nominatim.socket`:
``` systemd
[Unit]
Description=Gunicorn socket for Nominatim
[Socket]
ListenStream=/run/nominatim.sock
SocketUser=www-data
[Install]
WantedBy=multi-user.target
```
Now you can add the systemd service for Nominatim itself.
Create the following file `/etc/systemd/system/nominatim.service`: Create the following file `/etc/systemd/system/nominatim.service`:
``` systemd ``` systemd
@@ -74,12 +90,14 @@ its own Python process using
[`NOMINATIM_API_POOL_SIZE`](../customize/Settings.md#nominatim_api_pool_size) [`NOMINATIM_API_POOL_SIZE`](../customize/Settings.md#nominatim_api_pool_size)
connections to the database to serve requests in parallel. connections to the database to serve requests in parallel.
Make the new service known to systemd and start it: Make the new services known to systemd and start it:
``` sh ``` sh
sudo systemctl daemon-reload sudo systemctl daemon-reload
sudo systemctl enable nominatim sudo systemctl enable nominatim.socket
sudo systemctl start nominatim sudo systemctl start nominatim.socket
sudo systemctl enable nominatim.service
sudo systemctl start nominatim.service
``` ```
This sets the service up, so that Nominatim is automatically started This sets the service up, so that Nominatim is automatically started

View File

@@ -261,7 +261,7 @@ BEGIN
-- If the place had a postcode assigned, take this one only -- If the place had a postcode assigned, take this one only
-- into consideration when it is an area and the place does not have -- into consideration when it is an area and the place does not have
-- a postcode itself. -- a postcode itself.
IF location.fromarea AND location.isaddress IF location.fromarea AND location_isaddress
AND (place.address is null or not place.address ? 'postcode') AND (place.address is null or not place.address ? 'postcode')
THEN THEN
place.postcode := null; -- remove the less exact postcode place.postcode := null; -- remove the less exact postcode

View File

@@ -512,8 +512,8 @@ class PostcodeSearch(AbstractSearch):
sql = sql.where(t.c.geometry.intersects(VIEWBOX_PARAM)) sql = sql.where(t.c.geometry.intersects(VIEWBOX_PARAM))
else: else:
penalty += sa.case((t.c.geometry.intersects(VIEWBOX_PARAM), 0.0), penalty += sa.case((t.c.geometry.intersects(VIEWBOX_PARAM), 0.0),
(t.c.geometry.intersects(VIEWBOX2_PARAM), 1.0), (t.c.geometry.intersects(VIEWBOX2_PARAM), 0.5),
else_=2.0) else_=1.0)
if details.near is not None: if details.near is not None:
if details.near_radius is not None: if details.near_radius is not None:
@@ -634,8 +634,8 @@ class PlaceSearch(AbstractSearch):
sql = sql.where(tsearch.c.centroid.ST_Intersects_no_index(VIEWBOX2_PARAM)) sql = sql.where(tsearch.c.centroid.ST_Intersects_no_index(VIEWBOX2_PARAM))
else: else:
penalty += sa.case((t.c.geometry.intersects(VIEWBOX_PARAM), 0.0), penalty += sa.case((t.c.geometry.intersects(VIEWBOX_PARAM), 0.0),
(t.c.geometry.intersects(VIEWBOX2_PARAM), 1.0), (t.c.geometry.intersects(VIEWBOX2_PARAM), 0.5),
else_=2.0) else_=1.0)
if details.near is not None: if details.near is not None:
if details.near_radius is not None: if details.near_radius is not None:

View File

@@ -37,7 +37,7 @@ def zoom_to_rank(zoom: int) -> int:
return REVERSE_MAX_RANKS[max(0, min(18, zoom))] return REVERSE_MAX_RANKS[max(0, min(18, zoom))]
FEATURE_TYPE_TO_RANK: Dict[Optional[str], Any] = { FEATURE_TYPE_TO_RANK: Dict[Optional[str], Tuple[int, int]] = {
'country': (4, 4), 'country': (4, 4),
'state': (8, 8), 'state': (8, 8),
'city': (14, 16), 'city': (14, 16),

View File

@@ -31,7 +31,7 @@ class Cursor(psycopg2.extras.DictCursor):
""" Query execution that logs the SQL query when debugging is enabled. """ Query execution that logs the SQL query when debugging is enabled.
""" """
if LOG.isEnabledFor(logging.DEBUG): if LOG.isEnabledFor(logging.DEBUG):
LOG.debug(self.mogrify(query, args).decode('utf-8')) # type: ignore[arg-type] LOG.debug(self.mogrify(query, args).decode('utf-8'))
super().execute(query, args) super().execute(query, args)

View File

@@ -118,4 +118,4 @@ class CopyBuffer:
""" """
if self.buffer.tell() > 0: if self.buffer.tell() > 0:
self.buffer.seek(0) self.buffer.seek(0)
cur.copy_from(self.buffer, table, columns=columns) # type: ignore[arg-type] cur.copy_from(self.buffer, table, columns=columns)

View File

@@ -12,14 +12,13 @@ import os
import subprocess import subprocess
import sys import sys
from pathlib import Path from pathlib import Path
from typing import List, Optional, Tuple, Union, cast from typing import List, Optional, Tuple, Union
import psutil import psutil
from psycopg2.extensions import make_dsn, parse_dsn from psycopg2.extensions import make_dsn, parse_dsn
from nominatim.config import Configuration from nominatim.config import Configuration
from nominatim.db.connection import connect from nominatim.db.connection import connect
from nominatim.typing import DictCursorResults
from nominatim.version import NOMINATIM_VERSION from nominatim.version import NOMINATIM_VERSION
@@ -107,15 +106,15 @@ def report_system_information(config: Configuration) -> None:
postgresql_ver: str = convert_version(conn.server_version_tuple()) postgresql_ver: str = convert_version(conn.server_version_tuple())
with conn.cursor() as cur: with conn.cursor() as cur:
cur.execute(f""" num = cur.scalar("SELECT count(*) FROM pg_catalog.pg_database WHERE datname=%s",
SELECT datname FROM pg_catalog.pg_database (parse_dsn(config.get_libpq_dsn())['dbname'], ))
WHERE datname='{parse_dsn(config.get_libpq_dsn())['dbname']}'""") nominatim_db_exists = num == 1 if isinstance(num, int) else False
nominatim_db_exists = cast(Optional[DictCursorResults], cur.fetchall())
if nominatim_db_exists: if nominatim_db_exists:
with connect(config.get_libpq_dsn()) as conn: with connect(config.get_libpq_dsn()) as conn:
postgis_ver: str = convert_version(conn.postgis_version_tuple()) postgis_ver: str = convert_version(conn.postgis_version_tuple())
else: else:
postgis_ver = "Unable to connect to database" postgis_ver = "Unable to connect to database"
postgresql_config: str = get_postgresql_config(int(float(postgresql_ver))) postgresql_config: str = get_postgresql_config(int(float(postgresql_ver)))

View File

@@ -34,7 +34,7 @@ class NominatimVersion(NamedTuple):
return f"{self.major}.{self.minor}.{self.patch_level}-{self.db_patch_level}" return f"{self.major}.{self.minor}.{self.patch_level}-{self.db_patch_level}"
NOMINATIM_VERSION = NominatimVersion(4, 3, 1, 0) NOMINATIM_VERSION = NominatimVersion(4, 3, 2, 0)
POSTGRESQL_REQUIRED_VERSION = (9, 6) POSTGRESQL_REQUIRED_VERSION = (9, 6)
POSTGIS_REQUIRED_VERSION = (2, 2) POSTGIS_REQUIRED_VERSION = (2, 2)

View File

@@ -133,7 +133,7 @@ class TestNameOnlySearches:
@pytest.mark.parametrize('viewbox', ['5.0,4.0,6.0,5.0', '5.7,4.0,6.0,5.0']) @pytest.mark.parametrize('viewbox', ['5.0,4.0,6.0,5.0', '5.7,4.0,6.0,5.0'])
def test_prefer_viewbox(self, apiobj, viewbox): def test_prefer_viewbox(self, apiobj, viewbox):
lookup = FieldLookup('name_vector', [1, 2], 'lookup_all') lookup = FieldLookup('name_vector', [1, 2], 'lookup_all')
ranking = FieldRanking('name_vector', 0.9, [RankedTokens(0.0, [21])]) ranking = FieldRanking('name_vector', 0.2, [RankedTokens(0.0, [21])])
results = run_search(apiobj, 0.1, [lookup], [ranking]) results = run_search(apiobj, 0.1, [lookup], [ranking])
assert [r.place_id for r in results] == [101, 100] assert [r.place_id for r in results] == [101, 100]