]> git.openstreetmap.org Git - nominatim.git/blob - nominatim/db/sql_preprocessor.py
b450422da09396cb687cfe50750490311f82a31d
[nominatim.git] / nominatim / db / sql_preprocessor.py
1 # SPDX-License-Identifier: GPL-2.0-only
2 #
3 # This file is part of Nominatim. (https://nominatim.org)
4 #
5 # Copyright (C) 2022 by the Nominatim developer community.
6 # For a full list of authors see the git log.
7 """
8 Preprocessing of SQL files.
9 """
10 from typing import Set, Dict, Any
11 import jinja2
12
13 from nominatim.db.connection import Connection
14 from nominatim.config import Configuration
15
16 def _get_partitions(conn: Connection) -> Set[int]:
17     """ Get the set of partitions currently in use.
18     """
19     with conn.cursor() as cur:
20         cur.execute('SELECT DISTINCT partition FROM country_name')
21         partitions = set([0])
22         for row in cur:
23             partitions.add(row[0])
24
25     return partitions
26
27
28 def _get_tables(conn: Connection) -> Set[str]:
29     """ Return the set of tables currently in use.
30         Only includes non-partitioned
31     """
32     with conn.cursor() as cur:
33         cur.execute("SELECT tablename FROM pg_tables WHERE schemaname = 'public'")
34
35         return set((row[0] for row in list(cur)))
36
37
38 def _setup_tablespace_sql(config: Configuration) -> Dict[str, str]:
39     """ Returns a dict with tablespace expressions for the different tablespace
40         kinds depending on whether a tablespace is configured or not.
41     """
42     out = {}
43     for subset in ('ADDRESS', 'SEARCH', 'AUX'):
44         for kind in ('DATA', 'INDEX'):
45             tspace = getattr(config, f'TABLESPACE_{subset}_{kind}')
46             if tspace:
47                 tspace = f'TABLESPACE "{tspace}"'
48             out[f'{subset.lower()}_{kind.lower()}'] = tspace
49
50     return out
51
52
53 def _setup_postgresql_features(conn: Connection) -> Dict[str, Any]:
54     """ Set up a dictionary with various optional Postgresql/Postgis features that
55         depend on the database version.
56     """
57     pg_version = conn.server_version_tuple()
58     postgis_version = conn.postgis_version_tuple()
59     return {
60         'has_index_non_key_column': pg_version >= (11, 0, 0),
61         'spgist_geom' : 'SPGIST' if postgis_version >= (3, 0) else 'GIST'
62     }
63
64 class SQLPreprocessor:
65     """ A environment for preprocessing SQL files from the
66         lib-sql directory.
67
68         The preprocessor provides a number of default filters and variables.
69         The variables may be overwritten when rendering an SQL file.
70
71         The preprocessing is currently based on the jinja2 templating library
72         and follows its syntax.
73     """
74
75     def __init__(self, conn: Connection, config: Configuration) -> None:
76         self.env = jinja2.Environment(autoescape=False,
77                                       loader=jinja2.FileSystemLoader(str(config.lib_dir.sql)))
78
79         db_info: Dict[str, Any] = {}
80         db_info['partitions'] = _get_partitions(conn)
81         db_info['tables'] = _get_tables(conn)
82         db_info['reverse_only'] = 'search_name' not in db_info['tables']
83         db_info['tablespace'] = _setup_tablespace_sql(config)
84
85         self.env.globals['config'] = config
86         self.env.globals['db'] = db_info
87         self.env.globals['postgres'] = _setup_postgresql_features(conn)
88
89
90     def run_sql_file(self, conn: Connection, name: str, **kwargs: Any) -> None:
91         """ Execute the given SQL file on the connection. The keyword arguments
92             may supply additional parameters for preprocessing.
93         """
94         sql = self.env.get_template(name).render(**kwargs)
95
96         with conn.cursor() as cur:
97             cur.execute(sql)
98         conn.commit()