]> git.openstreetmap.org Git - nominatim.git/blob - src/nominatim_db/tools/exec_utils.py
Merge remote-tracking branch 'upstream/master'
[nominatim.git] / src / nominatim_db / tools / exec_utils.py
1 # SPDX-License-Identifier: GPL-3.0-or-later
2 #
3 # This file is part of Nominatim. (https://nominatim.org)
4 #
5 # Copyright (C) 2024 by the Nominatim developer community.
6 # For a full list of authors see the git log.
7 """
8 Helper functions for executing external programs.
9 """
10 from typing import Any, Mapping, List, Optional
11 import logging
12 import os
13 import re
14 import subprocess
15 import shutil
16
17 from ..db.connection import get_pg_env
18 from ..errors import UsageError
19 from ..version import OSM2PGSQL_REQUIRED_VERSION
20
21 LOG = logging.getLogger()
22
23
24 def run_osm2pgsql(options: Mapping[str, Any]) -> None:
25     """ Run osm2pgsql with the given options.
26     """
27     _check_osm2pgsql_version(options['osm2pgsql'])
28
29     env = get_pg_env(options['dsn'])
30
31     cmd = [_find_osm2pgsql_cmd(options['osm2pgsql']),
32            '--append' if options['append'] else '--create',
33            '--slim',
34            '--log-progress', 'true',
35            '--number-processes', '1' if options['append'] else str(options['threads']),
36            '--cache', str(options['osm2pgsql_cache']),
37            '--style', str(options['osm2pgsql_style'])
38            ]
39
40     if str(options['osm2pgsql_style']).endswith('.lua'):
41         env['LUA_PATH'] = ';'.join((str(options['osm2pgsql_style_path'] / '?.lua'),
42                                     os.environ.get('LUAPATH', ';')))
43         cmd.extend(('--output', 'flex'))
44
45         for flavour in ('data', 'index'):
46             if options['tablespaces'][f"main_{flavour}"]:
47                 env[f"NOMINATIM_TABLESPACE_PLACE_{flavour.upper()}"] = \
48                     options['tablespaces'][f"main_{flavour}"]
49     else:
50         cmd.extend(('--output', 'gazetteer', '--hstore', '--latlon'))
51         cmd.extend(_mk_tablespace_options('main', options))
52
53     if options['flatnode_file']:
54         cmd.extend(('--flat-nodes', options['flatnode_file']))
55
56     cmd.extend(_mk_tablespace_options('slim', options))
57
58     if options.get('disable_jit', False):
59         env['PGOPTIONS'] = '-c jit=off -c max_parallel_workers_per_gather=0'
60
61     if 'import_data' in options:
62         cmd.extend(('-r', 'xml', '-'))
63     elif isinstance(options['import_file'], list):
64         for fname in options['import_file']:
65             cmd.append(str(fname))
66     else:
67         cmd.append(str(options['import_file']))
68
69     subprocess.run(cmd, cwd=options.get('cwd', '.'),
70                    input=options.get('import_data'),
71                    env=env, check=True)
72
73
74 def _mk_tablespace_options(ttype: str, options: Mapping[str, Any]) -> List[str]:
75     cmds: List[str] = []
76     for flavour in ('data', 'index'):
77         if options['tablespaces'][f"{ttype}_{flavour}"]:
78             cmds.extend((f"--tablespace-{ttype}-{flavour}",
79                          options['tablespaces'][f"{ttype}_{flavour}"]))
80
81     return cmds
82
83
84 def _find_osm2pgsql_cmd(cmdline: Optional[str]) -> str:
85     if cmdline is not None:
86         return cmdline
87
88     in_path = shutil.which('osm2pgsql')
89     if in_path is None:
90         raise UsageError('osm2pgsql executable not found. Please install osm2pgsql first.')
91
92     return str(in_path)
93
94
95 def _check_osm2pgsql_version(cmdline: Optional[str]) -> None:
96     cmd = [_find_osm2pgsql_cmd(cmdline), '--version']
97
98     result = subprocess.run(cmd, capture_output=True, check=True)
99
100     if not result.stderr:
101         raise UsageError("osm2pgsql does not print version information.")
102
103     verinfo = result.stderr.decode('UTF-8')
104
105     match = re.search(r'osm2pgsql version (\d+)\.(\d+)', verinfo)
106     if match is None:
107         raise UsageError(f"No version information found in output: {verinfo}")
108
109     if (int(match[1]), int(match[2])) < OSM2PGSQL_REQUIRED_VERSION:
110         raise UsageError(f"osm2pgsql is too old. Found version {match[1]}.{match[2]}. "
111                          f"Need at least version {'.'.join(map(str, OSM2PGSQL_REQUIRED_VERSION))}.")