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