X-Git-Url: https://git.openstreetmap.org./nominatim.git/blobdiff_plain/446a5a95bc9ce401d7cf2afffc091aa36b9c2307..0df5f77bc4c92e2f8f635105441779277435da67:/utils/setup.php diff --git a/utils/setup.php b/utils/setup.php index c25dc094..ea3e14a3 100755 --- a/utils/setup.php +++ b/utils/setup.php @@ -1,13 +1,21 @@ -#!/usr/bin/php -Cq +#!@PHP_BIN@ -Cq indirect via init-cmd.php->/lib/cmd.php for runWithEnv, getCmdOpt +// ->indirect via init-cmd.php->/lib/init.php->db.php for &getDB() + +require_once(CONST_BasePath.'/lib/setup/SetupClass.php'); +require_once(CONST_BasePath.'/lib/setup_functions.php'); ini_set('memory_limit', '800M'); +use Nominatim\Setup\SetupFunctions as SetupFunctions; + +// (long-opt, short-opt, min-occurs, max-occurs, num-arguments, num-arguments, type, help) $aCMDOptions = array( - "Create and setup nominatim search system", + 'Create and setup nominatim search system', array('help', 'h', 0, 1, 0, 0, false, 'Show Help'), array('quiet', 'q', 0, 1, 0, 0, 'bool', 'Quiet output'), array('verbose', 'v', 0, 1, 0, 0, 'bool', 'Verbose output'), @@ -28,7 +36,7 @@ $aCMDOptions array('create-tables', '', 0, 1, 0, 0, 'bool', 'Create main tables'), array('create-partition-tables', '', 0, 1, 0, 0, 'bool', 'Create required partition tables'), array('create-partition-functions', '', 0, 1, 0, 0, 'bool', 'Create required partition triggers'), - array('no-partitions', '', 0, 1, 0, 0, 'bool', "Do not partition search indices (speeds up import of single country extracts)"), + array('no-partitions', '', 0, 1, 0, 0, 'bool', 'Do not partition search indices (speeds up import of single country extracts)'), array('import-wikipedia-articles', '', 0, 1, 0, 0, 'bool', 'Import wikipedia article dump'), array('load-data', '', 0, 1, 0, 0, 'bool', 'Copy data to live tables from import table'), array('disable-token-precalc', '', 0, 1, 0, 0, 'bool', 'Disable name precalculation (EXPERT)'), @@ -41,850 +49,120 @@ $aCMDOptions array('create-country-names', '', 0, 1, 0, 0, 'bool', 'Create default list of searchable country names'), array('drop', '', 0, 1, 0, 0, 'bool', 'Drop tables needed for updates, making the database readonly (EXPERIMENTAL)'), ); + +// $aCMDOptions passed to getCmdOpt by reference getCmdOpt($_SERVER['argv'], $aCMDOptions, $aCMDResult, true, true); $bDidSomething = false; -// Check if osm-file is set and points to a valid file if --all or --import-data is given +//******************************************************* +// Making some sanity check: +// Check if osm-file is set and points to a valid file if ($aCMDResult['import-data'] || $aCMDResult['all']) { - if (!isset($aCMDResult['osm-file'])) { - fail('missing --osm-file for data import'); - } - - if (!file_exists($aCMDResult['osm-file'])) { - fail('the path supplied to --osm-file does not exist'); - } - - if (!is_readable($aCMDResult['osm-file'])) { - fail('osm-file "'.$aCMDResult['osm-file'].'" not readable'); - } + // to remain in /lib/setup_functions.php function + checkInFile($aCMDResult['osm-file']); } - -// This is a pretty hard core default - the number of processors in the box - 1 -$iInstances = isset($aCMDResult['threads'])?$aCMDResult['threads']:(getProcessorCount()-1); -if ($iInstances < 1) { - $iInstances = 1; - echo "WARNING: resetting threads to $iInstances\n"; -} -if ($iInstances > getProcessorCount()) { - $iInstances = getProcessorCount(); - echo "WARNING: resetting threads to $iInstances\n"; -} - -// Assume we can steal all the cache memory in the box (unless told otherwise) -if (isset($aCMDResult['osm2pgsql-cache'])) { - $iCacheMemory = $aCMDResult['osm2pgsql-cache']; -} else { - $iCacheMemory = getCacheMemoryMB(); +// osmosis init is no longer supported +if ($aCMDResult['osmosis-init']) { + $bDidSomething = true; + echo "Command 'osmosis-init' no longer available, please use utils/update.php --init-updates.\n"; } -$aDSNInfo = DB::parseDSN(CONST_Database_DSN); -if (!isset($aDSNInfo['port']) || !$aDSNInfo['port']) $aDSNInfo['port'] = 5432; +// ****************************************************** +// instantiate Setup class +$cSetup = new SetupFunctions($aCMDResult); +// ******************************************************* +// go through complete process if 'all' is selected or start selected functions if ($aCMDResult['create-db'] || $aCMDResult['all']) { - echo "Create DB\n"; $bDidSomething = true; - $oDB = DB::connect(CONST_Database_DSN, false); - if (!PEAR::isError($oDB)) { - fail('database already exists ('.CONST_Database_DSN.')'); - } - passthruCheckReturn('createdb -E UTF-8 -p '.$aDSNInfo['port'].' '.$aDSNInfo['database']); + $cSetup->createDB(); } if ($aCMDResult['setup-db'] || $aCMDResult['all']) { - echo "Setup DB\n"; $bDidSomething = true; + $cSetup->setupDB(); +} - // TODO: path detection, detection memory, etc. - // - $oDB =& getDB(); - - $fPostgresVersion = getPostgresVersion($oDB); - echo 'Postgres version found: '.$fPostgresVersion."\n"; - - if ($fPostgresVersion < 9.1) { - fail("Minimum supported version of Postgresql is 9.1."); - } - - pgsqlRunScript('CREATE EXTENSION IF NOT EXISTS hstore'); - pgsqlRunScript('CREATE EXTENSION IF NOT EXISTS postgis'); - - // For extratags and namedetails the hstore_to_json converter is - // needed which is only available from Postgresql 9.3+. For older - // versions add a dummy function that returns nothing. - $iNumFunc = chksql($oDB->getOne("select count(*) from pg_proc where proname = 'hstore_to_json'")); - - if ($iNumFunc == 0) { - pgsqlRunScript("create function hstore_to_json(dummy hstore) returns text AS 'select null::text' language sql immutable"); - echo "WARNING: Postgresql is too old. extratags and namedetails API not available."; - } - - $fPostgisVersion = getPostgisVersion($oDB); - echo 'Postgis version found: '.$fPostgisVersion."\n"; - - if ($fPostgisVersion < 2.1) { - // Functions were renamed in 2.1 and throw an annoying deprecation warning - pgsqlRunScript('ALTER FUNCTION st_line_interpolate_point(geometry, double precision) RENAME TO ST_LineInterpolatePoint'); - pgsqlRunScript('ALTER FUNCTION ST_Line_Locate_Point(geometry, geometry) RENAME TO ST_LineLocatePoint'); - } - if ($fPostgisVersion < 2.2) { - pgsqlRunScript('ALTER FUNCTION ST_Distance_Spheroid(geometry, geometry, spheroid) RENAME TO ST_DistanceSpheroid'); - } - - pgsqlRunScriptFile(CONST_BasePath.'/data/country_name.sql'); - pgsqlRunScriptFile(CONST_BasePath.'/data/country_naturalearthdata.sql'); - pgsqlRunScriptFile(CONST_BasePath.'/data/country_osm_grid.sql'); - pgsqlRunScriptFile(CONST_BasePath.'/data/gb_postcode_table.sql'); - if (file_exists(CONST_BasePath.'/data/gb_postcode_data.sql.gz')) { - pgsqlRunScriptFile(CONST_BasePath.'/data/gb_postcode_data.sql.gz'); - } else { - echo "WARNING: external UK postcode table not found.\n"; - } - if (CONST_Use_Extra_US_Postcodes) { - pgsqlRunScriptFile(CONST_BasePath.'/data/us_postcode.sql'); - } - - if ($aCMDResult['no-partitions']) { - pgsqlRunScript('update country_name set partition = 0'); - } - - // the following will be needed by create_functions later but - // is only defined in the subsequently called create_tables. - // Create dummies here that will be overwritten by the proper - // versions in create-tables. - pgsqlRunScript('CREATE TABLE place_boundingbox ()'); - pgsqlRunScript('create type wikipedia_article_match as ()'); +// Try accessing the C module, so we know early if something is wrong +if (!checkModulePresence()) { + fail('error loading nominatim.so module'); } if ($aCMDResult['import-data'] || $aCMDResult['all']) { - echo "Import\n"; $bDidSomething = true; - - $osm2pgsql = CONST_Osm2pgsql_Binary; - if (!file_exists($osm2pgsql)) { - echo "Check CONST_Osm2pgsql_Binary in your local settings file.\n"; - echo "Normally you should not need to set this manually.\n"; - fail("osm2pgsql not found in '$osm2pgsql'"); - } - - if (!is_null(CONST_Osm2pgsql_Flatnode_File)) { - $osm2pgsql .= ' --flat-nodes '.CONST_Osm2pgsql_Flatnode_File; - } - if (CONST_Tablespace_Osm2pgsql_Data) - $osm2pgsql .= ' --tablespace-slim-data '.CONST_Tablespace_Osm2pgsql_Data; - if (CONST_Tablespace_Osm2pgsql_Index) - $osm2pgsql .= ' --tablespace-slim-index '.CONST_Tablespace_Osm2pgsql_Index; - if (CONST_Tablespace_Place_Data) - $osm2pgsql .= ' --tablespace-main-data '.CONST_Tablespace_Place_Data; - if (CONST_Tablespace_Place_Index) - $osm2pgsql .= ' --tablespace-main-index '.CONST_Tablespace_Place_Index; - $osm2pgsql .= ' -lsc -O gazetteer --hstore --number-processes 1'; - $osm2pgsql .= ' -C '.$iCacheMemory; - $osm2pgsql .= ' -P '.$aDSNInfo['port']; - $osm2pgsql .= ' -d '.$aDSNInfo['database'].' '.$aCMDResult['osm-file']; - passthruCheckReturn($osm2pgsql); - - $oDB =& getDB(); - if (!$aCMDResult['ignore-errors'] && !chksql($oDB->getRow('select * from place limit 1'))) { - fail('No Data'); - } + $cSetup->importData($aCMDResult['osm-file']); } if ($aCMDResult['create-functions'] || $aCMDResult['all']) { - echo "Functions\n"; $bDidSomething = true; - if (!file_exists(CONST_InstallPath.'/module/nominatim.so')) fail("nominatim module not built"); - create_sql_functions($aCMDResult); + $cSetup->createFunctions(); } if ($aCMDResult['create-tables'] || $aCMDResult['all']) { $bDidSomething = true; - - echo "Tables\n"; - $sTemplate = file_get_contents(CONST_BasePath.'/sql/tables.sql'); - $sTemplate = str_replace('{www-user}', CONST_Database_Web_User, $sTemplate); - $sTemplate = replace_tablespace( - '{ts:address-data}', - CONST_Tablespace_Address_Data, - $sTemplate - ); - $sTemplate = replace_tablespace( - '{ts:address-index}', - CONST_Tablespace_Address_Index, - $sTemplate - ); - $sTemplate = replace_tablespace( - '{ts:search-data}', - CONST_Tablespace_Search_Data, - $sTemplate - ); - $sTemplate = replace_tablespace( - '{ts:search-index}', - CONST_Tablespace_Search_Index, - $sTemplate - ); - $sTemplate = replace_tablespace( - '{ts:aux-data}', - CONST_Tablespace_Aux_Data, - $sTemplate - ); - $sTemplate = replace_tablespace( - '{ts:aux-index}', - CONST_Tablespace_Aux_Index, - $sTemplate - ); - pgsqlRunScript($sTemplate, false); - - // re-run the functions - echo "Functions\n"; - create_sql_functions($aCMDResult); + $cSetup->createTables(); + $cSetup->createFunctions(); } if ($aCMDResult['create-partition-tables'] || $aCMDResult['all']) { - echo "Partition Tables\n"; $bDidSomething = true; - - $sTemplate = file_get_contents(CONST_BasePath.'/sql/partition-tables.src.sql'); - $sTemplate = replace_tablespace( - '{ts:address-data}', - CONST_Tablespace_Address_Data, - $sTemplate - ); - $sTemplate = replace_tablespace( - '{ts:address-index}', - CONST_Tablespace_Address_Index, - $sTemplate - ); - $sTemplate = replace_tablespace( - '{ts:search-data}', - CONST_Tablespace_Search_Data, - $sTemplate - ); - $sTemplate = replace_tablespace( - '{ts:search-index}', - CONST_Tablespace_Search_Index, - $sTemplate - ); - $sTemplate = replace_tablespace( - '{ts:aux-data}', - CONST_Tablespace_Aux_Data, - $sTemplate - ); - $sTemplate = replace_tablespace( - '{ts:aux-index}', - CONST_Tablespace_Aux_Index, - $sTemplate - ); - - pgsqlRunPartitionScript($sTemplate); + $cSetup->createPartitionTables(); } - if ($aCMDResult['create-partition-functions'] || $aCMDResult['all']) { - echo "Partition Functions\n"; $bDidSomething = true; - - $sTemplate = file_get_contents(CONST_BasePath.'/sql/partition-functions.src.sql'); - - pgsqlRunPartitionScript($sTemplate); + $cSetup->createPartitionFunctions(); } if ($aCMDResult['import-wikipedia-articles'] || $aCMDResult['all']) { $bDidSomething = true; - $sWikiArticlesFile = CONST_Wikipedia_Data_Path.'/wikipedia_article.sql.bin'; - $sWikiRedirectsFile = CONST_Wikipedia_Data_Path.'/wikipedia_redirect.sql.bin'; - if (file_exists($sWikiArticlesFile)) { - echo "Importing wikipedia articles..."; - pgsqlRunDropAndRestore($sWikiArticlesFile); - echo "...done\n"; - } else { - echo "WARNING: wikipedia article dump file not found - places will have default importance\n"; - } - if (file_exists($sWikiRedirectsFile)) { - echo "Importing wikipedia redirects..."; - pgsqlRunDropAndRestore($sWikiRedirectsFile); - echo "...done\n"; - } else { - echo "WARNING: wikipedia redirect dump file not found - some place importance values may be missing\n"; - } + $cSetup->importWikipediaArticles(); } - if ($aCMDResult['load-data'] || $aCMDResult['all']) { - echo "Drop old Data\n"; $bDidSomething = true; - - $oDB =& getDB(); - if (!pg_query($oDB->connection, 'TRUNCATE word')) fail(pg_last_error($oDB->connection)); - echo '.'; - if (!pg_query($oDB->connection, 'TRUNCATE placex')) fail(pg_last_error($oDB->connection)); - echo '.'; - if (!pg_query($oDB->connection, 'TRUNCATE location_property_osmline')) fail(pg_last_error($oDB->connection)); - echo '.'; - if (!pg_query($oDB->connection, 'TRUNCATE place_addressline')) fail(pg_last_error($oDB->connection)); - echo '.'; - if (!pg_query($oDB->connection, 'TRUNCATE place_boundingbox')) fail(pg_last_error($oDB->connection)); - echo '.'; - if (!pg_query($oDB->connection, 'TRUNCATE location_area')) fail(pg_last_error($oDB->connection)); - echo '.'; - if (!pg_query($oDB->connection, 'TRUNCATE search_name')) fail(pg_last_error($oDB->connection)); - echo '.'; - if (!pg_query($oDB->connection, 'TRUNCATE search_name_blank')) fail(pg_last_error($oDB->connection)); - echo '.'; - if (!pg_query($oDB->connection, 'DROP SEQUENCE seq_place')) fail(pg_last_error($oDB->connection)); - echo '.'; - if (!pg_query($oDB->connection, 'CREATE SEQUENCE seq_place start 100000')) fail(pg_last_error($oDB->connection)); - echo '.'; - - $sSQL = 'select distinct partition from country_name'; - $aPartitions = chksql($oDB->getCol($sSQL)); - if (!$aCMDResult['no-partitions']) $aPartitions[] = 0; - foreach ($aPartitions as $sPartition) { - if (!pg_query($oDB->connection, 'TRUNCATE location_road_'.$sPartition)) fail(pg_last_error($oDB->connection)); - echo '.'; - } - - // used by getorcreate_word_id to ignore frequent partial words - $sSQL = 'CREATE OR REPLACE FUNCTION get_maxwordfreq() RETURNS integer AS '; - $sSQL .= '$$ SELECT '.CONST_Max_Word_Frequency.' as maxwordfreq; $$ LANGUAGE SQL IMMUTABLE'; - if (!pg_query($oDB->connection, $sSQL)) { - fail(pg_last_error($oDB->connection)); - } - echo ".\n"; - - // pre-create the word list - if (!$aCMDResult['disable-token-precalc']) { - echo "Loading word list\n"; - pgsqlRunScriptFile(CONST_BasePath.'/data/words.sql'); - } - - echo "Load Data\n"; - $aDBInstances = array(); - $iLoadThreads = max(1, $iInstances - 1); - for ($i = 0; $i < $iLoadThreads; $i++) { - $aDBInstances[$i] =& getDB(true); - $sSQL = 'insert into placex (osm_type, osm_id, class, type, name, admin_level, '; - $sSQL .= ' address, extratags, geometry) '; - $sSQL .= 'select * from place where osm_id % '.$iLoadThreads.' = '.$i; - $sSQL .= " and not (class='place' and type='houses' and osm_type='W' and ST_GeometryType(geometry) = 'ST_LineString')"; - if ($aCMDResult['verbose']) echo "$sSQL\n"; - if (!pg_send_query($aDBInstances[$i]->connection, $sSQL)) { - fail(pg_last_error($aDBInstances[$i]->connection)); - } - } - // last thread for interpolation lines - $aDBInstances[$iLoadThreads] =& getDB(true); - $sSQL = 'insert into location_property_osmline'; - $sSQL .= ' (osm_id, address, linegeo)'; - $sSQL .= ' SELECT osm_id, address, geometry from place where '; - $sSQL .= "class='place' and type='houses' and osm_type='W' and ST_GeometryType(geometry) = 'ST_LineString'"; - if ($aCMDResult['verbose']) echo "$sSQL\n"; - if (!pg_send_query($aDBInstances[$iLoadThreads]->connection, $sSQL)) { - fail(pg_last_error($aDBInstances[$iLoadThreads]->->connection)); - } - - $bAnyBusy = true; - while ($bAnyBusy) { - $bAnyBusy = false; - for ($i = 0; $i <= $iLoadThreads; $i++) { - if (pg_connection_busy($aDBInstances[$i]->connection)) $bAnyBusy = true; - } - sleep(1); - echo '.'; - } - echo "\n"; - echo "Reanalysing database...\n"; - pgsqlRunScript('ANALYSE'); + $cSetup->loadData($aCMDResult['disable-token-precalc']); } if ($aCMDResult['import-tiger-data']) { $bDidSomething = true; - - $sTemplate = file_get_contents(CONST_BasePath.'/sql/tiger_import_start.sql'); - $sTemplate = str_replace('{www-user}', CONST_Database_Web_User, $sTemplate); - $sTemplate = replace_tablespace( - '{ts:aux-data}', - CONST_Tablespace_Aux_Data, - $sTemplate - ); - $sTemplate = replace_tablespace( - '{ts:aux-index}', - CONST_Tablespace_Aux_Index, - $sTemplate - ); - pgsqlRunScript($sTemplate, false); - - $aDBInstances = array(); - for ($i = 0; $i < $iInstances; $i++) { - $aDBInstances[$i] =& getDB(true); - } - - foreach (glob(CONST_Tiger_Data_Path.'/*.sql') as $sFile) { - echo $sFile.': '; - $hFile = fopen($sFile, "r"); - $sSQL = fgets($hFile, 100000); - $iLines = 0; - - while (true) { - for ($i = 0; $i < $iInstances; $i++) { - if (!pg_connection_busy($aDBInstances[$i]->connection)) { - while (pg_get_result($aDBInstances[$i]->connection)); - $sSQL = fgets($hFile, 100000); - if (!$sSQL) break 2; - if (!pg_send_query($aDBInstances[$i]->connection, $sSQL)) fail(pg_last_error($oDB->connection)); - $iLines++; - if ($iLines == 1000) { - echo "."; - $iLines = 0; - } - } - } - usleep(10); - } - - fclose($hFile); - - $bAnyBusy = true; - while ($bAnyBusy) { - $bAnyBusy = false; - for ($i = 0; $i < $iInstances; $i++) { - if (pg_connection_busy($aDBInstances[$i]->connection)) $bAnyBusy = true; - } - usleep(10); - } - echo "\n"; - } - - echo "Creating indexes\n"; - $sTemplate = file_get_contents(CONST_BasePath.'/sql/tiger_import_finish.sql'); - $sTemplate = str_replace('{www-user}', CONST_Database_Web_User, $sTemplate); - $sTemplate = replace_tablespace( - '{ts:aux-data}', - CONST_Tablespace_Aux_Data, - $sTemplate - ); - $sTemplate = replace_tablespace( - '{ts:aux-index}', - CONST_Tablespace_Aux_Index, - $sTemplate - ); - pgsqlRunScript($sTemplate, false); + $cSetup->importTigerData(); } if ($aCMDResult['calculate-postcodes'] || $aCMDResult['all']) { $bDidSomething = true; - $oDB =& getDB(); - if (!pg_query($oDB->connection, 'DELETE from placex where osm_type=\'P\'')) fail(pg_last_error($oDB->connection)); - $sSQL = "insert into placex (osm_type,osm_id,class,type,postcode,country_code,geometry) "; - $sSQL .= "select 'P',nextval('seq_postcodes'),'place','postcode',postcode,country_code,"; - $sSQL .= "ST_SetSRID(ST_Point(x,y),4326) as geometry from (select country_code,postcode,"; - $sSQL .= "avg(st_x(st_centroid(geometry))) as x,avg(st_y(st_centroid(geometry))) as y "; - $sSQL .= "from placex where postcode is not null group by country_code,postcode) as x "; - $sSQL .= "where ST_Point(x,y) is not null"; - if (!pg_query($oDB->connection, $sSQL)) fail(pg_last_error($oDB->connection)); - - if (CONST_Use_Extra_US_Postcodes) { - $sSQL = "insert into placex (osm_type,osm_id,class,type,postcode,country_code,geometry) "; - $sSQL .= "select 'P',nextval('seq_postcodes'),'place','postcode',postcode,'us',"; - $sSQL .= "ST_SetSRID(ST_Point(x,y),4326) as geometry from us_postcode"; - if (!pg_query($oDB->connection, $sSQL)) fail(pg_last_error($oDB->connection)); - } -} - -if ($aCMDResult['osmosis-init'] || ($aCMDResult['all'] && !$aCMDResult['drop'])) { // no use doing osmosis-init when dropping update tables - $bDidSomething = true; - $oDB =& getDB(); - - if (!file_exists(CONST_Osmosis_Binary)) { - echo "Please download osmosis.\nIf it is already installed, check the path in your local settings (settings/local.php) file.\n"; - if (!$aCMDResult['all']) { - fail("osmosis not found in '".CONST_Osmosis_Binary."'"); - } - } else { - if (file_exists(CONST_InstallPath.'/settings/configuration.txt')) { - echo "settings/configuration.txt already exists\n"; - } else { - passthru(CONST_Osmosis_Binary.' --read-replication-interval-init '.CONST_InstallPath.'/settings'); - // update osmosis configuration.txt with our settings - passthru("sed -i 's!baseUrl=.*!baseUrl=".CONST_Replication_Url."!' ".CONST_InstallPath.'/settings/configuration.txt'); - passthru("sed -i 's:maxInterval = .*:maxInterval = ".CONST_Replication_MaxInterval.":' ".CONST_InstallPath.'/settings/configuration.txt'); - } - - // Find the last node in the DB - $iLastOSMID = $oDB->getOne("select max(osm_id) from place where osm_type = 'N'"); - - // Lookup the timestamp that node was created (less 3 hours for margin for changsets to be closed) - $sLastNodeURL = 'http://www.openstreetmap.org/api/0.6/node/'.$iLastOSMID."/1"; - $sLastNodeXML = file_get_contents($sLastNodeURL); - preg_match('#timestamp="(([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})Z)"#', $sLastNodeXML, $aLastNodeDate); - $iLastNodeTimestamp = strtotime($aLastNodeDate[1]) - (3*60*60); - - // Search for the correct state file - uses file timestamps so need to sort by date descending - $sRepURL = CONST_Replication_Url."/"; - $sRep = file_get_contents($sRepURL."?C=M;O=D;F=1"); - // download.geofabrik.de: 000/