4 require_once(dirname(dirname(__FILE__)).'/settings/settings.php');
 
   5 require_once(CONST_BasePath.'/lib/init-cmd.php');
 
   6 ini_set('memory_limit', '800M');
 
   8 # (long-opt, short-opt, min-occurs, max-occurs, num-arguments, num-arguments, type, help)
 
  12    'Create and setup nominatim search system',
 
  13    array('help', 'h', 0, 1, 0, 0, false, 'Show Help'),
 
  14    array('quiet', 'q', 0, 1, 0, 0, 'bool', 'Quiet output'),
 
  15    array('verbose', 'v', 0, 1, 0, 0, 'bool', 'Verbose output'),
 
  17    array('osm-file', '', 0, 1, 1, 1, 'realpath', 'File to import'),
 
  18    array('threads', '', 0, 1, 1, 1, 'int', 'Number of threads (where possible)'),
 
  19    array('module-path', '', 0, 1, 1, 1, 'string', 'Directory on Postgres server containing Nominatim module'),
 
  21    array('all', '', 0, 1, 0, 0, 'bool', 'Do the complete process'),
 
  23    array('create-db', '', 0, 1, 0, 0, 'bool', 'Create nominatim db'),
 
  24    array('setup-db', '', 0, 1, 0, 0, 'bool', 'Build a blank nominatim db'),
 
  25    array('import-data', '', 0, 1, 0, 0, 'bool', 'Import a osm file'),
 
  26    array('osm2pgsql-cache', '', 0, 1, 1, 1, 'int', 'Cache size used by osm2pgsql'),
 
  27    array('create-functions', '', 0, 1, 0, 0, 'bool', 'Create functions'),
 
  28    array('enable-diff-updates', '', 0, 1, 0, 0, 'bool', 'Turn on the code required to make diff updates work'),
 
  29    array('enable-debug-statements', '', 0, 1, 0, 0, 'bool', 'Include debug warning statements in pgsql commands'),
 
  30    array('ignore-errors', '', 0, 1, 0, 0, 'bool', 'Continue import even when errors in SQL are present (EXPERT)'),
 
  31    array('create-tables', '', 0, 1, 0, 0, 'bool', 'Create main tables'),
 
  32    array('create-partition-tables', '', 0, 1, 0, 0, 'bool', 'Create required partition tables'),
 
  33    array('create-partition-functions', '', 0, 1, 0, 0, 'bool', 'Create required partition triggers'),
 
  34    array('no-partitions', '', 0, 1, 0, 0, 'bool', 'Do not partition search indices (speeds up import of single country extracts)'),
 
  35    array('import-wikipedia-articles', '', 0, 1, 0, 0, 'bool', 'Import wikipedia article dump'),
 
  36    array('load-data', '', 0, 1, 0, 0, 'bool', 'Copy data to live tables from import table'),
 
  37    array('disable-token-precalc', '', 0, 1, 0, 0, 'bool', 'Disable name precalculation (EXPERT)'),
 
  38    array('import-tiger-data', '', 0, 1, 0, 0, 'bool', 'Import tiger data (not included in \'all\')'),
 
  39    array('calculate-postcodes', '', 0, 1, 0, 0, 'bool', 'Calculate postcode centroids'),
 
  40    array('osmosis-init', '', 0, 1, 0, 0, 'bool', 'Generate default osmosis configuration'),
 
  41    array('index', '', 0, 1, 0, 0, 'bool', 'Index the data'),
 
  42    array('index-noanalyse', '', 0, 1, 0, 0, 'bool', 'Do not perform analyse operations during index (EXPERT)'),
 
  43    array('create-search-indices', '', 0, 1, 0, 0, 'bool', 'Create additional indices required for search and update'),
 
  44    array('create-country-names', '', 0, 1, 0, 0, 'bool', 'Create default list of searchable country names'),
 
  45    array('drop', '', 0, 1, 0, 0, 'bool', 'Drop tables needed for updates, making the database readonly (EXPERIMENTAL)'),
 
  47 getCmdOpt($_SERVER['argv'], $aCMDOptions, $aCMDResult, true, true);
 
  49 $bDidSomething = false;
 
  51 // Check if osm-file is set and points to a valid file if --all or --import-data is given
 
  52 if ($aCMDResult['import-data'] || $aCMDResult['all']) {
 
  53     if (!isset($aCMDResult['osm-file'])) {
 
  54         fail('missing --osm-file for data import');
 
  57     if (!file_exists($aCMDResult['osm-file'])) {
 
  58         fail('the path supplied to --osm-file does not exist');
 
  61     if (!is_readable($aCMDResult['osm-file'])) {
 
  62         fail('osm-file "'.$aCMDResult['osm-file'].'" not readable');
 
  66 // by default, use all but one processor, but never more than 15.
 
  67 $iInstances = isset($aCMDResult['threads'])
 
  68               ? $aCMDResult['threads']
 
  69               : (min(16, getProcessorCount()) - 1);
 
  71 if ($iInstances < 1) {
 
  73     warn("resetting threads to $iInstances");
 
  76 // Assume we can steal all the cache memory in the box (unless told otherwise)
 
  77 if (isset($aCMDResult['osm2pgsql-cache'])) {
 
  78     $iCacheMemory = $aCMDResult['osm2pgsql-cache'];
 
  80     $iCacheMemory = getCacheMemoryMB();
 
  83 $sModulePath = CONST_InstallPath . '/module';
 
  84 if (isset($aCMDResult['module-path'])) {
 
  85     $sModulePath = $aCMDResult['module-path'];
 
  86     echo 'module path: ' . $sModulePath . '\n';
 
  89 $aDSNInfo = DB::parseDSN(CONST_Database_DSN);
 
  90 if (!isset($aDSNInfo['port']) || !$aDSNInfo['port']) $aDSNInfo['port'] = 5432;
 
  92 if ($aCMDResult['create-db'] || $aCMDResult['all']) {
 
  94     $bDidSomething = true;
 
  95     $oDB = DB::connect(CONST_Database_DSN, false);
 
  96     if (!PEAR::isError($oDB)) {
 
  97         fail('database already exists ('.CONST_Database_DSN.')');
 
 100     $sCreateDBCmd = 'createdb -E UTF-8 -p '.$aDSNInfo['port'].' '.$aDSNInfo['database'];
 
 101     if (isset($aDSNInfo['username']) && $aDSNInfo['username']) {
 
 102         $sCreateDBCmd .= ' -U ' . $aDSNInfo['username'];
 
 104     if (isset($aDSNInfo['hostspec']) && $aDSNInfo['hostspec']) {
 
 105         $sCreateDBCmd .= ' -h ' . $aDSNInfo['hostspec'];
 
 109     if (isset($aDSNInfo['password']) && $aDSNInfo['password']) {
 
 110         $aProcEnv = array_merge(array('PGPASSWORD' => $aDSNInfo['password']), $_ENV);
 
 113     $result = runWithEnv($sCreateDBCmd, $aProcEnv);
 
 114     if ($result != 0) fail('Error executing external command: '.$sCreateDBCmd);
 
 117 if ($aCMDResult['setup-db'] || $aCMDResult['all']) {
 
 119     $bDidSomething = true;
 
 123     $fPostgresVersion = getPostgresVersion($oDB);
 
 124     echo 'Postgres version found: '.$fPostgresVersion."\n";
 
 126     if ($fPostgresVersion < 9.1) {
 
 127         fail('Minimum supported version of Postgresql is 9.1.');
 
 130     pgsqlRunScript('CREATE EXTENSION IF NOT EXISTS hstore');
 
 131     pgsqlRunScript('CREATE EXTENSION IF NOT EXISTS postgis');
 
 133     // For extratags and namedetails the hstore_to_json converter is
 
 134     // needed which is only available from Postgresql 9.3+. For older
 
 135     // versions add a dummy function that returns nothing.
 
 136     $iNumFunc = chksql($oDB->getOne("select count(*) from pg_proc where proname = 'hstore_to_json'"));
 
 138     if ($iNumFunc == 0) {
 
 139         pgsqlRunScript("create function hstore_to_json(dummy hstore) returns text AS 'select null::text' language sql immutable");
 
 140         warn('Postgresql is too old. extratags and namedetails API not available.');
 
 143     $fPostgisVersion = getPostgisVersion($oDB);
 
 144     echo 'Postgis version found: '.$fPostgisVersion."\n";
 
 146     if ($fPostgisVersion < 2.1) {
 
 147         // Functions were renamed in 2.1 and throw an annoying deprecation warning
 
 148         pgsqlRunScript('ALTER FUNCTION st_line_interpolate_point(geometry, double precision) RENAME TO ST_LineInterpolatePoint');
 
 149         pgsqlRunScript('ALTER FUNCTION ST_Line_Locate_Point(geometry, geometry) RENAME TO ST_LineLocatePoint');
 
 151     if ($fPostgisVersion < 2.2) {
 
 152         pgsqlRunScript('ALTER FUNCTION ST_Distance_Spheroid(geometry, geometry, spheroid) RENAME TO ST_DistanceSpheroid');
 
 155     $i = chksql($oDB->getOne("select count(*) from pg_user where usename = '".CONST_Database_Web_User."'"));
 
 157         echo "\nERROR: Web user '".CONST_Database_Web_User."' does not exist. Create it with:\n";
 
 158         echo "\n          createuser ".CONST_Database_Web_User."\n\n";
 
 162     // Try accessing the C module, so we know early if something is wrong
 
 163     // and can simply error out.
 
 164     $sSQL = "CREATE FUNCTION nominatim_test_import_func(text) RETURNS text AS '";
 
 165     $sSQL .= $sModulePath."/nominatim.so', 'transliteration' LANGUAGE c IMMUTABLE STRICT";
 
 166     $sSQL .= ';DROP FUNCTION nominatim_test_import_func(text);';
 
 167     $oResult = $oDB->query($sSQL);
 
 169     if (PEAR::isError($oResult)) {
 
 170         echo "\nERROR: Failed to load nominatim module. Reason:\n";
 
 171         echo $oResult->userinfo."\n\n";
 
 175     if (!file_exists(CONST_ExtraDataPath.'/country_osm_grid.sql.gz')) {
 
 176         echo 'Error: you need to download the country_osm_grid first:';
 
 177         echo "\n    wget -O ".CONST_ExtraDataPath."/country_osm_grid.sql.gz https://www.nominatim.org/data/country_grid.sql.gz\n";
 
 181     pgsqlRunScriptFile(CONST_BasePath.'/data/country_name.sql');
 
 182     pgsqlRunScriptFile(CONST_BasePath.'/data/country_naturalearthdata.sql');
 
 183     pgsqlRunScriptFile(CONST_BasePath.'/data/country_osm_grid.sql.gz');
 
 184     pgsqlRunScriptFile(CONST_BasePath.'/data/gb_postcode_table.sql');
 
 185     if (file_exists(CONST_BasePath.'/data/gb_postcode_data.sql.gz')) {
 
 186         pgsqlRunScriptFile(CONST_BasePath.'/data/gb_postcode_data.sql.gz');
 
 188         warn('external UK postcode table not found.');
 
 190     if (CONST_Use_Extra_US_Postcodes) {
 
 191         pgsqlRunScriptFile(CONST_BasePath.'/data/us_postcode.sql');
 
 194     if ($aCMDResult['no-partitions']) {
 
 195         pgsqlRunScript('update country_name set partition = 0');
 
 198     // the following will be needed by create_functions later but
 
 199     // is only defined in the subsequently called create_tables.
 
 200     // Create dummies here that will be overwritten by the proper
 
 201     // versions in create-tables.
 
 202     pgsqlRunScript('CREATE TABLE IF NOT EXISTS place_boundingbox ()');
 
 203     pgsqlRunScript('CREATE TYPE wikipedia_article_match AS ()', false);
 
 206 if ($aCMDResult['import-data'] || $aCMDResult['all']) {
 
 208     $bDidSomething = true;
 
 210     $osm2pgsql = CONST_Osm2pgsql_Binary;
 
 211     if (!file_exists($osm2pgsql)) {
 
 212         echo "Check CONST_Osm2pgsql_Binary in your local settings file.\n";
 
 213         echo "Normally you should not need to set this manually.\n";
 
 214         fail("osm2pgsql not found in '$osm2pgsql'");
 
 217     if (!is_null(CONST_Osm2pgsql_Flatnode_File) && CONST_Osm2pgsql_Flatnode_File) {
 
 218         $osm2pgsql .= ' --flat-nodes '.CONST_Osm2pgsql_Flatnode_File;
 
 220     if (CONST_Tablespace_Osm2pgsql_Data)
 
 221         $osm2pgsql .= ' --tablespace-slim-data '.CONST_Tablespace_Osm2pgsql_Data;
 
 222     if (CONST_Tablespace_Osm2pgsql_Index)
 
 223         $osm2pgsql .= ' --tablespace-slim-index '.CONST_Tablespace_Osm2pgsql_Index;
 
 224     if (CONST_Tablespace_Place_Data)
 
 225         $osm2pgsql .= ' --tablespace-main-data '.CONST_Tablespace_Place_Data;
 
 226     if (CONST_Tablespace_Place_Index)
 
 227         $osm2pgsql .= ' --tablespace-main-index '.CONST_Tablespace_Place_Index;
 
 228     $osm2pgsql .= ' -lsc -O gazetteer --hstore --number-processes 1';
 
 229     $osm2pgsql .= ' -C '.$iCacheMemory;
 
 230     $osm2pgsql .= ' -P '.$aDSNInfo['port'];
 
 231     if (isset($aDSNInfo['username']) && $aDSNInfo['username']) {
 
 232         $osm2pgsql .= ' -U ' . $aDSNInfo['username'];
 
 234     if (isset($aDSNInfo['hostspec']) && $aDSNInfo['hostspec']) {
 
 235         $osm2pgsql .= ' -H ' . $aDSNInfo['hostspec'];
 
 239     if (isset($aDSNInfo['password']) && $aDSNInfo['password']) {
 
 240         $aProcEnv = array_merge(array('PGPASSWORD' => $aDSNInfo['password']), $_ENV);
 
 243     $osm2pgsql .= ' -d '.$aDSNInfo['database'].' '.$aCMDResult['osm-file'];
 
 244     runWithEnv($osm2pgsql, $aProcEnv);
 
 247     if (!$aCMDResult['ignore-errors'] && !chksql($oDB->getRow('select * from place limit 1'))) {
 
 252 if ($aCMDResult['create-functions'] || $aCMDResult['all']) {
 
 253     info('Create Functions');
 
 254     $bDidSomething = true;
 
 255     create_sql_functions($aCMDResult);
 
 258 if ($aCMDResult['create-tables'] || $aCMDResult['all']) {
 
 259     info('Create Tables');
 
 260     $bDidSomething = true;
 
 262     $sTemplate = file_get_contents(CONST_BasePath.'/sql/tables.sql');
 
 263     $sTemplate = str_replace('{www-user}', CONST_Database_Web_User, $sTemplate);
 
 264     $sTemplate = replace_tablespace(
 
 266         CONST_Tablespace_Address_Data,
 
 269     $sTemplate = replace_tablespace(
 
 270         '{ts:address-index}',
 
 271         CONST_Tablespace_Address_Index,
 
 274     $sTemplate = replace_tablespace(
 
 276         CONST_Tablespace_Search_Data,
 
 279     $sTemplate = replace_tablespace(
 
 281         CONST_Tablespace_Search_Index,
 
 284     $sTemplate = replace_tablespace(
 
 286         CONST_Tablespace_Aux_Data,
 
 289     $sTemplate = replace_tablespace(
 
 291         CONST_Tablespace_Aux_Index,
 
 294     pgsqlRunScript($sTemplate, false);
 
 296     // re-run the functions
 
 297     info('Recreate Functions');
 
 298     create_sql_functions($aCMDResult);
 
 301 if ($aCMDResult['create-partition-tables'] || $aCMDResult['all']) {
 
 302     info('Create Partition Tables');
 
 303     $bDidSomething = true;
 
 305     $sTemplate = file_get_contents(CONST_BasePath.'/sql/partition-tables.src.sql');
 
 306     $sTemplate = replace_tablespace(
 
 308         CONST_Tablespace_Address_Data,
 
 311     $sTemplate = replace_tablespace(
 
 312         '{ts:address-index}',
 
 313         CONST_Tablespace_Address_Index,
 
 316     $sTemplate = replace_tablespace(
 
 318         CONST_Tablespace_Search_Data,
 
 321     $sTemplate = replace_tablespace(
 
 323         CONST_Tablespace_Search_Index,
 
 326     $sTemplate = replace_tablespace(
 
 328         CONST_Tablespace_Aux_Data,
 
 331     $sTemplate = replace_tablespace(
 
 333         CONST_Tablespace_Aux_Index,
 
 337     pgsqlRunPartitionScript($sTemplate);
 
 341 if ($aCMDResult['create-partition-functions'] || $aCMDResult['all']) {
 
 342     info('Create Partition Functions');
 
 343     $bDidSomething = true;
 
 345     $sTemplate = file_get_contents(CONST_BasePath.'/sql/partition-functions.src.sql');
 
 347     pgsqlRunPartitionScript($sTemplate);
 
 350 if ($aCMDResult['import-wikipedia-articles'] || $aCMDResult['all']) {
 
 351     $bDidSomething = true;
 
 352     $sWikiArticlesFile = CONST_Wikipedia_Data_Path.'/wikipedia_article.sql.bin';
 
 353     $sWikiRedirectsFile = CONST_Wikipedia_Data_Path.'/wikipedia_redirect.sql.bin';
 
 354     if (file_exists($sWikiArticlesFile)) {
 
 355         info('Importing wikipedia articles');
 
 356         pgsqlRunDropAndRestore($sWikiArticlesFile);
 
 358         warn('wikipedia article dump file not found - places will have default importance');
 
 360     if (file_exists($sWikiRedirectsFile)) {
 
 361         info('Importing wikipedia redirects');
 
 362         pgsqlRunDropAndRestore($sWikiRedirectsFile);
 
 364         warn('wikipedia redirect dump file not found - some place importance values may be missing');
 
 369 if ($aCMDResult['load-data'] || $aCMDResult['all']) {
 
 370     info('Drop old Data');
 
 371     $bDidSomething = true;
 
 374     if (!pg_query($oDB->connection, 'TRUNCATE word')) fail(pg_last_error($oDB->connection));
 
 376     if (!pg_query($oDB->connection, 'TRUNCATE placex')) fail(pg_last_error($oDB->connection));
 
 378     if (!pg_query($oDB->connection, 'TRUNCATE location_property_osmline')) fail(pg_last_error($oDB->connection));
 
 380     if (!pg_query($oDB->connection, 'TRUNCATE place_addressline')) fail(pg_last_error($oDB->connection));
 
 382     if (!pg_query($oDB->connection, 'TRUNCATE place_boundingbox')) fail(pg_last_error($oDB->connection));
 
 384     if (!pg_query($oDB->connection, 'TRUNCATE location_area')) fail(pg_last_error($oDB->connection));
 
 386     if (!pg_query($oDB->connection, 'TRUNCATE search_name')) fail(pg_last_error($oDB->connection));
 
 388     if (!pg_query($oDB->connection, 'TRUNCATE search_name_blank')) fail(pg_last_error($oDB->connection));
 
 390     if (!pg_query($oDB->connection, 'DROP SEQUENCE seq_place')) fail(pg_last_error($oDB->connection));
 
 392     if (!pg_query($oDB->connection, 'CREATE SEQUENCE seq_place start 100000')) fail(pg_last_error($oDB->connection));
 
 395     $sSQL = 'select distinct partition from country_name';
 
 396     $aPartitions = chksql($oDB->getCol($sSQL));
 
 397     if (!$aCMDResult['no-partitions']) $aPartitions[] = 0;
 
 398     foreach ($aPartitions as $sPartition) {
 
 399         if (!pg_query($oDB->connection, 'TRUNCATE location_road_'.$sPartition)) fail(pg_last_error($oDB->connection));
 
 403     // used by getorcreate_word_id to ignore frequent partial words
 
 404     $sSQL = 'CREATE OR REPLACE FUNCTION get_maxwordfreq() RETURNS integer AS ';
 
 405     $sSQL .= '$$ SELECT '.CONST_Max_Word_Frequency.' as maxwordfreq; $$ LANGUAGE SQL IMMUTABLE';
 
 406     if (!pg_query($oDB->connection, $sSQL)) {
 
 407         fail(pg_last_error($oDB->connection));
 
 411     // pre-create the word list
 
 412     if (!$aCMDResult['disable-token-precalc']) {
 
 413         info('Loading word list');
 
 414         pgsqlRunScriptFile(CONST_BasePath.'/data/words.sql');
 
 418     $sColumns = 'osm_type, osm_id, class, type, name, admin_level, address, extratags, geometry';
 
 420     $aDBInstances = array();
 
 421     $iLoadThreads = max(1, $iInstances - 1);
 
 422     for ($i = 0; $i < $iLoadThreads; $i++) {
 
 423         $aDBInstances[$i] =& getDB(true);
 
 424         $sSQL = "INSERT INTO placex ($sColumns) SELECT $sColumns FROM place WHERE osm_id % $iLoadThreads = $i";
 
 425         $sSQL .= " and not (class='place' and type='houses' and osm_type='W'";
 
 426         $sSQL .= "          and ST_GeometryType(geometry) = 'ST_LineString')";
 
 427         $sSQL .= ' and ST_IsValid(geometry)';
 
 428         if ($aCMDResult['verbose']) echo "$sSQL\n";
 
 429         if (!pg_send_query($aDBInstances[$i]->connection, $sSQL)) {
 
 430             fail(pg_last_error($aDBInstances[$i]->connection));
 
 433     // last thread for interpolation lines
 
 434     $aDBInstances[$iLoadThreads] =& getDB(true);
 
 435     $sSQL = 'insert into location_property_osmline';
 
 436     $sSQL .= ' (osm_id, address, linegeo)';
 
 437     $sSQL .= ' SELECT osm_id, address, geometry from place where ';
 
 438     $sSQL .= "class='place' and type='houses' and osm_type='W' and ST_GeometryType(geometry) = 'ST_LineString'";
 
 439     if ($aCMDResult['verbose']) echo "$sSQL\n";
 
 440     if (!pg_send_query($aDBInstances[$iLoadThreads]->connection, $sSQL)) {
 
 441         fail(pg_last_error($aDBInstances[$iLoadThreads]->connection));
 
 445     for ($i = 0; $i <= $iLoadThreads; $i++) {
 
 446         while (($hPGresult = pg_get_result($aDBInstances[$i]->connection)) !== false) {
 
 447             $resultStatus = pg_result_status($hPGresult);
 
 448             // PGSQL_EMPTY_QUERY, PGSQL_COMMAND_OK, PGSQL_TUPLES_OK,
 
 449             // PGSQL_COPY_OUT, PGSQL_COPY_IN, PGSQL_BAD_RESPONSE,
 
 450             // PGSQL_NONFATAL_ERROR and PGSQL_FATAL_ERROR
 
 451             echo 'Query result ' . $i . ' is: ' . $resultStatus . "\n";
 
 452             if ($resultStatus != PGSQL_COMMAND_OK && $resultStatus != PGSQL_TUPLES_OK) {
 
 453                 $resultError = pg_result_error($hPGresult);
 
 454                 echo '-- error text ' . $i . ': ' . $resultError . "\n";
 
 460         fail('SQL errors loading placex and/or location_property_osmline tables');
 
 463     info('Reanalysing database');
 
 464     pgsqlRunScript('ANALYSE');
 
 466     $sDatabaseDate = getDatabaseDate($oDB);
 
 467     pg_query($oDB->connection, 'TRUNCATE import_status');
 
 468     if ($sDatabaseDate === false) {
 
 469         warn('could not determine database date.');
 
 471         $sSQL = "INSERT INTO import_status (lastimportdate) VALUES('".$sDatabaseDate."')";
 
 472         pg_query($oDB->connection, $sSQL);
 
 473         echo "Latest data imported from $sDatabaseDate.\n";
 
 477 if ($aCMDResult['import-tiger-data']) {
 
 478     info('Import Tiger data');
 
 479     $bDidSomething = true;
 
 481     $sTemplate = file_get_contents(CONST_BasePath.'/sql/tiger_import_start.sql');
 
 482     $sTemplate = str_replace('{www-user}', CONST_Database_Web_User, $sTemplate);
 
 483     $sTemplate = replace_tablespace(
 
 485         CONST_Tablespace_Aux_Data,
 
 488     $sTemplate = replace_tablespace(
 
 490         CONST_Tablespace_Aux_Index,
 
 493     pgsqlRunScript($sTemplate, false);
 
 495     $aDBInstances = array();
 
 496     for ($i = 0; $i < $iInstances; $i++) {
 
 497         $aDBInstances[$i] =& getDB(true);
 
 500     foreach (glob(CONST_Tiger_Data_Path.'/*.sql') as $sFile) {
 
 502         $hFile = fopen($sFile, 'r');
 
 503         $sSQL = fgets($hFile, 100000);
 
 507             for ($i = 0; $i < $iInstances; $i++) {
 
 508                 if (!pg_connection_busy($aDBInstances[$i]->connection)) {
 
 509                     while (pg_get_result($aDBInstances[$i]->connection));
 
 510                     $sSQL = fgets($hFile, 100000);
 
 512                     if (!pg_send_query($aDBInstances[$i]->connection, $sSQL)) fail(pg_last_error($oDB->connection));
 
 514                     if ($iLines == 1000) {
 
 528             for ($i = 0; $i < $iInstances; $i++) {
 
 529                 if (pg_connection_busy($aDBInstances[$i]->connection)) $bAnyBusy = true;
 
 536     info('Creating indexes on Tiger data');
 
 537     $sTemplate = file_get_contents(CONST_BasePath.'/sql/tiger_import_finish.sql');
 
 538     $sTemplate = str_replace('{www-user}', CONST_Database_Web_User, $sTemplate);
 
 539     $sTemplate = replace_tablespace(
 
 541         CONST_Tablespace_Aux_Data,
 
 544     $sTemplate = replace_tablespace(
 
 546         CONST_Tablespace_Aux_Index,
 
 549     pgsqlRunScript($sTemplate, false);
 
 552 if ($aCMDResult['calculate-postcodes'] || $aCMDResult['all']) {
 
 553     info('Calculate Postcodes');
 
 554     $bDidSomething = true;
 
 556     if (!pg_query($oDB->connection, 'TRUNCATE location_postcode')) {
 
 557         fail(pg_last_error($oDB->connection));
 
 560     $sSQL  = 'INSERT INTO location_postcode';
 
 561     $sSQL .= ' (place_id, indexed_status, country_code, postcode, geometry) ';
 
 562     $sSQL .= "SELECT nextval('seq_place'), 1, country_code,";
 
 563     $sSQL .= "       upper(trim (both ' ' from address->'postcode')) as pc,";
 
 564     $sSQL .= '       ST_Centroid(ST_Collect(ST_Centroid(geometry)))';
 
 565     $sSQL .= '  FROM placex';
 
 566     $sSQL .= " WHERE address ? 'postcode' AND address->'postcode' NOT SIMILAR TO '%(,|;)%'";
 
 567     $sSQL .= '       AND geometry IS NOT null';
 
 568     $sSQL .= ' GROUP BY country_code, pc';
 
 570     if (!pg_query($oDB->connection, $sSQL)) {
 
 571         fail(pg_last_error($oDB->connection));
 
 574     if (CONST_Use_Extra_US_Postcodes) {
 
 575         // only add postcodes that are not yet available in OSM
 
 576         $sSQL  = 'INSERT INTO location_postcode';
 
 577         $sSQL .= ' (place_id, indexed_status, country_code, postcode, geometry) ';
 
 578         $sSQL .= "SELECT nextval('seq_place'), 1, 'us', postcode,";
 
 579         $sSQL .= '       ST_SetSRID(ST_Point(x,y),4326)';
 
 580         $sSQL .= '  FROM us_postcode WHERE postcode NOT IN';
 
 581         $sSQL .= '        (SELECT postcode FROM location_postcode';
 
 582         $sSQL .= "          WHERE country_code = 'us')";
 
 583         if (!pg_query($oDB->connection, $sSQL)) fail(pg_last_error($oDB->connection));
 
 586     // add missing postcodes for GB (if available)
 
 587     $sSQL  = 'INSERT INTO location_postcode';
 
 588     $sSQL .= ' (place_id, indexed_status, country_code, postcode, geometry) ';
 
 589     $sSQL .= "SELECT nextval('seq_place'), 1, 'gb', postcode, geometry";
 
 590     $sSQL .= '  FROM gb_postcode WHERE postcode NOT IN';
 
 591     $sSQL .= '           (SELECT postcode FROM location_postcode';
 
 592     $sSQL .= "             WHERE country_code = 'gb')";
 
 593     if (!pg_query($oDB->connection, $sSQL)) fail(pg_last_error($oDB->connection));
 
 595     if (!$aCMDResult['all']) {
 
 596         $sSQL = "DELETE FROM word WHERE class='place' and type='postcode'";
 
 597         $sSQL .= 'and word NOT IN (SELECT postcode FROM location_postcode)';
 
 598         if (!pg_query($oDB->connection, $sSQL)) {
 
 599             fail(pg_last_error($oDB->connection));
 
 602     $sSQL = 'SELECT count(getorcreate_postcode_id(v)) FROM ';
 
 603     $sSQL .= '(SELECT distinct(postcode) as v FROM location_postcode) p';
 
 605     if (!pg_query($oDB->connection, $sSQL)) {
 
 606         fail(pg_last_error($oDB->connection));
 
 610 if ($aCMDResult['osmosis-init']) {
 
 611     $bDidSomething = true;
 
 612     echo "Command 'osmosis-init' no longer available, please use utils/update.php --init-updates.\n";
 
 615 if ($aCMDResult['index'] || $aCMDResult['all']) {
 
 616     $bDidSomething = true;
 
 618     $sBaseCmd = CONST_InstallPath.'/nominatim/nominatim -i -d '.$aDSNInfo['database'].' -P '.$aDSNInfo['port'].' -t '.$iInstances.$sOutputFile;
 
 619     if (isset($aDSNInfo['hostspec']) && $aDSNInfo['hostspec']) {
 
 620         $sBaseCmd .= ' -H ' . $aDSNInfo['hostspec'];
 
 622     if (isset($aDSNInfo['username']) && $aDSNInfo['username']) {
 
 623         $sBaseCmd .= ' -U ' . $aDSNInfo['username'];
 
 626     if (isset($aDSNInfo['password']) && $aDSNInfo['password']) {
 
 627         $aProcEnv = array_merge(array('PGPASSWORD' => $aDSNInfo['password']), $_ENV);
 
 630     info('Index ranks 0 - 4');
 
 631     $iStatus = runWithEnv($sBaseCmd.' -R 4', $aProcEnv);
 
 633         fail('error status ' . $iStatus . ' running nominatim!');
 
 635     if (!$aCMDResult['index-noanalyse']) pgsqlRunScript('ANALYSE');
 
 636     info('Index ranks 5 - 25');
 
 637     $iStatus = runWithEnv($sBaseCmd.' -r 5 -R 25', $aProcEnv);
 
 639         fail('error status ' . $iStatus . ' running nominatim!');
 
 641     if (!$aCMDResult['index-noanalyse']) pgsqlRunScript('ANALYSE');
 
 642     info('Index ranks 26 - 30');
 
 643     $iStatus = runWithEnv($sBaseCmd.' -r 26', $aProcEnv);
 
 645         fail('error status ' . $iStatus . ' running nominatim!');
 
 648     info('Index postcodes');
 
 650     $sSQL = 'UPDATE location_postcode SET indexed_status = 0';
 
 651     if (!pg_query($oDB->connection, $sSQL)) fail(pg_last_error($oDB->connection));
 
 654 if ($aCMDResult['create-search-indices'] || $aCMDResult['all']) {
 
 655     info('Create Search indices');
 
 656     $bDidSomething = true;
 
 658     $sTemplate = file_get_contents(CONST_BasePath.'/sql/indices.src.sql');
 
 659     $sTemplate = str_replace('{www-user}', CONST_Database_Web_User, $sTemplate);
 
 660     $sTemplate = replace_tablespace(
 
 661         '{ts:address-index}',
 
 662         CONST_Tablespace_Address_Index,
 
 665     $sTemplate = replace_tablespace(
 
 667         CONST_Tablespace_Search_Index,
 
 670     $sTemplate = replace_tablespace(
 
 672         CONST_Tablespace_Aux_Index,
 
 676     pgsqlRunScript($sTemplate);
 
 679 if ($aCMDResult['create-country-names'] || $aCMDResult['all']) {
 
 680     info('Create search index for default country names');
 
 681     $bDidSomething = true;
 
 683     pgsqlRunScript("select getorcreate_country(make_standard_name('uk'), 'gb')");
 
 684     pgsqlRunScript("select getorcreate_country(make_standard_name('united states'), 'us')");
 
 685     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');
 
 686     pgsqlRunScript("select count(*) from (select getorcreate_country(make_standard_name(name->'name'), country_code) from country_name where name ? 'name') as x");
 
 688     $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 ';
 
 689     if (CONST_Languages) {
 
 692         foreach (explode(',', CONST_Languages) as $sLang) {
 
 693             $sSQL .= $sDelim."'name:$sLang'";
 
 698         // all include all simple name tags
 
 699         $sSQL .= "like 'name:%'";
 
 702     pgsqlRunScript($sSQL);
 
 705 if ($aCMDResult['drop']) {
 
 706     info('Drop tables only required for updates');
 
 707     // The implementation is potentially a bit dangerous because it uses
 
 708     // a positive selection of tables to keep, and deletes everything else.
 
 709     // Including any tables that the unsuspecting user might have manually
 
 710     // created. USE AT YOUR OWN PERIL.
 
 711     $bDidSomething = true;
 
 713     // tables we want to keep. everything else goes.
 
 714     $aKeepTables = array(
 
 720                     'location_property*',
 
 733     $aDropTables = array();
 
 734     $aHaveTables = chksql($oDB->getCol("SELECT tablename FROM pg_tables WHERE schemaname='public'"));
 
 736     foreach ($aHaveTables as $sTable) {
 
 738         foreach ($aKeepTables as $sKeep) {
 
 739             if (fnmatch($sKeep, $sTable)) {
 
 744         if (!$bFound) array_push($aDropTables, $sTable);
 
 747     foreach ($aDropTables as $sDrop) {
 
 748         if ($aCMDResult['verbose']) echo "dropping table $sDrop\n";
 
 749         @pg_query($oDB->connection, "DROP TABLE $sDrop CASCADE");
 
 750         // ignore warnings/errors as they might be caused by a table having
 
 751         // been deleted already by CASCADE
 
 754     if (!is_null(CONST_Osm2pgsql_Flatnode_File) && CONST_Osm2pgsql_Flatnode_File) {
 
 755         if ($aCMDResult['verbose']) echo 'deleting '.CONST_Osm2pgsql_Flatnode_File."\n";
 
 756         unlink(CONST_Osm2pgsql_Flatnode_File);
 
 760 if (!$bDidSomething) {
 
 761     showUsage($aCMDOptions, true);
 
 763     echo "Summary of warnings:\n\n";
 
 766     info('Setup finished.');
 
 770 function pgsqlRunScriptFile($sFilename)
 
 773     if (!file_exists($sFilename)) fail('unable to find '.$sFilename);
 
 775     // Convert database DSN to psql parameters
 
 776     $aDSNInfo = DB::parseDSN(CONST_Database_DSN);
 
 777     if (!isset($aDSNInfo['port']) || !$aDSNInfo['port']) $aDSNInfo['port'] = 5432;
 
 778     $sCMD = 'psql -p '.$aDSNInfo['port'].' -d '.$aDSNInfo['database'];
 
 779     if (!$aCMDResult['verbose']) {
 
 782     if (isset($aDSNInfo['hostspec']) && $aDSNInfo['hostspec']) {
 
 783         $sCMD .= ' -h ' . $aDSNInfo['hostspec'];
 
 785     if (isset($aDSNInfo['username']) && $aDSNInfo['username']) {
 
 786         $sCMD .= ' -U ' . $aDSNInfo['username'];
 
 789     if (isset($aDSNInfo['password']) && $aDSNInfo['password']) {
 
 790         $aProcEnv = array_merge(array('PGPASSWORD' => $aDSNInfo['password']), $_ENV);
 
 794     if (preg_match('/\\.gz$/', $sFilename)) {
 
 795         $aDescriptors = array(
 
 796                          0 => array('pipe', 'r'),
 
 797                          1 => array('pipe', 'w'),
 
 798                          2 => array('file', '/dev/null', 'a')
 
 800         $hGzipProcess = proc_open('zcat '.$sFilename, $aDescriptors, $ahGzipPipes);
 
 801         if (!is_resource($hGzipProcess)) fail('unable to start zcat');
 
 802         $aReadPipe = $ahGzipPipes[1];
 
 803         fclose($ahGzipPipes[0]);
 
 805         $sCMD .= ' -f '.$sFilename;
 
 806         $aReadPipe = array('pipe', 'r');
 
 809     $aDescriptors = array(
 
 811                      1 => array('pipe', 'w'),
 
 812                      2 => array('file', '/dev/null', 'a')
 
 815     $hProcess = proc_open($sCMD, $aDescriptors, $ahPipes, null, $aProcEnv);
 
 816     if (!is_resource($hProcess)) fail('unable to start pgsql');
 
 818     // TODO: error checking
 
 819     while (!feof($ahPipes[1])) {
 
 820         echo fread($ahPipes[1], 4096);
 
 824     $iReturn = proc_close($hProcess);
 
 826         fail("pgsql returned with error code ($iReturn)");
 
 829         fclose($ahGzipPipes[1]);
 
 830         proc_close($hGzipProcess);
 
 834 function pgsqlRunScript($sScript, $bfatal = true)
 
 840         $aCMDResult['verbose'],
 
 841         $aCMDResult['ignore-errors']
 
 845 function pgsqlRunPartitionScript($sTemplate)
 
 850     $sSQL = 'select distinct partition from country_name';
 
 851     $aPartitions = chksql($oDB->getCol($sSQL));
 
 852     if (!$aCMDResult['no-partitions']) $aPartitions[] = 0;
 
 854     preg_match_all('#^-- start(.*?)^-- end#ms', $sTemplate, $aMatches, PREG_SET_ORDER);
 
 855     foreach ($aMatches as $aMatch) {
 
 857         foreach ($aPartitions as $sPartitionName) {
 
 858             $sResult .= str_replace('-partition-', $sPartitionName, $aMatch[1]);
 
 860         $sTemplate = str_replace($aMatch[0], $sResult, $sTemplate);
 
 863     pgsqlRunScript($sTemplate);
 
 866 function pgsqlRunRestoreData($sDumpFile)
 
 868     // Convert database DSN to psql parameters
 
 869     $aDSNInfo = DB::parseDSN(CONST_Database_DSN);
 
 870     if (!isset($aDSNInfo['port']) || !$aDSNInfo['port']) $aDSNInfo['port'] = 5432;
 
 871     $sCMD = 'pg_restore -p '.$aDSNInfo['port'].' -d '.$aDSNInfo['database'].' -Fc -a '.$sDumpFile;
 
 873     $aDescriptors = array(
 
 874                      0 => array('pipe', 'r'),
 
 875                      1 => array('pipe', 'w'),
 
 876                      2 => array('file', '/dev/null', 'a')
 
 879     $hProcess = proc_open($sCMD, $aDescriptors, $ahPipes);
 
 880     if (!is_resource($hProcess)) fail('unable to start pg_restore');
 
 884     // TODO: error checking
 
 885     while (!feof($ahPipes[1])) {
 
 886         echo fread($ahPipes[1], 4096);
 
 890     $iReturn = proc_close($hProcess);
 
 893 function pgsqlRunDropAndRestore($sDumpFile)
 
 895     // Convert database DSN to psql parameters
 
 896     $aDSNInfo = DB::parseDSN(CONST_Database_DSN);
 
 897     if (!isset($aDSNInfo['port']) || !$aDSNInfo['port']) $aDSNInfo['port'] = 5432;
 
 898     $sCMD = 'pg_restore -p '.$aDSNInfo['port'].' -d '.$aDSNInfo['database'].' -Fc --clean '.$sDumpFile;
 
 899     if (isset($aDSNInfo['hostspec']) && $aDSNInfo['hostspec']) {
 
 900         $sCMD .= ' -h ' . $aDSNInfo['hostspec'];
 
 902     if (isset($aDSNInfo['username']) && $aDSNInfo['username']) {
 
 903         $sCMD .= ' -U ' . $aDSNInfo['username'];
 
 906     if (isset($aDSNInfo['password']) && $aDSNInfo['password']) {
 
 907         $aProcEnv = array_merge(array('PGPASSWORD' => $aDSNInfo['password']), $_ENV);
 
 910     $iReturn = runWithEnv($sCMD, $aProcEnv);
 
 913 function passthruCheckReturn($sCmd)
 
 916     passthru($sCmd, $iResult);
 
 919 function replace_tablespace($sTemplate, $sTablespace, $sSql)
 
 922         $sSql = str_replace($sTemplate, 'TABLESPACE "'.$sTablespace.'"', $sSql);
 
 924         $sSql = str_replace($sTemplate, '', $sSql);
 
 930 function create_sql_functions($aCMDResult)
 
 933     $sTemplate = file_get_contents(CONST_BasePath.'/sql/functions.sql');
 
 934     $sTemplate = str_replace('{modulepath}', $sModulePath, $sTemplate);
 
 935     if ($aCMDResult['enable-diff-updates']) {
 
 936         $sTemplate = str_replace('RETURN NEW; -- %DIFFUPDATES%', '--', $sTemplate);
 
 938     if ($aCMDResult['enable-debug-statements']) {
 
 939         $sTemplate = str_replace('--DEBUG:', '', $sTemplate);
 
 941     if (CONST_Limit_Reindexing) {
 
 942         $sTemplate = str_replace('--LIMIT INDEXING:', '', $sTemplate);
 
 944     if (!CONST_Use_US_Tiger_Data) {
 
 945         $sTemplate = str_replace('-- %NOTIGERDATA% ', '', $sTemplate);
 
 947     if (!CONST_Use_Aux_Location_data) {
 
 948         $sTemplate = str_replace('-- %NOAUXDATA% ', '', $sTemplate);
 
 950     pgsqlRunScript($sTemplate);