]> git.openstreetmap.org Git - nominatim.git/blob - nominatim/tools/database_import.py
port database setup function to python
[nominatim.git] / nominatim / tools / database_import.py
1 """
2 Functions for setting up and importing a new Nominatim database.
3 """
4 import logging
5 import subprocess
6 import shutil
7
8 from ..db.connection import connect, get_pg_env
9 from ..db import utils as db_utils
10 from ..errors import UsageError
11 from ..version import POSTGRESQL_REQUIRED_VERSION, POSTGIS_REQUIRED_VERSION
12
13 LOG = logging.getLogger()
14
15 def create_db(dsn, rouser=None):
16     """ Create a new database for the given DSN. Fails when the database
17         already exists or the PostgreSQL version is too old.
18         Uses `createdb` to create the database.
19
20         If 'rouser' is given, then the function also checks that the user
21         with that given name exists.
22
23         Requires superuser rights by the caller.
24     """
25     proc = subprocess.run(['createdb'], env=get_pg_env(dsn), check=False)
26
27     if proc.returncode != 0:
28         raise UsageError('Creating new database failed.')
29
30     with connect(dsn) as conn:
31         postgres_version = conn.server_version_tuple() # pylint: disable=E1101
32         if postgres_version < POSTGRESQL_REQUIRED_VERSION:
33             LOG.fatal('Minimum supported version of Postgresql is %d.%d. '
34                       'Found version %d.%d.',
35                       POSTGRESQL_REQUIRED_VERSION[0], POSTGRESQL_REQUIRED_VERSION[1],
36                       postgres_version[0], postgres_version[1])
37             raise UsageError('PostgreSQL server is too old.')
38
39         if rouser is not None:
40             with conn.cursor() as cur:  # pylint: disable=E1101
41                 cnt = cur.scalar('SELECT count(*) FROM pg_user where usename = %s',
42                                  (rouser, ))
43                 if cnt == 0:
44                     LOG.fatal("Web user '%s' does not exists. Create it with:\n"
45                               "\n      createuser %s", rouser, rouser)
46                     raise UsageError('Missing read-only user.')
47
48
49
50 def setup_extensions(conn):
51     """ Set up all extensions needed for Nominatim. Also checks that the
52         versions of the extensions are sufficient.
53     """
54     with conn.cursor() as cur:
55         cur.execute('CREATE EXTENSION IF NOT EXISTS hstore')
56         cur.execute('CREATE EXTENSION IF NOT EXISTS postgis')
57     conn.commit()
58
59     postgis_version = conn.postgis_version_tuple()
60     if postgis_version < POSTGIS_REQUIRED_VERSION:
61         LOG.fatal('Minimum supported version of PostGIS is %d.%d. '
62                   'Found version %d.%d.',
63                   POSTGIS_REQUIRED_VERSION[0], POSTGIS_REQUIRED_VERSION[1],
64                   postgis_version[0], postgis_version[1])
65         raise UsageError('PostGIS version is too old.')
66
67
68 def install_module(src_dir, project_dir, module_dir):
69     """ Copy the normalization module from src_dir into the project
70         directory under the '/module' directory. If 'module_dir' is set, then
71         use the module from there instead and check that it is accessible
72         for Postgresql.
73
74         The function detects when the installation is run from the
75         build directory. It doesn't touch the module in that case.
76     """
77     if not module_dir:
78         module_dir = project_dir / 'module'
79
80         if not module_dir.exists() or not src_dir.samefile(module_dir):
81
82             if not module_dir.exists():
83                 module_dir.mkdir()
84
85             destfile = module_dir / 'nominatim.so'
86             shutil.copy(str(src_dir / 'nominatim.so'), str(destfile))
87             destfile.chmod(0o755)
88
89             LOG.info('Database module installed at %s', str(destfile))
90         else:
91             LOG.info('Running from build directory. Leaving database module as is.')
92     else:
93         LOG.info("Using custom path for database module at '%s'", module_dir)
94
95     return module_dir
96
97
98 def check_module_dir_path(conn, path):
99     """ Check that the normalisation module can be found and executed
100         from the given path.
101     """
102     with conn.cursor() as cur:
103         cur.execute("""CREATE FUNCTION nominatim_test_import_func(text)
104                        RETURNS text AS '{}/nominatim.so', 'transliteration'
105                        LANGUAGE c IMMUTABLE STRICT;
106                        DROP FUNCTION nominatim_test_import_func(text)
107                     """.format(path))
108
109
110 def import_base_data(dsn, sql_dir, ignore_partitions=False):
111     """ Create and populate the tables with basic static data that provides
112         the background for geocoding.
113     """
114     db_utils.execute_file(dsn, sql_dir / 'country_name.sql')
115     db_utils.execute_file(dsn, sql_dir / 'country_osm_grid.sql.gz')
116
117     if ignore_partitions:
118         with connect(dsn) as conn:
119             with conn.cursor() as cur:  # pylint: disable=E1101
120                 cur.execute('UPDATE country_name SET partition = 0')
121             conn.commit()  # pylint: disable=E1101