]> git.openstreetmap.org Git - nominatim.git/blob - utils/update.php
Merge remote-tracking branch 'upstream/master'
[nominatim.git] / utils / update.php
1 <?php
2
3 require_once(CONST_BasePath.'/lib/init-cmd.php');
4 require_once(CONST_BasePath.'/lib/setup_functions.php');
5 require_once(CONST_BasePath.'/lib/setup/SetupClass.php');
6 require_once(CONST_BasePath.'/lib/setup/AddressLevelParser.php');
7
8 ini_set('memory_limit', '800M');
9
10 use Nominatim\Setup\SetupFunctions as SetupFunctions;
11
12 // (long-opt, short-opt, min-occurs, max-occurs, num-arguments, num-arguments, type, help)
13 $aCMDOptions
14 = array(
15    'Import / update / index osm data',
16    array('help', 'h', 0, 1, 0, 0, false, 'Show Help'),
17    array('quiet', 'q', 0, 1, 0, 0, 'bool', 'Quiet output'),
18    array('verbose', 'v', 0, 1, 0, 0, 'bool', 'Verbose output'),
19
20    array('init-updates', '', 0, 1, 0, 0, 'bool', 'Set up database for updating'),
21    array('check-for-updates', '', 0, 1, 0, 0, 'bool', 'Check if new updates are available'),
22    array('no-update-functions', '', 0, 1, 0, 0, 'bool', 'Do not update trigger functions to support differential updates (assuming the diff update logic is already present)'),
23    array('import-osmosis', '', 0, 1, 0, 0, 'bool', 'Import updates once'),
24    array('import-osmosis-all', '', 0, 1, 0, 0, 'bool', 'Import updates forever'),
25    array('no-index', '', 0, 1, 0, 0, 'bool', 'Do not index the new data'),
26
27    array('calculate-postcodes', '', 0, 1, 0, 0, 'bool', 'Update postcode centroid table'),
28
29    array('import-file', '', 0, 1, 1, 1, 'realpath', 'Re-import data from an OSM file'),
30    array('import-diff', '', 0, 1, 1, 1, 'realpath', 'Import a diff (osc) file from local file system'),
31    array('osm2pgsql-cache', '', 0, 1, 1, 1, 'int', 'Cache size used by osm2pgsql'),
32
33    array('import-node', '', 0, 1, 1, 1, 'int', 'Re-import node'),
34    array('import-way', '', 0, 1, 1, 1, 'int', 'Re-import way'),
35    array('import-relation', '', 0, 1, 1, 1, 'int', 'Re-import relation'),
36    array('import-from-main-api', '', 0, 1, 0, 0, 'bool', 'Use OSM API instead of Overpass to download objects'),
37
38    array('index', '', 0, 1, 0, 0, 'bool', 'Index'),
39    array('index-rank', '', 0, 1, 1, 1, 'int', 'Rank to start indexing from'),
40    array('index-instances', '', 0, 1, 1, 1, 'int', 'Number of indexing instances (threads)'),
41
42    array('deduplicate', '', 0, 1, 0, 0, 'bool', 'Deduplicate tokens'),
43    array('recompute-word-counts', '', 0, 1, 0, 0, 'bool', 'Compute frequency of full-word search terms'),
44    array('update-address-levels', '', 0, 1, 0, 0, 'bool', 'Reimport address level configuration (EXPERT)'),
45    array('recompute-importance', '', 0, 1, 0, 0, 'bool', 'Recompute place importances'),
46    array('no-npi', '', 0, 1, 0, 0, 'bool', '(obsolete)'),
47   );
48
49 getCmdOpt($_SERVER['argv'], $aCMDOptions, $aResult, true, true);
50
51 if (!isset($aResult['index-instances'])) $aResult['index-instances'] = 1;
52
53 if (!isset($aResult['index-rank'])) $aResult['index-rank'] = 0;
54
55 date_default_timezone_set('Etc/UTC');
56
57 $oDB = new Nominatim\DB();
58 $oDB->connect();
59
60 $aDSNInfo = Nominatim\DB::parseDSN(CONST_Database_DSN);
61 if (!isset($aDSNInfo['port']) || !$aDSNInfo['port']) $aDSNInfo['port'] = 5432;
62
63 // cache memory to be used by osm2pgsql, should not be more than the available memory
64 $iCacheMemory = (isset($aResult['osm2pgsql-cache'])?$aResult['osm2pgsql-cache']:2000);
65 if ($iCacheMemory + 500 > getTotalMemoryMB()) {
66     $iCacheMemory = getCacheMemoryMB();
67     echo "WARNING: resetting cache memory to $iCacheMemory\n";
68 }
69
70 $oOsm2pgsqlCmd = (new \Nominatim\Shell(CONST_Osm2pgsql_Binary))
71                  ->addParams('--hstore')
72                  ->addParams('--latlong')
73                  ->addParams('--append')
74                  ->addParams('--slim')
75                  ->addParams('--number-processes', 1)
76                  ->addParams('--cache', $iCacheMemory)
77                  ->addParams('--output', 'gazetteer')
78                  ->addParams('--style', CONST_Import_Style)
79                  ->addParams('--database', $aDSNInfo['database'])
80                  ->addParams('--port', $aDSNInfo['port']);
81
82 if (isset($aDSNInfo['hostspec']) && $aDSNInfo['hostspec']) {
83     $oOsm2pgsqlCmd->addParams('--host', $aDSNInfo['hostspec']);
84 }
85 if (isset($aDSNInfo['username']) && $aDSNInfo['username']) {
86     $oOsm2pgsqlCmd->addParams('--user', $aDSNInfo['username']);
87 }
88 if (isset($aDSNInfo['password']) && $aDSNInfo['password']) {
89     $oOsm2pgsqlCmd->addEnvPair('PGPASSWORD', $aDSNInfo['password']);
90 }
91 if (!is_null(CONST_Osm2pgsql_Flatnode_File) && CONST_Osm2pgsql_Flatnode_File) {
92     $oOsm2pgsqlCmd->addParams('--flat-nodes', CONST_Osm2pgsql_Flatnode_File);
93 }
94
95
96 $oIndexCmd = (new \Nominatim\Shell(CONST_BasePath.'/nominatim/nominatim.py'))
97              ->addParams('--database', $aDSNInfo['database'])
98              ->addParams('--port', $aDSNInfo['port'])
99              ->addParams('--threads', $aResult['index-instances']);
100
101 if ($aResult['verbose']) {
102     $oIndexCmd->addParams('--verbose');
103 }
104 if (isset($aDSNInfo['hostspec']) && $aDSNInfo['hostspec']) {
105     $oIndexCmd->addParams('--host', $aDSNInfo['hostspec']);
106 }
107 if (isset($aDSNInfo['username']) && $aDSNInfo['username']) {
108     $oIndexCmd->addParams('--username', $aDSNInfo['username']);
109 }
110 if (isset($aDSNInfo['password']) && $aDSNInfo['password']) {
111     $oIndexCmd->addEnvPair('PGPASSWORD', $aDSNInfo['password']);
112 }
113
114
115 if ($aResult['init-updates']) {
116     // sanity check that the replication URL is correct
117     $sBaseState = file_get_contents(CONST_Replication_Url.'/state.txt');
118     if ($sBaseState === false) {
119         echo "\nCannot find state.txt file at the configured replication URL.\n";
120         echo "Does the URL point to a directory containing OSM update data?\n\n";
121         fail('replication URL not reachable.');
122     }
123     // sanity check for pyosmium-get-changes
124     if (!CONST_Pyosmium_Binary) {
125         echo "\nCONST_Pyosmium_Binary not configured.\n";
126         echo "You need to install pyosmium and set up the path to pyosmium-get-changes\n";
127         echo "in your local settings file.\n\n";
128         fail('CONST_Pyosmium_Binary not configured');
129     }
130
131     $aOutput = 0;
132     $oCMD = new \Nominatim\Shell(CONST_Pyosmium_Binary, '--help');
133     exec($oCMD->escapedCmd(), $aOutput, $iRet);
134
135     if ($iRet != 0) {
136         echo "Cannot execute pyosmium-get-changes.\n";
137         echo "Make sure you have pyosmium installed correctly\n";
138         echo "and have set up CONST_Pyosmium_Binary to point to pyosmium-get-changes.\n";
139         fail('pyosmium-get-changes not found or not usable');
140     }
141
142     if (!$aResult['no-update-functions']) {
143         // instantiate setupClass to use the function therein
144         $cSetup = new SetupFunctions(array(
145                                       'enable-diff-updates' => true,
146                                       'verbose' => $aResult['verbose']
147                                      ));
148         $cSetup->connect();
149         $cSetup->createFunctions();
150     }
151
152     $sDatabaseDate = getDatabaseDate($oDB);
153     if (!$sDatabaseDate) {
154         fail('Cannot determine date of database.');
155     }
156     $sWindBack = strftime('%Y-%m-%dT%H:%M:%SZ', strtotime($sDatabaseDate) - (3*60*60));
157
158     // get the appropriate state id
159     $aOutput = 0;
160     $oCMD = (new \Nominatim\Shell(CONST_Pyosmium_Binary))
161             ->addParams('--start-date', $sWindBack)
162             ->addParams('--server', CONST_Replication_Url);
163
164     exec($oCMD->escapedCmd(), $aOutput, $iRet);
165     if ($iRet != 0 || $aOutput[0] == 'None') {
166         fail('Error running pyosmium tools');
167     }
168
169     $oDB->exec('TRUNCATE import_status');
170     $sSQL = "INSERT INTO import_status (lastimportdate, sequence_id, indexed) VALUES('";
171     $sSQL .= $sDatabaseDate."',".$aOutput[0].', true)';
172
173     try {
174         $oDB->exec($sSQL);
175     } catch (\Nominatim\DatabaseError $e) {
176         fail('Could not enter sequence into database.');
177     }
178
179     echo "Done. Database updates will start at sequence $aOutput[0] ($sWindBack)\n";
180 }
181
182 if ($aResult['check-for-updates']) {
183     $aLastState = $oDB->getRow('SELECT sequence_id FROM import_status');
184
185     if (!$aLastState['sequence_id']) {
186         fail('Updates not set up. Please run ./utils/update.php --init-updates.');
187     }
188
189     $oCmd = (new \Nominatim\Shell(CONST_BasePath.'/utils/check_server_for_updates.py'))
190             ->addParams(CONST_Replication_Url)
191             ->addParams($aLastState['sequence_id']);
192     $iRet = $oCmd->run();
193
194     exit($iRet);
195 }
196
197 if (isset($aResult['import-diff']) || isset($aResult['import-file'])) {
198     // import diffs and files directly (e.g. from osmosis --rri)
199     $sNextFile = isset($aResult['import-diff']) ? $aResult['import-diff'] : $aResult['import-file'];
200
201     if (!file_exists($sNextFile)) {
202         fail("Cannot open $sNextFile\n");
203     }
204
205     // Import the file
206     $oCMD = (clone $oOsm2pgsqlCmd)->addParams($sNextFile);
207     echo $oCMD->escapedCmd()."\n";
208     $iRet = $oCMD->run();
209
210     if ($iRet) {
211         fail("Error from osm2pgsql, $iRet\n");
212     }
213
214     // Don't update the import status - we don't know what this file contains
215 }
216
217 if ($aResult['calculate-postcodes']) {
218     info('Update postcodes centroids');
219     $sTemplate = file_get_contents(CONST_BasePath.'/sql/update-postcodes.sql');
220     runSQLScript($sTemplate, true, true);
221 }
222
223 $sTemporaryFile = CONST_BasePath.'/data/osmosischange.osc';
224 $bHaveDiff = false;
225 $bUseOSMApi = isset($aResult['import-from-main-api']) && $aResult['import-from-main-api'];
226 $sContentURL = '';
227 if (isset($aResult['import-node']) && $aResult['import-node']) {
228     if ($bUseOSMApi) {
229         $sContentURL = 'https://www.openstreetmap.org/api/0.6/node/'.$aResult['import-node'];
230     } else {
231         $sContentURL = 'https://overpass-api.de/api/interpreter?data=node('.$aResult['import-node'].');out%20meta;';
232     }
233 }
234
235 if (isset($aResult['import-way']) && $aResult['import-way']) {
236     if ($bUseOSMApi) {
237         $sContentURL = 'https://www.openstreetmap.org/api/0.6/way/'.$aResult['import-way'].'/full';
238     } else {
239         $sContentURL = 'https://overpass-api.de/api/interpreter?data=(way('.$aResult['import-way'].');node(w););out%20meta;';
240     }
241 }
242
243 if (isset($aResult['import-relation']) && $aResult['import-relation']) {
244     if ($bUseOSMApi) {
245         $sContentURLsModifyXMLstr = 'https://www.openstreetmap.org/api/0.6/relation/'.$aResult['import-relation'].'/full';
246     } else {
247         $sContentURL = 'https://overpass-api.de/api/interpreter?data=((rel('.$aResult['import-relation'].');way(r);node(w));node(r));out%20meta;';
248     }
249 }
250
251 if ($sContentURL) {
252     file_put_contents($sTemporaryFile, file_get_contents($sContentURL));
253     $bHaveDiff = true;
254 }
255
256 if ($bHaveDiff) {
257     // import generated change file
258
259     $oCMD = (clone $oOsm2pgsqlCmd)->addParams($sTemporaryFile);
260     echo $oCMD->escapedCmd()."\n";
261
262     $iRet = $oCMD->run();
263     if ($iRet) {
264         fail("osm2pgsql exited with error level $iRet\n");
265     }
266 }
267
268 if ($aResult['deduplicate']) {
269     $oDB = new Nominatim\DB();
270     $oDB->connect();
271
272     if ($oDB->getPostgresVersion() < 9.3) {
273         fail('ERROR: deduplicate is only currently supported in postgresql 9.3');
274     }
275
276     $sSQL = 'select partition from country_name order by country_code';
277     $aPartitions = $oDB->getCol($sSQL);
278     $aPartitions[] = 0;
279
280     // we don't care about empty search_name_* partitions, they can't contain mentions of duplicates
281     foreach ($aPartitions as $i => $sPartition) {
282         $sSQL = 'select count(*) from search_name_'.$sPartition;
283         $nEntries = $oDB->getOne($sSQL);
284         if ($nEntries == 0) {
285             unset($aPartitions[$i]);
286         }
287     }
288
289     $sSQL = "select word_token,count(*) from word where substr(word_token, 1, 1) = ' '";
290     $sSQL .= ' and class is null and type is null and country_code is null';
291     $sSQL .= ' group by word_token having count(*) > 1 order by word_token';
292     $aDuplicateTokens = $oDB->getAll($sSQL);
293     foreach ($aDuplicateTokens as $aToken) {
294         if (trim($aToken['word_token']) == '' || trim($aToken['word_token']) == '-') continue;
295         echo 'Deduping '.$aToken['word_token']."\n";
296         $sSQL = 'select word_id,';
297         $sSQL .= ' (select count(*) from search_name where nameaddress_vector @> ARRAY[word_id]) as num';
298         $sSQL .= " from word where word_token = '".$aToken['word_token'];
299         $sSQL .= "' and class is null and type is null and country_code is null order by num desc";
300         $aTokenSet = $oDB->getAll($sSQL);
301
302         $aKeep = array_shift($aTokenSet);
303         $iKeepID = $aKeep['word_id'];
304
305         foreach ($aTokenSet as $aRemove) {
306             $sSQL = 'update search_name set';
307             $sSQL .= ' name_vector = array_replace(name_vector,'.$aRemove['word_id'].','.$iKeepID.'),';
308             $sSQL .= ' nameaddress_vector = array_replace(nameaddress_vector,'.$aRemove['word_id'].','.$iKeepID.')';
309             $sSQL .= ' where name_vector @> ARRAY['.$aRemove['word_id'].']';
310             $oDB->exec($sSQL);
311
312             $sSQL = 'update search_name set';
313             $sSQL .= ' nameaddress_vector = array_replace(nameaddress_vector,'.$aRemove['word_id'].','.$iKeepID.')';
314             $sSQL .= ' where nameaddress_vector @> ARRAY['.$aRemove['word_id'].']';
315             $oDB->exec($sSQL);
316
317             $sSQL = 'update location_area_country set';
318             $sSQL .= ' keywords = array_replace(keywords,'.$aRemove['word_id'].','.$iKeepID.')';
319             $sSQL .= ' where keywords @> ARRAY['.$aRemove['word_id'].']';
320             $oDB->exec($sSQL);
321
322             foreach ($aPartitions as $sPartition) {
323                 $sSQL = 'update search_name_'.$sPartition.' set';
324                 $sSQL .= ' name_vector = array_replace(name_vector,'.$aRemove['word_id'].','.$iKeepID.')';
325                 $sSQL .= ' where name_vector @> ARRAY['.$aRemove['word_id'].']';
326                 $oDB->exec($sSQL);
327
328                 $sSQL = 'update location_area_country set';
329                 $sSQL .= ' keywords = array_replace(keywords,'.$aRemove['word_id'].','.$iKeepID.')';
330                 $sSQL .= ' where keywords @> ARRAY['.$aRemove['word_id'].']';
331                 $oDB->exec($sSQL);
332             }
333
334             $sSQL = 'delete from word where word_id = '.$aRemove['word_id'];
335             $oDB->exec($sSQL);
336         }
337     }
338 }
339
340 if ($aResult['recompute-word-counts']) {
341     info('Recompute frequency of full-word search terms');
342     $sTemplate = file_get_contents(CONST_BasePath.'/sql/words_from_search_name.sql');
343     runSQLScript($sTemplate, true, true);
344 }
345
346 if ($aResult['index']) {
347     $oCmd = (clone $oIndexCmd)
348             ->addParams('--minrank', $aResult['index-rank']);
349
350     // echo $oCmd->escapedCmd()."\n";
351     $oCmd->run();
352
353     $oDB->exec('update import_status set indexed = true');
354 }
355
356 if ($aResult['update-address-levels']) {
357     echo 'Updating address levels from '.CONST_Address_Level_Config.".\n";
358     $oAlParser = new \Nominatim\Setup\AddressLevelParser(CONST_Address_Level_Config);
359     $oAlParser->createTable($oDB, 'address_levels');
360 }
361
362 if ($aResult['recompute-importance']) {
363     echo "Updating importance values for database.\n";
364     $oDB = new Nominatim\DB();
365     $oDB->connect();
366
367     $sSQL = 'ALTER TABLE placex DISABLE TRIGGER ALL;';
368     $sSQL .= 'UPDATE placex SET (wikipedia, importance) =';
369     $sSQL .= '   (SELECT wikipedia, importance';
370     $sSQL .= '    FROM compute_importance(extratags, country_code, osm_type, osm_id));';
371     $sSQL .= 'UPDATE placex s SET wikipedia = d.wikipedia, importance = d.importance';
372     $sSQL .= ' FROM placex d';
373     $sSQL .= ' WHERE s.place_id = d.linked_place_id and d.wikipedia is not null';
374     $sSQL .= '       and (s.wikipedia is null or s.importance < d.importance);';
375     $sSQL .= 'ALTER TABLE placex ENABLE TRIGGER ALL;';
376     $oDB->exec($sSQL);
377 }
378
379 if ($aResult['import-osmosis'] || $aResult['import-osmosis-all']) {
380     //
381     if (strpos(CONST_Replication_Url, 'download.geofabrik.de') !== false && CONST_Replication_Update_Interval < 86400) {
382         fail('Error: Update interval too low for download.geofabrik.de. ' .
383              "Please check install documentation (https://nominatim.org/release-docs/latest/admin/Import-and-Update#setting-up-the-update-process)\n");
384     }
385
386     $sImportFile = CONST_InstallPath.'/osmosischange.osc';
387
388     $oCMDDownload = (new \Nominatim\Shell(CONST_Pyosmium_Binary))
389                     ->addParams('--server', CONST_Replication_Url)
390                     ->addParams('--outfile', $sImportFile)
391                     ->addParams('--size', CONST_Replication_Max_Diff_size);
392
393     $oCMDImport = (clone $oOsm2pgsqlCmd)->addParams($sImportFile);
394
395     while (true) {
396         $fStartTime = time();
397         $aLastState = $oDB->getRow('SELECT *, EXTRACT (EPOCH FROM lastimportdate) as unix_ts FROM import_status');
398
399         if (!$aLastState['sequence_id']) {
400             echo "Updates not set up. Please run ./utils/update.php --init-updates.\n";
401             exit(1);
402         }
403
404         echo 'Currently at sequence '.$aLastState['sequence_id'].' ('.$aLastState['lastimportdate'].') - '.$aLastState['indexed']." indexed\n";
405
406         $sBatchEnd = $aLastState['lastimportdate'];
407         $iEndSequence = $aLastState['sequence_id'];
408
409         if ($aLastState['indexed']) {
410             // Sleep if the update interval has not yet been reached.
411             $fNextUpdate = $aLastState['unix_ts'] + CONST_Replication_Update_Interval;
412             if ($fNextUpdate > $fStartTime) {
413                 $iSleepTime = $fNextUpdate - $fStartTime;
414                 echo "Waiting for next update for $iSleepTime sec.";
415                 sleep($iSleepTime);
416             }
417
418             // Download the next batch of changes.
419             do {
420                 $fCMDStartTime = time();
421                 $iNextSeq = (int) $aLastState['sequence_id'];
422                 unset($aOutput);
423
424                 $oCMD = (clone $oCMDDownload)->addParams('--start-id', $iNextSeq);
425                 echo $oCMD->escapedCmd()."\n";
426                 if (file_exists($sImportFile)) {
427                     unlink($sImportFile);
428                 }
429                 exec($oCMD->escapedCmd(), $aOutput, $iResult);
430
431                 if ($iResult == 3) {
432                     echo 'No new updates. Sleeping for '.CONST_Replication_Recheck_Interval." sec.\n";
433                     sleep(CONST_Replication_Recheck_Interval);
434                 } elseif ($iResult != 0) {
435                     echo 'ERROR: updates failed.';
436                     exit($iResult);
437                 } else {
438                     $iEndSequence = (int)$aOutput[0];
439                 }
440             } while ($iResult);
441
442             // get the newest object from the diff file
443             $sBatchEnd = 0;
444             $iRet = 0;
445             $oCMD = new \Nominatim\Shell(CONST_BasePath.'/utils/osm_file_date.py', $sImportFile);
446             exec($oCMD->escapedCmd(), $sBatchEnd, $iRet);
447             if ($iRet == 5) {
448                 echo "Diff file is empty. skipping import.\n";
449                 if (!$aResult['import-osmosis-all']) {
450                     exit(0);
451                 } else {
452                     continue;
453                 }
454             }
455             if ($iRet != 0) {
456                 fail('Error getting date from diff file.');
457             }
458             $sBatchEnd = $sBatchEnd[0];
459
460             // Import the file
461             $fCMDStartTime = time();
462
463
464             echo $oCMDImport->escapedCmd()."\n";
465             unset($sJunk);
466             $iErrorLevel = $oCMDImport->run();
467             if ($iErrorLevel) {
468                 echo "Error executing osm2pgsql: $iErrorLevel\n";
469                 exit($iErrorLevel);
470             }
471
472             // write the update logs
473             $iFileSize = filesize($sImportFile);
474             $sSQL = 'INSERT INTO import_osmosis_log';
475             $sSQL .= '(batchend, batchseq, batchsize, starttime, endtime, event)';
476             $sSQL .= " values ('$sBatchEnd',$iEndSequence,$iFileSize,'";
477             $sSQL .= date('Y-m-d H:i:s', $fCMDStartTime)."','";
478             $sSQL .= date('Y-m-d H:i:s')."','import')";
479             var_Dump($sSQL);
480             $oDB->exec($sSQL);
481
482             // update the status
483             $sSQL = "UPDATE import_status SET lastimportdate = '$sBatchEnd', indexed=false, sequence_id = $iEndSequence";
484             var_Dump($sSQL);
485             $oDB->exec($sSQL);
486             echo date('Y-m-d H:i:s')." Completed download step for $sBatchEnd in ".round((time()-$fCMDStartTime)/60, 2)." minutes\n";
487         }
488
489         // Index file
490         if (!$aResult['no-index']) {
491             $oThisIndexCmd = clone($oIndexCmd);
492             $fCMDStartTime = time();
493
494             echo $oThisIndexCmd->escapedCmd()."\n";
495             $iErrorLevel = $oThisIndexCmd->run();
496             if ($iErrorLevel) {
497                 echo "Error: $iErrorLevel\n";
498                 exit($iErrorLevel);
499             }
500
501             $sSQL = 'INSERT INTO import_osmosis_log';
502             $sSQL .= '(batchend, batchseq, batchsize, starttime, endtime, event)';
503             $sSQL .= " values ('$sBatchEnd',$iEndSequence,NULL,'";
504             $sSQL .= date('Y-m-d H:i:s', $fCMDStartTime)."','";
505             $sSQL .= date('Y-m-d H:i:s')."','index')";
506             var_Dump($sSQL);
507             $oDB->exec($sSQL);
508             echo date('Y-m-d H:i:s')." Completed index step for $sBatchEnd in ".round((time()-$fCMDStartTime)/60, 2)." minutes\n";
509
510             $sSQL = 'update import_status set indexed = true';
511             $oDB->exec($sSQL);
512         } else {
513             if ($aResult['import-osmosis-all']) {
514                 echo "Error: --no-index cannot be used with continuous imports (--import-osmosis-all).\n";
515                 exit(1);
516             }
517         }
518
519         $fDuration = time() - $fStartTime;
520         echo date('Y-m-d H:i:s')." Completed all for $sBatchEnd in ".round($fDuration/60, 2)." minutes\n";
521         if (!$aResult['import-osmosis-all']) exit(0);
522     }
523 }