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