]> git.openstreetmap.org Git - nominatim.git/blobdiff - nominatim/api/connection.py
enable API use with psycopg 3
[nominatim.git] / nominatim / api / connection.py
index 1502ba01f7f61825d1b628313dac85ff2fde8e17..efa4490e0cbf6d263398a4cf21d2f883b419bad9 100644 (file)
@@ -7,12 +7,13 @@
 """
 Extended SQLAlchemy connection class that also includes access to the schema.
 """
-from typing import Any, Mapping, Sequence, Union
+from typing import Any, Mapping, Sequence, Union, Dict, cast
 
 import sqlalchemy as sa
 from sqlalchemy.ext.asyncio import AsyncConnection
 
 from nominatim.db.sqlalchemy_schema import SearchTables
+from nominatim.api.logging import log
 
 class SearchConnection:
     """ An extended SQLAlchemy connection class, that also contains
@@ -22,9 +23,11 @@ class SearchConnection:
     """
 
     def __init__(self, conn: AsyncConnection,
-                 tables: SearchTables) -> None:
+                 tables: SearchTables,
+                 properties: Dict[str, Any]) -> None:
         self.connection = conn
         self.t = tables # pylint: disable=invalid-name
+        self._property_cache = properties
 
 
     async def scalar(self, sql: sa.sql.base.Executable,
@@ -32,12 +35,55 @@ class SearchConnection:
                     ) -> Any:
         """ Execute a 'scalar()' query on the connection.
         """
+        log().sql(self.connection, sql)
         return await self.connection.scalar(sql, params)
 
 
-    async def execute(self, sql: sa.sql.base.Executable,
+    async def execute(self, sql: 'sa.Executable',
                       params: Union[Mapping[str, Any], Sequence[Mapping[str, Any]], None] = None
-                     ) -> sa.engine.Result:
+                     ) -> 'sa.Result[Any]':
         """ Execute a 'execute()' query on the connection.
         """
+        log().sql(self.connection, sql)
         return await self.connection.execute(sql, params)
+
+
+    async def get_property(self, name: str, cached: bool = True) -> str:
+        """ Get a property from Nominatim's property table.
+
+            Property values are normally cached so that they are only
+            retrieved from the database when they are queried for the
+            first time with this function. Set 'cached' to False to force
+            reading the property from the database.
+
+            Raises a ValueError if the property does not exist.
+        """
+        if name.startswith('DB:'):
+            raise ValueError(f"Illegal property value '{name}'.")
+
+        if cached and name in self._property_cache:
+            return cast(str, self._property_cache[name])
+
+        sql = sa.select(self.t.properties.c.value)\
+            .where(self.t.properties.c.property == name)
+        value = await self.connection.scalar(sql)
+
+        if value is None:
+            raise ValueError(f"Property '{name}' not found in database.")
+
+        self._property_cache[name] = cast(str, value)
+
+        return cast(str, value)
+
+
+    async def get_db_property(self, name: str) -> Any:
+        """ Get a setting from the database. At the moment, only
+            'server_version', the version of the database software, can
+            be retrieved with this function.
+
+            Raises a ValueError if the property does not exist.
+        """
+        if name != 'server_version':
+            raise ValueError(f"DB setting '{name}' not found in database.")
+
+        return self._property_cache['DB:server_version']