4 require_once(dirname(dirname(__FILE__)).'/settings/settings.php');
5 require_once(CONST_BasePath.'/lib/init-cmd.php');
6 ini_set('memory_limit', '800M');
10 'Create and setup nominatim search system',
11 array('help', 'h', 0, 1, 0, 0, false, 'Show Help'),
12 array('quiet', 'q', 0, 1, 0, 0, 'bool', 'Quiet output'),
13 array('verbose', 'v', 0, 1, 0, 0, 'bool', 'Verbose output'),
15 array('osm-file', '', 0, 1, 1, 1, 'realpath', 'File to import'),
16 array('threads', '', 0, 1, 1, 1, 'int', 'Number of threads (where possible)'),
18 array('all', '', 0, 1, 0, 0, 'bool', 'Do the complete process'),
20 array('create-db', '', 0, 1, 0, 0, 'bool', 'Create nominatim db'),
21 array('setup-db', '', 0, 1, 0, 0, 'bool', 'Build a blank nominatim db'),
22 array('import-data', '', 0, 1, 0, 0, 'bool', 'Import a osm file'),
23 array('osm2pgsql-cache', '', 0, 1, 1, 1, 'int', 'Cache size used by osm2pgsql'),
24 array('create-functions', '', 0, 1, 0, 0, 'bool', 'Create functions'),
25 array('enable-diff-updates', '', 0, 1, 0, 0, 'bool', 'Turn on the code required to make diff updates work'),
26 array('enable-debug-statements', '', 0, 1, 0, 0, 'bool', 'Include debug warning statements in pgsql commands'),
27 array('ignore-errors', '', 0, 1, 0, 0, 'bool', 'Continue import even when errors in SQL are present (EXPERT)'),
28 array('create-tables', '', 0, 1, 0, 0, 'bool', 'Create main tables'),
29 array('create-partition-tables', '', 0, 1, 0, 0, 'bool', 'Create required partition tables'),
30 array('create-partition-functions', '', 0, 1, 0, 0, 'bool', 'Create required partition triggers'),
31 array('no-partitions', '', 0, 1, 0, 0, 'bool', 'Do not partition search indices (speeds up import of single country extracts)'),
32 array('import-wikipedia-articles', '', 0, 1, 0, 0, 'bool', 'Import wikipedia article dump'),
33 array('load-data', '', 0, 1, 0, 0, 'bool', 'Copy data to live tables from import table'),
34 array('disable-token-precalc', '', 0, 1, 0, 0, 'bool', 'Disable name precalculation (EXPERT)'),
35 array('import-tiger-data', '', 0, 1, 0, 0, 'bool', 'Import tiger data (not included in \'all\')'),
36 array('calculate-postcodes', '', 0, 1, 0, 0, 'bool', 'Calculate postcode centroids'),
37 array('osmosis-init', '', 0, 1, 0, 0, 'bool', 'Generate default osmosis configuration'),
38 array('index', '', 0, 1, 0, 0, 'bool', 'Index the data'),
39 array('index-noanalyse', '', 0, 1, 0, 0, 'bool', 'Do not perform analyse operations during index (EXPERT)'),
40 array('create-search-indices', '', 0, 1, 0, 0, 'bool', 'Create additional indices required for search and update'),
41 array('create-country-names', '', 0, 1, 0, 0, 'bool', 'Create default list of searchable country names'),
42 array('drop', '', 0, 1, 0, 0, 'bool', 'Drop tables needed for updates, making the database readonly (EXPERIMENTAL)'),
44 getCmdOpt($_SERVER['argv'], $aCMDOptions, $aCMDResult, true, true);
46 $bDidSomething = false;
48 // Check if osm-file is set and points to a valid file if --all or --import-data is given
49 if ($aCMDResult['import-data'] || $aCMDResult['all']) {
50 if (!isset($aCMDResult['osm-file'])) {
51 fail('missing --osm-file for data import');
54 if (!file_exists($aCMDResult['osm-file'])) {
55 fail('the path supplied to --osm-file does not exist');
58 if (!is_readable($aCMDResult['osm-file'])) {
59 fail('osm-file "'.$aCMDResult['osm-file'].'" not readable');
64 // This is a pretty hard core default - the number of processors in the box - 1
65 $iInstances = isset($aCMDResult['threads'])?$aCMDResult['threads']:(getProcessorCount()-1);
66 if ($iInstances < 1) {
68 warn("resetting threads to $iInstances");
70 if ($iInstances > getProcessorCount()) {
71 $iInstances = getProcessorCount();
72 warn("resetting threads to $iInstances");
75 // Assume we can steal all the cache memory in the box (unless told otherwise)
76 if (isset($aCMDResult['osm2pgsql-cache'])) {
77 $iCacheMemory = $aCMDResult['osm2pgsql-cache'];
79 $iCacheMemory = getCacheMemoryMB();
82 $aDSNInfo = DB::parseDSN(CONST_Database_DSN);
83 if (!isset($aDSNInfo['port']) || !$aDSNInfo['port']) $aDSNInfo['port'] = 5432;
85 if ($aCMDResult['create-db'] || $aCMDResult['all']) {
87 $bDidSomething = true;
88 $oDB = DB::connect(CONST_Database_DSN, false);
89 if (!PEAR::isError($oDB)) {
90 fail('database already exists ('.CONST_Database_DSN.')');
92 passthruCheckReturn('createdb -E UTF-8 -p '.$aDSNInfo['port'].' '.$aDSNInfo['database']);
95 if ($aCMDResult['setup-db'] || $aCMDResult['all']) {
97 $bDidSomething = true;
101 $fPostgresVersion = getPostgresVersion($oDB);
102 echo 'Postgres version found: '.$fPostgresVersion."\n";
104 if ($fPostgresVersion < 9.1) {
105 fail('Minimum supported version of Postgresql is 9.1.');
108 pgsqlRunScript('CREATE EXTENSION IF NOT EXISTS hstore');
109 pgsqlRunScript('CREATE EXTENSION IF NOT EXISTS postgis');
111 // For extratags and namedetails the hstore_to_json converter is
112 // needed which is only available from Postgresql 9.3+. For older
113 // versions add a dummy function that returns nothing.
114 $iNumFunc = chksql($oDB->getOne("select count(*) from pg_proc where proname = 'hstore_to_json'"));
116 if ($iNumFunc == 0) {
117 pgsqlRunScript("create function hstore_to_json(dummy hstore) returns text AS 'select null::text' language sql immutable");
118 warn('Postgresql is too old. extratags and namedetails API not available.');
121 $fPostgisVersion = getPostgisVersion($oDB);
122 echo 'Postgis version found: '.$fPostgisVersion."\n";
124 if ($fPostgisVersion < 2.1) {
125 // Functions were renamed in 2.1 and throw an annoying deprecation warning
126 pgsqlRunScript('ALTER FUNCTION st_line_interpolate_point(geometry, double precision) RENAME TO ST_LineInterpolatePoint');
127 pgsqlRunScript('ALTER FUNCTION ST_Line_Locate_Point(geometry, geometry) RENAME TO ST_LineLocatePoint');
129 if ($fPostgisVersion < 2.2) {
130 pgsqlRunScript('ALTER FUNCTION ST_Distance_Spheroid(geometry, geometry, spheroid) RENAME TO ST_DistanceSpheroid');
133 $i = chksql($oDB->getOne("select count(*) from pg_user where usename = '".CONST_Database_Web_User."'"));
135 echo "\nERROR: Web user '".CONST_Database_Web_User."' does not exist. Create it with:\n";
136 echo "\n createuser ".CONST_Database_Web_User."\n\n";
140 // Try accessing the C module, so we know early if something is wrong
141 // and can simply error out.
142 $sSQL = "CREATE FUNCTION nominatim_test_import_func(text) RETURNS text AS '";
143 $sSQL .= CONST_InstallPath."/module/nominatim.so', 'transliteration' LANGUAGE c IMMUTABLE STRICT";
144 $sSQL .= ';DROP FUNCTION nominatim_test_import_func(text);';
145 $oResult = $oDB->query($sSQL);
147 if (PEAR::isError($oResult)) {
148 echo "\nERROR: Failed to load nominatim module. Reason:\n";
149 echo $oResult->userinfo."\n\n";
153 if (!file_exists(CONST_ExtraDataPath.'/country_osm_grid.sql.gz')) {
154 echo 'Error: you need to download the country_osm_grid first:';
155 echo "\n wget -O ".CONST_ExtraDataPath."/country_osm_grid.sql.gz https://www.nominatim.org/data/country_grid.sql.gz\n";
159 pgsqlRunScriptFile(CONST_BasePath.'/data/country_name.sql');
160 pgsqlRunScriptFile(CONST_BasePath.'/data/country_naturalearthdata.sql');
161 pgsqlRunScriptFile(CONST_BasePath.'/data/country_osm_grid.sql.gz');
162 pgsqlRunScriptFile(CONST_BasePath.'/data/gb_postcode_table.sql');
163 if (file_exists(CONST_BasePath.'/data/gb_postcode_data.sql.gz')) {
164 pgsqlRunScriptFile(CONST_BasePath.'/data/gb_postcode_data.sql.gz');
166 warn('external UK postcode table not found.');
168 if (CONST_Use_Extra_US_Postcodes) {
169 pgsqlRunScriptFile(CONST_BasePath.'/data/us_postcode.sql');
172 if ($aCMDResult['no-partitions']) {
173 pgsqlRunScript('update country_name set partition = 0');
176 // the following will be needed by create_functions later but
177 // is only defined in the subsequently called create_tables.
178 // Create dummies here that will be overwritten by the proper
179 // versions in create-tables.
180 pgsqlRunScript('CREATE TABLE place_boundingbox ()');
181 pgsqlRunScript('create type wikipedia_article_match as ()');
184 if ($aCMDResult['import-data'] || $aCMDResult['all']) {
186 $bDidSomething = true;
188 $osm2pgsql = CONST_Osm2pgsql_Binary;
189 if (!file_exists($osm2pgsql)) {
190 echo "Check CONST_Osm2pgsql_Binary in your local settings file.\n";
191 echo "Normally you should not need to set this manually.\n";
192 fail("osm2pgsql not found in '$osm2pgsql'");
195 if (!is_null(CONST_Osm2pgsql_Flatnode_File) && CONST_Osm2pgsql_Flatnode_File) {
196 $osm2pgsql .= ' --flat-nodes '.CONST_Osm2pgsql_Flatnode_File;
198 if (CONST_Tablespace_Osm2pgsql_Data)
199 $osm2pgsql .= ' --tablespace-slim-data '.CONST_Tablespace_Osm2pgsql_Data;
200 if (CONST_Tablespace_Osm2pgsql_Index)
201 $osm2pgsql .= ' --tablespace-slim-index '.CONST_Tablespace_Osm2pgsql_Index;
202 if (CONST_Tablespace_Place_Data)
203 $osm2pgsql .= ' --tablespace-main-data '.CONST_Tablespace_Place_Data;
204 if (CONST_Tablespace_Place_Index)
205 $osm2pgsql .= ' --tablespace-main-index '.CONST_Tablespace_Place_Index;
206 $osm2pgsql .= ' -lsc -O gazetteer --hstore --number-processes 1';
207 $osm2pgsql .= ' -C '.$iCacheMemory;
208 $osm2pgsql .= ' -P '.$aDSNInfo['port'];
209 $osm2pgsql .= ' -d '.$aDSNInfo['database'].' '.$aCMDResult['osm-file'];
210 passthruCheckReturn($osm2pgsql);
213 if (!$aCMDResult['ignore-errors'] && !chksql($oDB->getRow('select * from place limit 1'))) {
218 if ($aCMDResult['create-functions'] || $aCMDResult['all']) {
219 info('Create Functions');
220 $bDidSomething = true;
221 if (!file_exists(CONST_InstallPath.'/module/nominatim.so')) {
222 fail('nominatim module not built');
224 create_sql_functions($aCMDResult);
227 if ($aCMDResult['create-tables'] || $aCMDResult['all']) {
228 info('Create Tables');
229 $bDidSomething = true;
231 $sTemplate = file_get_contents(CONST_BasePath.'/sql/tables.sql');
232 $sTemplate = str_replace('{www-user}', CONST_Database_Web_User, $sTemplate);
233 $sTemplate = replace_tablespace(
235 CONST_Tablespace_Address_Data,
238 $sTemplate = replace_tablespace(
239 '{ts:address-index}',
240 CONST_Tablespace_Address_Index,
243 $sTemplate = replace_tablespace(
245 CONST_Tablespace_Search_Data,
248 $sTemplate = replace_tablespace(
250 CONST_Tablespace_Search_Index,
253 $sTemplate = replace_tablespace(
255 CONST_Tablespace_Aux_Data,
258 $sTemplate = replace_tablespace(
260 CONST_Tablespace_Aux_Index,
263 pgsqlRunScript($sTemplate, false);
265 // re-run the functions
266 info('Recreate Functions');
267 create_sql_functions($aCMDResult);
270 if ($aCMDResult['create-partition-tables'] || $aCMDResult['all']) {
271 info('Create Partition Tables');
272 $bDidSomething = true;
274 $sTemplate = file_get_contents(CONST_BasePath.'/sql/partition-tables.src.sql');
275 $sTemplate = replace_tablespace(
277 CONST_Tablespace_Address_Data,
280 $sTemplate = replace_tablespace(
281 '{ts:address-index}',
282 CONST_Tablespace_Address_Index,
285 $sTemplate = replace_tablespace(
287 CONST_Tablespace_Search_Data,
290 $sTemplate = replace_tablespace(
292 CONST_Tablespace_Search_Index,
295 $sTemplate = replace_tablespace(
297 CONST_Tablespace_Aux_Data,
300 $sTemplate = replace_tablespace(
302 CONST_Tablespace_Aux_Index,
306 pgsqlRunPartitionScript($sTemplate);
310 if ($aCMDResult['create-partition-functions'] || $aCMDResult['all']) {
311 info('Create Partition Functions');
312 $bDidSomething = true;
314 $sTemplate = file_get_contents(CONST_BasePath.'/sql/partition-functions.src.sql');
316 pgsqlRunPartitionScript($sTemplate);
319 if ($aCMDResult['import-wikipedia-articles'] || $aCMDResult['all']) {
320 $bDidSomething = true;
321 $sWikiArticlesFile = CONST_Wikipedia_Data_Path.'/wikipedia_article.sql.bin';
322 $sWikiRedirectsFile = CONST_Wikipedia_Data_Path.'/wikipedia_redirect.sql.bin';
323 if (file_exists($sWikiArticlesFile)) {
324 info('Importing wikipedia articles');
325 pgsqlRunDropAndRestore($sWikiArticlesFile);
327 warn('wikipedia article dump file not found - places will have default importance');
329 if (file_exists($sWikiRedirectsFile)) {
330 info('Importing wikipedia redirects');
331 pgsqlRunDropAndRestore($sWikiRedirectsFile);
333 warn('wikipedia redirect dump file not found - some place importance values may be missing');
338 if ($aCMDResult['load-data'] || $aCMDResult['all']) {
339 info('Drop old Data');
340 $bDidSomething = true;
343 if (!pg_query($oDB->connection, 'TRUNCATE word')) fail(pg_last_error($oDB->connection));
345 if (!pg_query($oDB->connection, 'TRUNCATE placex')) fail(pg_last_error($oDB->connection));
347 if (!pg_query($oDB->connection, 'TRUNCATE location_property_osmline')) fail(pg_last_error($oDB->connection));
349 if (!pg_query($oDB->connection, 'TRUNCATE place_addressline')) fail(pg_last_error($oDB->connection));
351 if (!pg_query($oDB->connection, 'TRUNCATE place_boundingbox')) fail(pg_last_error($oDB->connection));
353 if (!pg_query($oDB->connection, 'TRUNCATE location_area')) fail(pg_last_error($oDB->connection));
355 if (!pg_query($oDB->connection, 'TRUNCATE search_name')) fail(pg_last_error($oDB->connection));
357 if (!pg_query($oDB->connection, 'TRUNCATE search_name_blank')) fail(pg_last_error($oDB->connection));
359 if (!pg_query($oDB->connection, 'DROP SEQUENCE seq_place')) fail(pg_last_error($oDB->connection));
361 if (!pg_query($oDB->connection, 'CREATE SEQUENCE seq_place start 100000')) fail(pg_last_error($oDB->connection));
364 $sSQL = 'select distinct partition from country_name';
365 $aPartitions = chksql($oDB->getCol($sSQL));
366 if (!$aCMDResult['no-partitions']) $aPartitions[] = 0;
367 foreach ($aPartitions as $sPartition) {
368 if (!pg_query($oDB->connection, 'TRUNCATE location_road_'.$sPartition)) fail(pg_last_error($oDB->connection));
372 // used by getorcreate_word_id to ignore frequent partial words
373 $sSQL = 'CREATE OR REPLACE FUNCTION get_maxwordfreq() RETURNS integer AS ';
374 $sSQL .= '$$ SELECT '.CONST_Max_Word_Frequency.' as maxwordfreq; $$ LANGUAGE SQL IMMUTABLE';
375 if (!pg_query($oDB->connection, $sSQL)) {
376 fail(pg_last_error($oDB->connection));
380 // pre-create the word list
381 if (!$aCMDResult['disable-token-precalc']) {
382 info('Loading word list');
383 pgsqlRunScriptFile(CONST_BasePath.'/data/words.sql');
387 $sColumns = 'osm_type, osm_id, class, type, name, admin_level, address, extratags, geometry';
389 $aDBInstances = array();
390 $iLoadThreads = max(1, $iInstances - 1);
391 for ($i = 0; $i < $iLoadThreads; $i++) {
392 $aDBInstances[$i] =& getDB(true);
393 $sSQL = "INSERT INTO placex ($sColumns) SELECT $sColumns FROM place WHERE osm_id % $iLoadThreads = $i";
394 $sSQL .= " and not (class='place' and type='houses' and osm_type='W'";
395 $sSQL .= " and ST_GeometryType(geometry) = 'ST_LineString')";
396 $sSQL .= ' and ST_IsValid(geometry)';
397 if ($aCMDResult['verbose']) echo "$sSQL\n";
398 if (!pg_send_query($aDBInstances[$i]->connection, $sSQL)) {
399 fail(pg_last_error($aDBInstances[$i]->connection));
402 // last thread for interpolation lines
403 $aDBInstances[$iLoadThreads] =& getDB(true);
404 $sSQL = 'insert into location_property_osmline';
405 $sSQL .= ' (osm_id, address, linegeo)';
406 $sSQL .= ' SELECT osm_id, address, geometry from place where ';
407 $sSQL .= "class='place' and type='houses' and osm_type='W' and ST_GeometryType(geometry) = 'ST_LineString'";
408 if ($aCMDResult['verbose']) echo "$sSQL\n";
409 if (!pg_send_query($aDBInstances[$iLoadThreads]->connection, $sSQL)) {
410 fail(pg_last_error($aDBInstances[$iLoadThreads]->connection));
416 for ($i = 0; $i <= $iLoadThreads; $i++) {
417 if (pg_connection_busy($aDBInstances[$i]->connection)) $bAnyBusy = true;
423 info('Reanalysing database');
424 pgsqlRunScript('ANALYSE');
426 $sDatabaseDate = getDatabaseDate($oDB);
427 pg_query($oDB->connection, 'TRUNCATE import_status');
428 if ($sDatabaseDate === false) {
429 warn('could not determine database date.');
431 $sSQL = "INSERT INTO import_status (lastimportdate) VALUES('".$sDatabaseDate."')";
432 pg_query($oDB->connection, $sSQL);
433 echo "Latest data imported from $sDatabaseDate.\n";
437 if ($aCMDResult['import-tiger-data']) {
438 info('Import Tiger data');
439 $bDidSomething = true;
441 $sTemplate = file_get_contents(CONST_BasePath.'/sql/tiger_import_start.sql');
442 $sTemplate = str_replace('{www-user}', CONST_Database_Web_User, $sTemplate);
443 $sTemplate = replace_tablespace(
445 CONST_Tablespace_Aux_Data,
448 $sTemplate = replace_tablespace(
450 CONST_Tablespace_Aux_Index,
453 pgsqlRunScript($sTemplate, false);
455 $aDBInstances = array();
456 for ($i = 0; $i < $iInstances; $i++) {
457 $aDBInstances[$i] =& getDB(true);
460 foreach (glob(CONST_Tiger_Data_Path.'/*.sql') as $sFile) {
462 $hFile = fopen($sFile, 'r');
463 $sSQL = fgets($hFile, 100000);
467 for ($i = 0; $i < $iInstances; $i++) {
468 if (!pg_connection_busy($aDBInstances[$i]->connection)) {
469 while (pg_get_result($aDBInstances[$i]->connection));
470 $sSQL = fgets($hFile, 100000);
472 if (!pg_send_query($aDBInstances[$i]->connection, $sSQL)) fail(pg_last_error($oDB->connection));
474 if ($iLines == 1000) {
488 for ($i = 0; $i < $iInstances; $i++) {
489 if (pg_connection_busy($aDBInstances[$i]->connection)) $bAnyBusy = true;
496 info('Creating indexes on Tiger data');
497 $sTemplate = file_get_contents(CONST_BasePath.'/sql/tiger_import_finish.sql');
498 $sTemplate = str_replace('{www-user}', CONST_Database_Web_User, $sTemplate);
499 $sTemplate = replace_tablespace(
501 CONST_Tablespace_Aux_Data,
504 $sTemplate = replace_tablespace(
506 CONST_Tablespace_Aux_Index,
509 pgsqlRunScript($sTemplate, false);
512 if ($aCMDResult['calculate-postcodes'] || $aCMDResult['all']) {
513 info('Calculate Postcodes');
514 $bDidSomething = true;
516 if (!pg_query($oDB->connection, 'TRUNCATE location_postcode')) {
517 fail(pg_last_error($oDB->connection));
520 $sSQL = 'INSERT INTO location_postcode';
521 $sSQL .= ' (place_id, indexed_status, country_code, postcode, geometry) ';
522 $sSQL .= "SELECT nextval('seq_place'), 1, country_code,";
523 $sSQL .= " upper(trim (both ' ' from address->'postcode')) as pc,";
524 $sSQL .= ' ST_Centroid(ST_Collect(ST_Centroid(geometry)))';
525 $sSQL .= ' FROM placex';
526 $sSQL .= " WHERE address ? 'postcode' AND address->'postcode' NOT SIMILAR TO '%(,|;)%'";
527 $sSQL .= ' AND geometry IS NOT null';
528 $sSQL .= ' GROUP BY country_code, pc';
530 if (!pg_query($oDB->connection, $sSQL)) {
531 fail(pg_last_error($oDB->connection));
534 if (CONST_Use_Extra_US_Postcodes) {
535 // only add postcodes that are not yet available in OSM
536 $sSQL = 'INSERT INTO location_postcode';
537 $sSQL .= ' (place_id, indexed_status, country_code, postcode, geometry) ';
538 $sSQL .= "SELECT nextval('seq_place'), 1, 'us', postcode,";
539 $sSQL .= ' ST_SetSRID(ST_Point(x,y),4326)';
540 $sSQL .= ' FROM us_postcode WHERE postcode NOT IN';
541 $sSQL .= ' (SELECT postcode FROM location_postcode';
542 $sSQL .= " WHERE country_code = 'us')";
543 if (!pg_query($oDB->connection, $sSQL)) fail(pg_last_error($oDB->connection));
546 // add missing postcodes for GB (if available)
547 $sSQL = 'INSERT INTO location_postcode';
548 $sSQL .= ' (place_id, indexed_status, country_code, postcode, geometry) ';
549 $sSQL .= "SELECT nextval('seq_place'), 1, 'gb', postcode, geometry";
550 $sSQL .= ' FROM gb_postcode WHERE postcode NOT IN';
551 $sSQL .= ' (SELECT postcode FROM location_postcode';
552 $sSQL .= " WHERE country_code = 'gb')";
553 if (!pg_query($oDB->connection, $sSQL)) fail(pg_last_error($oDB->connection));
555 if (!$aCMDResult['all']) {
556 $sSQL = "DELETE FROM word WHERE class='place' and type='postcode'";
557 $sSQL .= 'and word NOT IN (SELECT postcode FROM location_postcode)';
558 if (!pg_query($oDB->connection, $sSQL)) {
559 fail(pg_last_error($oDB->connection));
562 $sSQL = 'SELECT count(getorcreate_postcode_id(v)) FROM ';
563 $sSQL .= '(SELECT distinct(postcode) as v FROM location_postcode) p';
565 if (!pg_query($oDB->connection, $sSQL)) {
566 fail(pg_last_error($oDB->connection));
570 if ($aCMDResult['osmosis-init']) {
571 $bDidSomething = true;
572 echo "Command 'osmosis-init' no longer available, please use utils/update.php --init-updates.\n";
575 if ($aCMDResult['index'] || $aCMDResult['all']) {
576 $bDidSomething = true;
578 $sBaseCmd = CONST_InstallPath.'/nominatim/nominatim -i -d '.$aDSNInfo['database'].' -P '.$aDSNInfo['port'].' -t '.$iInstances.$sOutputFile;
579 info('Index ranks 0 - 4');
580 passthruCheckReturn($sBaseCmd.' -R 4');
581 if (!$aCMDResult['index-noanalyse']) pgsqlRunScript('ANALYSE');
582 info('Index ranks 5 - 25');
583 passthruCheckReturn($sBaseCmd.' -r 5 -R 25');
584 if (!$aCMDResult['index-noanalyse']) pgsqlRunScript('ANALYSE');
585 info('Index ranks 26 - 30');
586 passthruCheckReturn($sBaseCmd.' -r 26');
588 info('Index postcodes');
590 $sSQL = 'UPDATE location_postcode SET indexed_status = 0';
591 if (!pg_query($oDB->connection, $sSQL)) fail(pg_last_error($oDB->connection));
594 if ($aCMDResult['create-search-indices'] || $aCMDResult['all']) {
595 info('Create Search indices');
596 $bDidSomething = true;
598 $sTemplate = file_get_contents(CONST_BasePath.'/sql/indices.src.sql');
599 $sTemplate = replace_tablespace(
600 '{ts:address-index}',
601 CONST_Tablespace_Address_Index,
604 $sTemplate = replace_tablespace(
606 CONST_Tablespace_Search_Index,
609 $sTemplate = replace_tablespace(
611 CONST_Tablespace_Aux_Index,
615 pgsqlRunScript($sTemplate);
618 if ($aCMDResult['create-country-names'] || $aCMDResult['all']) {
619 info('Create search index for default country names');
620 $bDidSomething = true;
622 pgsqlRunScript("select getorcreate_country(make_standard_name('uk'), 'gb')");
623 pgsqlRunScript("select getorcreate_country(make_standard_name('united states'), 'us')");
624 pgsqlRunScript('select count(*) from (select getorcreate_country(make_standard_name(country_code), country_code) from country_name where country_code is not null) as x');
625 pgsqlRunScript("select count(*) from (select getorcreate_country(make_standard_name(name->'name'), country_code) from country_name where name ? 'name') as x");
627 $sSQL = 'select count(*) from (select getorcreate_country(make_standard_name(v), country_code) from (select country_code, skeys(name) as k, svals(name) as v from country_name) x where k ';
628 if (CONST_Languages) {
631 foreach (explode(',', CONST_Languages) as $sLang) {
632 $sSQL .= $sDelim."'name:$sLang'";
637 // all include all simple name tags
638 $sSQL .= "like 'name:%'";
641 pgsqlRunScript($sSQL);
644 if ($aCMDResult['drop']) {
645 info('Drop tables only required for updates');
646 // The implementation is potentially a bit dangerous because it uses
647 // a positive selection of tables to keep, and deletes everything else.
648 // Including any tables that the unsuspecting user might have manually
649 // created. USE AT YOUR OWN PERIL.
650 $bDidSomething = true;
652 // tables we want to keep. everything else goes.
653 $aKeepTables = array(
659 'location_property*',
672 $aDropTables = array();
673 $aHaveTables = chksql($oDB->getCol("SELECT tablename FROM pg_tables WHERE schemaname='public'"));
675 foreach ($aHaveTables as $sTable) {
677 foreach ($aKeepTables as $sKeep) {
678 if (fnmatch($sKeep, $sTable)) {
683 if (!$bFound) array_push($aDropTables, $sTable);
686 foreach ($aDropTables as $sDrop) {
687 if ($aCMDResult['verbose']) echo "dropping table $sDrop\n";
688 @pg_query($oDB->connection, "DROP TABLE $sDrop CASCADE");
689 // ignore warnings/errors as they might be caused by a table having
690 // been deleted already by CASCADE
693 if (!is_null(CONST_Osm2pgsql_Flatnode_File) && CONST_Osm2pgsql_Flatnode_File) {
694 if ($aCMDResult['verbose']) echo 'deleting '.CONST_Osm2pgsql_Flatnode_File."\n";
695 unlink(CONST_Osm2pgsql_Flatnode_File);
699 if (!$bDidSomething) {
700 showUsage($aCMDOptions, true);
702 echo "Summary of warnings:\n\n";
705 info('Setup finished.');
709 function pgsqlRunScriptFile($sFilename)
712 if (!file_exists($sFilename)) fail('unable to find '.$sFilename);
714 // Convert database DSN to psql parameters
715 $aDSNInfo = DB::parseDSN(CONST_Database_DSN);
716 if (!isset($aDSNInfo['port']) || !$aDSNInfo['port']) $aDSNInfo['port'] = 5432;
717 $sCMD = 'psql -p '.$aDSNInfo['port'].' -d '.$aDSNInfo['database'];
718 if (!$aCMDResult['verbose']) {
723 if (preg_match('/\\.gz$/', $sFilename)) {
724 $aDescriptors = array(
725 0 => array('pipe', 'r'),
726 1 => array('pipe', 'w'),
727 2 => array('file', '/dev/null', 'a')
729 $hGzipProcess = proc_open('zcat '.$sFilename, $aDescriptors, $ahGzipPipes);
730 if (!is_resource($hGzipProcess)) fail('unable to start zcat');
731 $aReadPipe = $ahGzipPipes[1];
732 fclose($ahGzipPipes[0]);
734 $sCMD .= ' -f '.$sFilename;
735 $aReadPipe = array('pipe', 'r');
738 $aDescriptors = array(
740 1 => array('pipe', 'w'),
741 2 => array('file', '/dev/null', 'a')
744 $hProcess = proc_open($sCMD, $aDescriptors, $ahPipes);
745 if (!is_resource($hProcess)) fail('unable to start pgsql');
748 // TODO: error checking
749 while (!feof($ahPipes[1])) {
750 echo fread($ahPipes[1], 4096);
754 $iReturn = proc_close($hProcess);
756 fail("pgsql returned with error code ($iReturn)");
759 fclose($ahGzipPipes[1]);
760 proc_close($hGzipProcess);
764 function pgsqlRunScript($sScript, $bfatal = true)
770 $aCMDResult['verbose'],
771 $aCMDResult['ignore-errors']
775 function pgsqlRunPartitionScript($sTemplate)
780 $sSQL = 'select distinct partition from country_name';
781 $aPartitions = chksql($oDB->getCol($sSQL));
782 if (!$aCMDResult['no-partitions']) $aPartitions[] = 0;
784 preg_match_all('#^-- start(.*?)^-- end#ms', $sTemplate, $aMatches, PREG_SET_ORDER);
785 foreach ($aMatches as $aMatch) {
787 foreach ($aPartitions as $sPartitionName) {
788 $sResult .= str_replace('-partition-', $sPartitionName, $aMatch[1]);
790 $sTemplate = str_replace($aMatch[0], $sResult, $sTemplate);
793 pgsqlRunScript($sTemplate);
796 function pgsqlRunRestoreData($sDumpFile)
798 // Convert database DSN to psql parameters
799 $aDSNInfo = DB::parseDSN(CONST_Database_DSN);
800 if (!isset($aDSNInfo['port']) || !$aDSNInfo['port']) $aDSNInfo['port'] = 5432;
801 $sCMD = 'pg_restore -p '.$aDSNInfo['port'].' -d '.$aDSNInfo['database'].' -Fc -a '.$sDumpFile;
803 $aDescriptors = array(
804 0 => array('pipe', 'r'),
805 1 => array('pipe', 'w'),
806 2 => array('file', '/dev/null', 'a')
809 $hProcess = proc_open($sCMD, $aDescriptors, $ahPipes);
810 if (!is_resource($hProcess)) fail('unable to start pg_restore');
814 // TODO: error checking
815 while (!feof($ahPipes[1])) {
816 echo fread($ahPipes[1], 4096);
820 $iReturn = proc_close($hProcess);
823 function pgsqlRunDropAndRestore($sDumpFile)
825 // Convert database DSN to psql parameters
826 $aDSNInfo = DB::parseDSN(CONST_Database_DSN);
827 if (!isset($aDSNInfo['port']) || !$aDSNInfo['port']) $aDSNInfo['port'] = 5432;
828 $sCMD = 'pg_restore -p '.$aDSNInfo['port'].' -d '.$aDSNInfo['database'].' -Fc --clean '.$sDumpFile;
830 $aDescriptors = array(
831 0 => array('pipe', 'r'),
832 1 => array('pipe', 'w'),
833 2 => array('file', '/dev/null', 'a')
836 $hProcess = proc_open($sCMD, $aDescriptors, $ahPipes);
837 if (!is_resource($hProcess)) fail('unable to start pg_restore');
841 // TODO: error checking
842 while (!feof($ahPipes[1])) {
843 echo fread($ahPipes[1], 4096);
847 $iReturn = proc_close($hProcess);
850 function passthruCheckReturn($cmd)
853 passthru($cmd, $result);
854 if ($result != 0) fail('Error executing external command: '.$cmd);
857 function replace_tablespace($sTemplate, $sTablespace, $sSql)
860 $sSql = str_replace($sTemplate, 'TABLESPACE "'.$sTablespace.'"', $sSql);
862 $sSql = str_replace($sTemplate, '', $sSql);
868 function create_sql_functions($aCMDResult)
870 $sTemplate = file_get_contents(CONST_BasePath.'/sql/functions.sql');
871 $sTemplate = str_replace('{modulepath}', CONST_InstallPath.'/module', $sTemplate);
872 if ($aCMDResult['enable-diff-updates']) {
873 $sTemplate = str_replace('RETURN NEW; -- %DIFFUPDATES%', '--', $sTemplate);
875 if ($aCMDResult['enable-debug-statements']) {
876 $sTemplate = str_replace('--DEBUG:', '', $sTemplate);
878 if (CONST_Limit_Reindexing) {
879 $sTemplate = str_replace('--LIMIT INDEXING:', '', $sTemplate);
881 if (!CONST_Use_US_Tiger_Data) {
882 $sTemplate = str_replace('-- %NOTIGERDATA% ', '', $sTemplate);
884 if (!CONST_Use_Aux_Location_data) {
885 $sTemplate = str_replace('-- %NOAUXDATA% ', '', $sTemplate);
887 pgsqlRunScript($sTemplate);