2 Tests for migration functions
7 from nominatim.tools import migration
8 from nominatim.errors import UsageError
9 import nominatim.version
13 def update_sql_functions(self, config):
18 def postprocess_mock(monkeypatch):
19 monkeypatch.setattr(migration.refresh, 'create_functions', lambda *args: args)
20 monkeypatch.setattr(migration.tokenizer_factory, 'get_tokenizer_for_db',
21 lambda *args: DummyTokenizer())
24 def test_no_migration_old_versions(temp_db_with_extensions, table_factory, def_config):
25 table_factory('country_name', 'name HSTORE, country_code TEXT')
27 with pytest.raises(UsageError, match='Migration not possible'):
28 migration.migrate(def_config, {})
31 def test_set_up_migration_for_36(temp_db_with_extensions, temp_db_cursor,
32 table_factory, def_config, monkeypatch,
34 psycopg2.extras.register_hstore(temp_db_cursor)
35 # don't actually run any migration, except the property table creation
36 monkeypatch.setattr(migration, '_MIGRATION_FUNCTIONS',
37 [((3, 5, 0, 99), migration.add_nominatim_property_table)])
38 # Use a r/o user name that always exists
39 monkeypatch.setenv('NOMINATIM_DATABASE_WEBUSER', 'postgres')
41 table_factory('country_name', 'name HSTORE, country_code TEXT',
42 (({str(x): 'a' for x in range(200)}, 'gb'),))
44 assert not temp_db_cursor.table_exists('nominatim_properties')
46 assert migration.migrate(def_config, {}) == 0
48 assert temp_db_cursor.table_exists('nominatim_properties')
50 assert 1 == temp_db_cursor.scalar(""" SELECT count(*) FROM nominatim_properties
51 WHERE property = 'database_version'""")
54 def test_already_at_version(def_config, property_table):
56 property_table.set('database_version',
57 '{0[0]}.{0[1]}.{0[2]}-{0[3]}'.format(nominatim.version.NOMINATIM_VERSION))
59 assert migration.migrate(def_config, {}) == 0
62 def test_no_migrations_necessary(def_config, temp_db_cursor, property_table,
64 oldversion = [x for x in nominatim.version.NOMINATIM_VERSION]
66 property_table.set('database_version',
67 '{0[0]}.{0[1]}.{0[2]}-{0[3]}'.format(oldversion))
70 monkeypatch.setattr(migration, '_MIGRATION_FUNCTIONS',
71 [(tuple(oldversion), lambda **attr: True)])
73 assert migration.migrate(def_config, {}) == 0
76 def test_run_single_migration(def_config, temp_db_cursor, property_table,
77 monkeypatch, postprocess_mock):
78 oldversion = [x for x in nominatim.version.NOMINATIM_VERSION]
80 property_table.set('database_version',
81 '{0[0]}.{0[1]}.{0[2]}-{0[3]}'.format(oldversion))
83 done = {'old': False, 'new': False}
85 """ Dummy migration"""
88 def _old_migration(**_):
89 """ Dummy migration"""
93 monkeypatch.setattr(migration, '_MIGRATION_FUNCTIONS',
94 [(tuple(oldversion), _old_migration),
95 (nominatim.version.NOMINATIM_VERSION, _migration)])
97 assert migration.migrate(def_config, {}) == 0
100 assert not done['old']
101 assert property_table.get('database_version') == \
102 '{0[0]}.{0[1]}.{0[2]}-{0[3]}'.format(nominatim.version.NOMINATIM_VERSION)
105 ###### Tests for specific migrations
107 # Each migration should come with two tests:
108 # 1. Test that migration from old to new state works as expected.
109 # 2. Test that the migration can be rerun on the new state without side effects.
112 @pytest.mark.parametrize('in_attr', ('', 'with time zone'))
113 def test_import_status_timestamp_change(temp_db_conn, temp_db_cursor,
114 table_factory, in_attr):
115 table_factory('import_status',
116 f"""lastimportdate timestamp {in_attr},
120 migration.import_status_timestamp_change(temp_db_conn)
121 temp_db_conn.commit()
123 assert temp_db_cursor.scalar("""SELECT data_type FROM information_schema.columns
124 WHERE table_name = 'import_status'
125 and column_name = 'lastimportdate'""")\
126 == 'timestamp with time zone'
129 def test_add_nominatim_property_table(temp_db_conn, temp_db_cursor,
130 def_config, monkeypatch):
131 # Use a r/o user name that always exists
132 monkeypatch.setenv('NOMINATIM_DATABASE_WEBUSER', 'postgres')
134 assert not temp_db_cursor.table_exists('nominatim_properties')
136 migration.add_nominatim_property_table(temp_db_conn, def_config)
137 temp_db_conn.commit()
139 assert temp_db_cursor.table_exists('nominatim_properties')
142 def test_add_nominatim_property_table_repeat(temp_db_conn, temp_db_cursor,
143 def_config, property_table):
144 assert temp_db_cursor.table_exists('nominatim_properties')
146 migration.add_nominatim_property_table(temp_db_conn, def_config)
147 temp_db_conn.commit()
149 assert temp_db_cursor.table_exists('nominatim_properties')
152 def test_change_housenumber_transliteration(temp_db_conn, temp_db_cursor,
153 word_table, placex_table):
154 placex_table.add(housenumber='3A')
156 temp_db_cursor.execute("""CREATE OR REPLACE FUNCTION make_standard_name(name TEXT)
157 RETURNS TEXT AS $$ SELECT lower(name) $$ LANGUAGE SQL """)
158 temp_db_cursor.execute("""CREATE OR REPLACE FUNCTION getorcreate_housenumber_id(lookup_word TEXT)
159 RETURNS INTEGER AS $$ SELECT 4325 $$ LANGUAGE SQL """)
161 migration.change_housenumber_transliteration(temp_db_conn)
162 temp_db_conn.commit()
164 assert temp_db_cursor.scalar('SELECT housenumber from placex') == '3a'
166 migration.change_housenumber_transliteration(temp_db_conn)
167 temp_db_conn.commit()
169 assert temp_db_cursor.scalar('SELECT housenumber from placex') == '3a'
172 def test_switch_placenode_geometry_index(temp_db_conn, temp_db_cursor, placex_table):
173 temp_db_cursor.execute("""CREATE INDEX idx_placex_adminname
174 ON placex (place_id)""")
176 migration.switch_placenode_geometry_index(temp_db_conn)
177 temp_db_conn.commit()
179 assert temp_db_cursor.index_exists('placex', 'idx_placex_geometry_placenode')
180 assert not temp_db_cursor.index_exists('placex', 'idx_placex_adminname')
183 def test_switch_placenode_geometry_index_repeat(temp_db_conn, temp_db_cursor, placex_table):
184 temp_db_cursor.execute("""CREATE INDEX idx_placex_geometry_placenode
185 ON placex (place_id)""")
187 migration.switch_placenode_geometry_index(temp_db_conn)
188 temp_db_conn.commit()
190 assert temp_db_cursor.index_exists('placex', 'idx_placex_geometry_placenode')
191 assert not temp_db_cursor.index_exists('placex', 'idx_placex_adminname')
192 assert temp_db_cursor.scalar("""SELECT indexdef from pg_indexes
193 WHERE tablename = 'placex'
194 and indexname = 'idx_placex_geometry_placenode'
195 """).endswith('(place_id)')
198 def test_install_legacy_tokenizer(temp_db_conn, temp_db_cursor, project_env,
199 property_table, table_factory, monkeypatch,
201 table_factory('placex', 'place_id BIGINT')
202 table_factory('location_property_osmline', 'place_id BIGINT')
204 # Setting up the tokenizer is problematic
206 def migrate_database(self, config):
209 monkeypatch.setattr(migration.tokenizer_factory, 'create_tokenizer',
210 lambda cfg, **kwargs: MiniTokenizer())
212 migration.install_legacy_tokenizer(temp_db_conn, project_env)
213 temp_db_conn.commit()
217 def test_install_legacy_tokenizer_repeat(temp_db_conn, temp_db_cursor,
218 def_config, property_table):
220 property_table.set('tokenizer', 'dummy')
221 migration.install_legacy_tokenizer(temp_db_conn, def_config)
222 temp_db_conn.commit()
225 def test_create_tiger_housenumber_index(temp_db_conn, temp_db_cursor, table_factory):
226 table_factory('location_property_tiger',
227 'parent_place_id BIGINT, startnumber INT, endnumber INT')
229 migration.create_tiger_housenumber_index(temp_db_conn)
230 temp_db_conn.commit()
232 if temp_db_conn.server_version_tuple() >= (11, 0, 0):
233 assert temp_db_cursor.index_exists('location_property_tiger',
234 'idx_location_property_tiger_housenumber_migrated')
236 migration.create_tiger_housenumber_index(temp_db_conn)
237 temp_db_conn.commit()