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 // by default, use all but one processor, but never more than 15.
65 $iInstances = isset($aCMDResult['threads'])?$aCMDResult['threads']:(min(16,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 = str_replace('{www-user}', CONST_Database_Web_User, $sTemplate);
600 $sTemplate = replace_tablespace(
601 '{ts:address-index}',
602 CONST_Tablespace_Address_Index,
605 $sTemplate = replace_tablespace(
607 CONST_Tablespace_Search_Index,
610 $sTemplate = replace_tablespace(
612 CONST_Tablespace_Aux_Index,
616 pgsqlRunScript($sTemplate);
619 if ($aCMDResult['create-country-names'] || $aCMDResult['all']) {
620 info('Create search index for default country names');
621 $bDidSomething = true;
623 pgsqlRunScript("select getorcreate_country(make_standard_name('uk'), 'gb')");
624 pgsqlRunScript("select getorcreate_country(make_standard_name('united states'), 'us')");
625 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');
626 pgsqlRunScript("select count(*) from (select getorcreate_country(make_standard_name(name->'name'), country_code) from country_name where name ? 'name') as x");
628 $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 ';
629 if (CONST_Languages) {
632 foreach (explode(',', CONST_Languages) as $sLang) {
633 $sSQL .= $sDelim."'name:$sLang'";
638 // all include all simple name tags
639 $sSQL .= "like 'name:%'";
642 pgsqlRunScript($sSQL);
645 if ($aCMDResult['drop']) {
646 info('Drop tables only required for updates');
647 // The implementation is potentially a bit dangerous because it uses
648 // a positive selection of tables to keep, and deletes everything else.
649 // Including any tables that the unsuspecting user might have manually
650 // created. USE AT YOUR OWN PERIL.
651 $bDidSomething = true;
653 // tables we want to keep. everything else goes.
654 $aKeepTables = array(
660 'location_property*',
673 $aDropTables = array();
674 $aHaveTables = chksql($oDB->getCol("SELECT tablename FROM pg_tables WHERE schemaname='public'"));
676 foreach ($aHaveTables as $sTable) {
678 foreach ($aKeepTables as $sKeep) {
679 if (fnmatch($sKeep, $sTable)) {
684 if (!$bFound) array_push($aDropTables, $sTable);
687 foreach ($aDropTables as $sDrop) {
688 if ($aCMDResult['verbose']) echo "dropping table $sDrop\n";
689 @pg_query($oDB->connection, "DROP TABLE $sDrop CASCADE");
690 // ignore warnings/errors as they might be caused by a table having
691 // been deleted already by CASCADE
694 if (!is_null(CONST_Osm2pgsql_Flatnode_File) && CONST_Osm2pgsql_Flatnode_File) {
695 if ($aCMDResult['verbose']) echo 'deleting '.CONST_Osm2pgsql_Flatnode_File."\n";
696 unlink(CONST_Osm2pgsql_Flatnode_File);
700 if (!$bDidSomething) {
701 showUsage($aCMDOptions, true);
703 echo "Summary of warnings:\n\n";
706 info('Setup finished.');
710 function pgsqlRunScriptFile($sFilename)
713 if (!file_exists($sFilename)) fail('unable to find '.$sFilename);
715 // Convert database DSN to psql parameters
716 $aDSNInfo = DB::parseDSN(CONST_Database_DSN);
717 if (!isset($aDSNInfo['port']) || !$aDSNInfo['port']) $aDSNInfo['port'] = 5432;
718 $sCMD = 'psql -p '.$aDSNInfo['port'].' -d '.$aDSNInfo['database'];
719 if (!$aCMDResult['verbose']) {
724 if (preg_match('/\\.gz$/', $sFilename)) {
725 $aDescriptors = array(
726 0 => array('pipe', 'r'),
727 1 => array('pipe', 'w'),
728 2 => array('file', '/dev/null', 'a')
730 $hGzipProcess = proc_open('zcat '.$sFilename, $aDescriptors, $ahGzipPipes);
731 if (!is_resource($hGzipProcess)) fail('unable to start zcat');
732 $aReadPipe = $ahGzipPipes[1];
733 fclose($ahGzipPipes[0]);
735 $sCMD .= ' -f '.$sFilename;
736 $aReadPipe = array('pipe', 'r');
739 $aDescriptors = array(
741 1 => array('pipe', 'w'),
742 2 => array('file', '/dev/null', 'a')
745 $hProcess = proc_open($sCMD, $aDescriptors, $ahPipes);
746 if (!is_resource($hProcess)) fail('unable to start pgsql');
749 // TODO: error checking
750 while (!feof($ahPipes[1])) {
751 echo fread($ahPipes[1], 4096);
755 $iReturn = proc_close($hProcess);
757 fail("pgsql returned with error code ($iReturn)");
760 fclose($ahGzipPipes[1]);
761 proc_close($hGzipProcess);
765 function pgsqlRunScript($sScript, $bfatal = true)
771 $aCMDResult['verbose'],
772 $aCMDResult['ignore-errors']
776 function pgsqlRunPartitionScript($sTemplate)
781 $sSQL = 'select distinct partition from country_name';
782 $aPartitions = chksql($oDB->getCol($sSQL));
783 if (!$aCMDResult['no-partitions']) $aPartitions[] = 0;
785 preg_match_all('#^-- start(.*?)^-- end#ms', $sTemplate, $aMatches, PREG_SET_ORDER);
786 foreach ($aMatches as $aMatch) {
788 foreach ($aPartitions as $sPartitionName) {
789 $sResult .= str_replace('-partition-', $sPartitionName, $aMatch[1]);
791 $sTemplate = str_replace($aMatch[0], $sResult, $sTemplate);
794 pgsqlRunScript($sTemplate);
797 function pgsqlRunRestoreData($sDumpFile)
799 // Convert database DSN to psql parameters
800 $aDSNInfo = DB::parseDSN(CONST_Database_DSN);
801 if (!isset($aDSNInfo['port']) || !$aDSNInfo['port']) $aDSNInfo['port'] = 5432;
802 $sCMD = 'pg_restore -p '.$aDSNInfo['port'].' -d '.$aDSNInfo['database'].' -Fc -a '.$sDumpFile;
804 $aDescriptors = array(
805 0 => array('pipe', 'r'),
806 1 => array('pipe', 'w'),
807 2 => array('file', '/dev/null', 'a')
810 $hProcess = proc_open($sCMD, $aDescriptors, $ahPipes);
811 if (!is_resource($hProcess)) fail('unable to start pg_restore');
815 // TODO: error checking
816 while (!feof($ahPipes[1])) {
817 echo fread($ahPipes[1], 4096);
821 $iReturn = proc_close($hProcess);
824 function pgsqlRunDropAndRestore($sDumpFile)
826 // Convert database DSN to psql parameters
827 $aDSNInfo = DB::parseDSN(CONST_Database_DSN);
828 if (!isset($aDSNInfo['port']) || !$aDSNInfo['port']) $aDSNInfo['port'] = 5432;
829 $sCMD = 'pg_restore -p '.$aDSNInfo['port'].' -d '.$aDSNInfo['database'].' -Fc --clean '.$sDumpFile;
831 $aDescriptors = array(
832 0 => array('pipe', 'r'),
833 1 => array('pipe', 'w'),
834 2 => array('file', '/dev/null', 'a')
837 $hProcess = proc_open($sCMD, $aDescriptors, $ahPipes);
838 if (!is_resource($hProcess)) fail('unable to start pg_restore');
842 // TODO: error checking
843 while (!feof($ahPipes[1])) {
844 echo fread($ahPipes[1], 4096);
848 $iReturn = proc_close($hProcess);
851 function passthruCheckReturn($cmd)
854 passthru($cmd, $result);
855 if ($result != 0) fail('Error executing external command: '.$cmd);
858 function replace_tablespace($sTemplate, $sTablespace, $sSql)
861 $sSql = str_replace($sTemplate, 'TABLESPACE "'.$sTablespace.'"', $sSql);
863 $sSql = str_replace($sTemplate, '', $sSql);
869 function create_sql_functions($aCMDResult)
871 $sTemplate = file_get_contents(CONST_BasePath.'/sql/functions.sql');
872 $sTemplate = str_replace('{modulepath}', CONST_InstallPath.'/module', $sTemplate);
873 if ($aCMDResult['enable-diff-updates']) {
874 $sTemplate = str_replace('RETURN NEW; -- %DIFFUPDATES%', '--', $sTemplate);
876 if ($aCMDResult['enable-debug-statements']) {
877 $sTemplate = str_replace('--DEBUG:', '', $sTemplate);
879 if (CONST_Limit_Reindexing) {
880 $sTemplate = str_replace('--LIMIT INDEXING:', '', $sTemplate);
882 if (!CONST_Use_US_Tiger_Data) {
883 $sTemplate = str_replace('-- %NOTIGERDATA% ', '', $sTemplate);
885 if (!CONST_Use_Aux_Location_data) {
886 $sTemplate = str_replace('-- %NOAUXDATA% ', '', $sTemplate);
888 pgsqlRunScript($sTemplate);