2 # SPDX-License-Identifier: GPL-2.0-only
4 # This file is part of Nominatim.
5 # Copyright (C) 2020 Sarah Hoffmann
8 Script for analysing the indexing process.
10 The script enables detailed logging for nested statements and then
11 runs the indexing process for teh given object. Detailed 'EXPLAIN ANALYSE'
12 information is printed for each executed query in the trigger. The
13 transaction is then rolled back, so that no actual changes to the database
14 happen. It also disables logging into the system log, so that the
15 log files are not cluttered.
18 from argparse import ArgumentParser, RawDescriptionHelpFormatter, ArgumentTypeError
23 class Analyser(object):
25 def __init__(self, options):
27 if options.password_prompt:
28 password = getpass.getpass("Database password: ")
30 self.options = options
31 self.conn = psycopg2.connect(dbname=options.dbname,
40 c = self.conn.cursor()
42 if self.options.placeid:
43 place_id = self.options.placeid
46 c.execute(f"""select place_id from placex
47 where rank_address = {self.options.rank}
48 and linked_place_id is null
50 objinfo = f"rank {self.options.rank}"
52 if self.options.osmid:
53 osm_type = self.options.osmid[0].upper()
54 if osm_type not in ('N', 'W', 'R'):
55 raise RuntimeError("OSM ID must be of form <N|W|R><id>")
57 osm_id = int(self.options.osmid[1:])
59 raise RuntimeError("OSM ID must be of form <N|W|R><id>")
61 c.execute(f"""SELECT place_id FROM placex
62 WHERE osm_type = '{osm_type}' AND osm_id = {osm_id}""")
63 objinfo = f"OSM object {self.options.osmid}"
67 raise RuntimeError(f"Cannot find a place for {objinfo}.")
68 place_id = c.fetchone()[0]
70 c.execute(f"""update placex set indexed_status = 2 where
71 place_id = {place_id}""")
73 c.execute("""SET auto_explain.log_min_duration = '0';
74 SET auto_explain.log_analyze = 'true';
75 SET auto_explain.log_nested_statements = 'true';
77 SET client_min_messages = LOG;
78 SET log_min_messages = FATAL""");
80 c.execute(f"""update placex set indexed_status = 0 where
81 place_id = {place_id}""")
83 c.close() # automatic rollback
85 for l in self.conn.notices:
89 if __name__ == '__main__':
91 return re.sub("\s\s+" , " ", s)
93 p = ArgumentParser(description=__doc__,
94 formatter_class=RawDescriptionHelpFormatter)
96 group = p.add_mutually_exclusive_group(required=True)
97 group.add_argument('--rank', dest='rank', type=int,
98 help='Analyse indexing of the given address rank')
99 group.add_argument('--osm-id', dest='osmid', type=str,
100 help='Analyse indexing of the given OSM object')
101 group.add_argument('--place-id', dest='placeid', type=int,
102 help='Analyse indexing of the given Nominatim object')
103 p.add_argument('-d', '--database',
104 dest='dbname', action='store', default='nominatim',
105 help='Name of the PostgreSQL database to connect to.')
106 p.add_argument('-U', '--username',
107 dest='user', action='store',
108 help='PostgreSQL user name.')
109 p.add_argument('-W', '--password',
110 dest='password_prompt', action='store_true',
111 help='Force password prompt.')
112 p.add_argument('-H', '--host',
113 dest='host', action='store',
114 help='PostgreSQL server hostname or socket location.')
115 p.add_argument('-P', '--port',
116 dest='port', action='store',
117 help='PostgreSQL server port')
119 Analyser(p.parse_args()).run()