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'])
66 ? $aCMDResult['threads']
67 : (min(16, getProcessorCount()) - 1);
69 if ($iInstances < 1) {
71 warn("resetting threads to $iInstances");
73 if ($iInstances > getProcessorCount()) {
74 $iInstances = getProcessorCount();
75 warn("resetting threads to $iInstances");
78 // Assume we can steal all the cache memory in the box (unless told otherwise)
79 if (isset($aCMDResult['osm2pgsql-cache'])) {
80 $iCacheMemory = $aCMDResult['osm2pgsql-cache'];
82 $iCacheMemory = getCacheMemoryMB();
85 $aDSNInfo = DB::parseDSN(CONST_Database_DSN);
86 if (!isset($aDSNInfo['port']) || !$aDSNInfo['port']) $aDSNInfo['port'] = 5432;
88 if ($aCMDResult['create-db'] || $aCMDResult['all']) {
90 $bDidSomething = true;
91 $oDB = DB::connect(CONST_Database_DSN, false);
92 if (!PEAR::isError($oDB)) {
93 fail('database already exists ('.CONST_Database_DSN.')');
95 passthruCheckReturn('createdb -E UTF-8 -p '.$aDSNInfo['port'].' '.$aDSNInfo['database']);
98 if ($aCMDResult['setup-db'] || $aCMDResult['all']) {
100 $bDidSomething = true;
104 $fPostgresVersion = getPostgresVersion($oDB);
105 echo 'Postgres version found: '.$fPostgresVersion."\n";
107 if ($fPostgresVersion < 9.1) {
108 fail('Minimum supported version of Postgresql is 9.1.');
111 pgsqlRunScript('CREATE EXTENSION IF NOT EXISTS hstore');
112 pgsqlRunScript('CREATE EXTENSION IF NOT EXISTS postgis');
114 // For extratags and namedetails the hstore_to_json converter is
115 // needed which is only available from Postgresql 9.3+. For older
116 // versions add a dummy function that returns nothing.
117 $iNumFunc = chksql($oDB->getOne("select count(*) from pg_proc where proname = 'hstore_to_json'"));
119 if ($iNumFunc == 0) {
120 pgsqlRunScript("create function hstore_to_json(dummy hstore) returns text AS 'select null::text' language sql immutable");
121 warn('Postgresql is too old. extratags and namedetails API not available.');
124 $fPostgisVersion = getPostgisVersion($oDB);
125 echo 'Postgis version found: '.$fPostgisVersion."\n";
127 if ($fPostgisVersion < 2.1) {
128 // Functions were renamed in 2.1 and throw an annoying deprecation warning
129 pgsqlRunScript('ALTER FUNCTION st_line_interpolate_point(geometry, double precision) RENAME TO ST_LineInterpolatePoint');
130 pgsqlRunScript('ALTER FUNCTION ST_Line_Locate_Point(geometry, geometry) RENAME TO ST_LineLocatePoint');
132 if ($fPostgisVersion < 2.2) {
133 pgsqlRunScript('ALTER FUNCTION ST_Distance_Spheroid(geometry, geometry, spheroid) RENAME TO ST_DistanceSpheroid');
136 $i = chksql($oDB->getOne("select count(*) from pg_user where usename = '".CONST_Database_Web_User."'"));
138 echo "\nERROR: Web user '".CONST_Database_Web_User."' does not exist. Create it with:\n";
139 echo "\n createuser ".CONST_Database_Web_User."\n\n";
143 // Try accessing the C module, so we know early if something is wrong
144 // and can simply error out.
145 $sSQL = "CREATE FUNCTION nominatim_test_import_func(text) RETURNS text AS '";
146 $sSQL .= CONST_InstallPath."/module/nominatim.so', 'transliteration' LANGUAGE c IMMUTABLE STRICT";
147 $sSQL .= ';DROP FUNCTION nominatim_test_import_func(text);';
148 $oResult = $oDB->query($sSQL);
150 if (PEAR::isError($oResult)) {
151 echo "\nERROR: Failed to load nominatim module. Reason:\n";
152 echo $oResult->userinfo."\n\n";
156 if (!file_exists(CONST_ExtraDataPath.'/country_osm_grid.sql.gz')) {
157 echo 'Error: you need to download the country_osm_grid first:';
158 echo "\n wget -O ".CONST_ExtraDataPath."/country_osm_grid.sql.gz https://www.nominatim.org/data/country_grid.sql.gz\n";
162 pgsqlRunScriptFile(CONST_BasePath.'/data/country_name.sql');
163 pgsqlRunScriptFile(CONST_BasePath.'/data/country_naturalearthdata.sql');
164 pgsqlRunScriptFile(CONST_BasePath.'/data/country_osm_grid.sql.gz');
165 pgsqlRunScriptFile(CONST_BasePath.'/data/gb_postcode_table.sql');
166 if (file_exists(CONST_BasePath.'/data/gb_postcode_data.sql.gz')) {
167 pgsqlRunScriptFile(CONST_BasePath.'/data/gb_postcode_data.sql.gz');
169 warn('external UK postcode table not found.');
171 if (CONST_Use_Extra_US_Postcodes) {
172 pgsqlRunScriptFile(CONST_BasePath.'/data/us_postcode.sql');
175 if ($aCMDResult['no-partitions']) {
176 pgsqlRunScript('update country_name set partition = 0');
179 // the following will be needed by create_functions later but
180 // is only defined in the subsequently called create_tables.
181 // Create dummies here that will be overwritten by the proper
182 // versions in create-tables.
183 pgsqlRunScript('CREATE TABLE place_boundingbox ()');
184 pgsqlRunScript('create type wikipedia_article_match as ()');
187 if ($aCMDResult['import-data'] || $aCMDResult['all']) {
189 $bDidSomething = true;
191 $osm2pgsql = CONST_Osm2pgsql_Binary;
192 if (!file_exists($osm2pgsql)) {
193 echo "Check CONST_Osm2pgsql_Binary in your local settings file.\n";
194 echo "Normally you should not need to set this manually.\n";
195 fail("osm2pgsql not found in '$osm2pgsql'");
198 if (!is_null(CONST_Osm2pgsql_Flatnode_File) && CONST_Osm2pgsql_Flatnode_File) {
199 $osm2pgsql .= ' --flat-nodes '.CONST_Osm2pgsql_Flatnode_File;
201 if (CONST_Tablespace_Osm2pgsql_Data)
202 $osm2pgsql .= ' --tablespace-slim-data '.CONST_Tablespace_Osm2pgsql_Data;
203 if (CONST_Tablespace_Osm2pgsql_Index)
204 $osm2pgsql .= ' --tablespace-slim-index '.CONST_Tablespace_Osm2pgsql_Index;
205 if (CONST_Tablespace_Place_Data)
206 $osm2pgsql .= ' --tablespace-main-data '.CONST_Tablespace_Place_Data;
207 if (CONST_Tablespace_Place_Index)
208 $osm2pgsql .= ' --tablespace-main-index '.CONST_Tablespace_Place_Index;
209 $osm2pgsql .= ' -lsc -O gazetteer --hstore --number-processes 1';
210 $osm2pgsql .= ' -C '.$iCacheMemory;
211 $osm2pgsql .= ' -P '.$aDSNInfo['port'];
212 $osm2pgsql .= ' -d '.$aDSNInfo['database'].' '.$aCMDResult['osm-file'];
213 passthruCheckReturn($osm2pgsql);
216 if (!$aCMDResult['ignore-errors'] && !chksql($oDB->getRow('select * from place limit 1'))) {
221 if ($aCMDResult['create-functions'] || $aCMDResult['all']) {
222 info('Create Functions');
223 $bDidSomething = true;
224 if (!file_exists(CONST_InstallPath.'/module/nominatim.so')) {
225 fail('nominatim module not built');
227 create_sql_functions($aCMDResult);
230 if ($aCMDResult['create-tables'] || $aCMDResult['all']) {
231 info('Create Tables');
232 $bDidSomething = true;
234 $sTemplate = file_get_contents(CONST_BasePath.'/sql/tables.sql');
235 $sTemplate = str_replace('{www-user}', CONST_Database_Web_User, $sTemplate);
236 $sTemplate = replace_tablespace(
238 CONST_Tablespace_Address_Data,
241 $sTemplate = replace_tablespace(
242 '{ts:address-index}',
243 CONST_Tablespace_Address_Index,
246 $sTemplate = replace_tablespace(
248 CONST_Tablespace_Search_Data,
251 $sTemplate = replace_tablespace(
253 CONST_Tablespace_Search_Index,
256 $sTemplate = replace_tablespace(
258 CONST_Tablespace_Aux_Data,
261 $sTemplate = replace_tablespace(
263 CONST_Tablespace_Aux_Index,
266 pgsqlRunScript($sTemplate, false);
268 // re-run the functions
269 info('Recreate Functions');
270 create_sql_functions($aCMDResult);
273 if ($aCMDResult['create-partition-tables'] || $aCMDResult['all']) {
274 info('Create Partition Tables');
275 $bDidSomething = true;
277 $sTemplate = file_get_contents(CONST_BasePath.'/sql/partition-tables.src.sql');
278 $sTemplate = replace_tablespace(
280 CONST_Tablespace_Address_Data,
283 $sTemplate = replace_tablespace(
284 '{ts:address-index}',
285 CONST_Tablespace_Address_Index,
288 $sTemplate = replace_tablespace(
290 CONST_Tablespace_Search_Data,
293 $sTemplate = replace_tablespace(
295 CONST_Tablespace_Search_Index,
298 $sTemplate = replace_tablespace(
300 CONST_Tablespace_Aux_Data,
303 $sTemplate = replace_tablespace(
305 CONST_Tablespace_Aux_Index,
309 pgsqlRunPartitionScript($sTemplate);
313 if ($aCMDResult['create-partition-functions'] || $aCMDResult['all']) {
314 info('Create Partition Functions');
315 $bDidSomething = true;
317 $sTemplate = file_get_contents(CONST_BasePath.'/sql/partition-functions.src.sql');
319 pgsqlRunPartitionScript($sTemplate);
322 if ($aCMDResult['import-wikipedia-articles'] || $aCMDResult['all']) {
323 $bDidSomething = true;
324 $sWikiArticlesFile = CONST_Wikipedia_Data_Path.'/wikipedia_article.sql.bin';
325 $sWikiRedirectsFile = CONST_Wikipedia_Data_Path.'/wikipedia_redirect.sql.bin';
326 if (file_exists($sWikiArticlesFile)) {
327 info('Importing wikipedia articles');
328 pgsqlRunDropAndRestore($sWikiArticlesFile);
330 warn('wikipedia article dump file not found - places will have default importance');
332 if (file_exists($sWikiRedirectsFile)) {
333 info('Importing wikipedia redirects');
334 pgsqlRunDropAndRestore($sWikiRedirectsFile);
336 warn('wikipedia redirect dump file not found - some place importance values may be missing');
341 if ($aCMDResult['load-data'] || $aCMDResult['all']) {
342 info('Drop old Data');
343 $bDidSomething = true;
346 if (!pg_query($oDB->connection, 'TRUNCATE word')) fail(pg_last_error($oDB->connection));
348 if (!pg_query($oDB->connection, 'TRUNCATE placex')) fail(pg_last_error($oDB->connection));
350 if (!pg_query($oDB->connection, 'TRUNCATE location_property_osmline')) fail(pg_last_error($oDB->connection));
352 if (!pg_query($oDB->connection, 'TRUNCATE place_addressline')) fail(pg_last_error($oDB->connection));
354 if (!pg_query($oDB->connection, 'TRUNCATE place_boundingbox')) fail(pg_last_error($oDB->connection));
356 if (!pg_query($oDB->connection, 'TRUNCATE location_area')) fail(pg_last_error($oDB->connection));
358 if (!pg_query($oDB->connection, 'TRUNCATE search_name')) fail(pg_last_error($oDB->connection));
360 if (!pg_query($oDB->connection, 'TRUNCATE search_name_blank')) fail(pg_last_error($oDB->connection));
362 if (!pg_query($oDB->connection, 'DROP SEQUENCE seq_place')) fail(pg_last_error($oDB->connection));
364 if (!pg_query($oDB->connection, 'CREATE SEQUENCE seq_place start 100000')) fail(pg_last_error($oDB->connection));
367 $sSQL = 'select distinct partition from country_name';
368 $aPartitions = chksql($oDB->getCol($sSQL));
369 if (!$aCMDResult['no-partitions']) $aPartitions[] = 0;
370 foreach ($aPartitions as $sPartition) {
371 if (!pg_query($oDB->connection, 'TRUNCATE location_road_'.$sPartition)) fail(pg_last_error($oDB->connection));
375 // used by getorcreate_word_id to ignore frequent partial words
376 $sSQL = 'CREATE OR REPLACE FUNCTION get_maxwordfreq() RETURNS integer AS ';
377 $sSQL .= '$$ SELECT '.CONST_Max_Word_Frequency.' as maxwordfreq; $$ LANGUAGE SQL IMMUTABLE';
378 if (!pg_query($oDB->connection, $sSQL)) {
379 fail(pg_last_error($oDB->connection));
383 // pre-create the word list
384 if (!$aCMDResult['disable-token-precalc']) {
385 info('Loading word list');
386 pgsqlRunScriptFile(CONST_BasePath.'/data/words.sql');
390 $sColumns = 'osm_type, osm_id, class, type, name, admin_level, address, extratags, geometry';
392 $aDBInstances = array();
393 $iLoadThreads = max(1, $iInstances - 1);
394 for ($i = 0; $i < $iLoadThreads; $i++) {
395 $aDBInstances[$i] =& getDB(true);
396 $sSQL = "INSERT INTO placex ($sColumns) SELECT $sColumns FROM place WHERE osm_id % $iLoadThreads = $i";
397 $sSQL .= " and not (class='place' and type='houses' and osm_type='W'";
398 $sSQL .= " and ST_GeometryType(geometry) = 'ST_LineString')";
399 $sSQL .= ' and ST_IsValid(geometry)';
400 if ($aCMDResult['verbose']) echo "$sSQL\n";
401 if (!pg_send_query($aDBInstances[$i]->connection, $sSQL)) {
402 fail(pg_last_error($aDBInstances[$i]->connection));
405 // last thread for interpolation lines
406 $aDBInstances[$iLoadThreads] =& getDB(true);
407 $sSQL = 'insert into location_property_osmline';
408 $sSQL .= ' (osm_id, address, linegeo)';
409 $sSQL .= ' SELECT osm_id, address, geometry from place where ';
410 $sSQL .= "class='place' and type='houses' and osm_type='W' and ST_GeometryType(geometry) = 'ST_LineString'";
411 if ($aCMDResult['verbose']) echo "$sSQL\n";
412 if (!pg_send_query($aDBInstances[$iLoadThreads]->connection, $sSQL)) {
413 fail(pg_last_error($aDBInstances[$iLoadThreads]->connection));
419 for ($i = 0; $i <= $iLoadThreads; $i++) {
420 if (pg_connection_busy($aDBInstances[$i]->connection)) $bAnyBusy = true;
426 info('Reanalysing database');
427 pgsqlRunScript('ANALYSE');
429 $sDatabaseDate = getDatabaseDate($oDB);
430 pg_query($oDB->connection, 'TRUNCATE import_status');
431 if ($sDatabaseDate === false) {
432 warn('could not determine database date.');
434 $sSQL = "INSERT INTO import_status (lastimportdate) VALUES('".$sDatabaseDate."')";
435 pg_query($oDB->connection, $sSQL);
436 echo "Latest data imported from $sDatabaseDate.\n";
440 if ($aCMDResult['import-tiger-data']) {
441 info('Import Tiger data');
442 $bDidSomething = true;
444 $sTemplate = file_get_contents(CONST_BasePath.'/sql/tiger_import_start.sql');
445 $sTemplate = str_replace('{www-user}', CONST_Database_Web_User, $sTemplate);
446 $sTemplate = replace_tablespace(
448 CONST_Tablespace_Aux_Data,
451 $sTemplate = replace_tablespace(
453 CONST_Tablespace_Aux_Index,
456 pgsqlRunScript($sTemplate, false);
458 $aDBInstances = array();
459 for ($i = 0; $i < $iInstances; $i++) {
460 $aDBInstances[$i] =& getDB(true);
463 foreach (glob(CONST_Tiger_Data_Path.'/*.sql') as $sFile) {
465 $hFile = fopen($sFile, 'r');
466 $sSQL = fgets($hFile, 100000);
470 for ($i = 0; $i < $iInstances; $i++) {
471 if (!pg_connection_busy($aDBInstances[$i]->connection)) {
472 while (pg_get_result($aDBInstances[$i]->connection));
473 $sSQL = fgets($hFile, 100000);
475 if (!pg_send_query($aDBInstances[$i]->connection, $sSQL)) fail(pg_last_error($oDB->connection));
477 if ($iLines == 1000) {
491 for ($i = 0; $i < $iInstances; $i++) {
492 if (pg_connection_busy($aDBInstances[$i]->connection)) $bAnyBusy = true;
499 info('Creating indexes on Tiger data');
500 $sTemplate = file_get_contents(CONST_BasePath.'/sql/tiger_import_finish.sql');
501 $sTemplate = str_replace('{www-user}', CONST_Database_Web_User, $sTemplate);
502 $sTemplate = replace_tablespace(
504 CONST_Tablespace_Aux_Data,
507 $sTemplate = replace_tablespace(
509 CONST_Tablespace_Aux_Index,
512 pgsqlRunScript($sTemplate, false);
515 if ($aCMDResult['calculate-postcodes'] || $aCMDResult['all']) {
516 info('Calculate Postcodes');
517 $bDidSomething = true;
519 if (!pg_query($oDB->connection, 'TRUNCATE location_postcode')) {
520 fail(pg_last_error($oDB->connection));
523 $sSQL = 'INSERT INTO location_postcode';
524 $sSQL .= ' (place_id, indexed_status, country_code, postcode, geometry) ';
525 $sSQL .= "SELECT nextval('seq_place'), 1, country_code,";
526 $sSQL .= " upper(trim (both ' ' from address->'postcode')) as pc,";
527 $sSQL .= ' ST_Centroid(ST_Collect(ST_Centroid(geometry)))';
528 $sSQL .= ' FROM placex';
529 $sSQL .= " WHERE address ? 'postcode' AND address->'postcode' NOT SIMILAR TO '%(,|;)%'";
530 $sSQL .= ' AND geometry IS NOT null';
531 $sSQL .= ' GROUP BY country_code, pc';
533 if (!pg_query($oDB->connection, $sSQL)) {
534 fail(pg_last_error($oDB->connection));
537 if (CONST_Use_Extra_US_Postcodes) {
538 // only add postcodes that are not yet available in OSM
539 $sSQL = 'INSERT INTO location_postcode';
540 $sSQL .= ' (place_id, indexed_status, country_code, postcode, geometry) ';
541 $sSQL .= "SELECT nextval('seq_place'), 1, 'us', postcode,";
542 $sSQL .= ' ST_SetSRID(ST_Point(x,y),4326)';
543 $sSQL .= ' FROM us_postcode WHERE postcode NOT IN';
544 $sSQL .= ' (SELECT postcode FROM location_postcode';
545 $sSQL .= " WHERE country_code = 'us')";
546 if (!pg_query($oDB->connection, $sSQL)) fail(pg_last_error($oDB->connection));
549 // add missing postcodes for GB (if available)
550 $sSQL = 'INSERT INTO location_postcode';
551 $sSQL .= ' (place_id, indexed_status, country_code, postcode, geometry) ';
552 $sSQL .= "SELECT nextval('seq_place'), 1, 'gb', postcode, geometry";
553 $sSQL .= ' FROM gb_postcode WHERE postcode NOT IN';
554 $sSQL .= ' (SELECT postcode FROM location_postcode';
555 $sSQL .= " WHERE country_code = 'gb')";
556 if (!pg_query($oDB->connection, $sSQL)) fail(pg_last_error($oDB->connection));
558 if (!$aCMDResult['all']) {
559 $sSQL = "DELETE FROM word WHERE class='place' and type='postcode'";
560 $sSQL .= 'and word NOT IN (SELECT postcode FROM location_postcode)';
561 if (!pg_query($oDB->connection, $sSQL)) {
562 fail(pg_last_error($oDB->connection));
565 $sSQL = 'SELECT count(getorcreate_postcode_id(v)) FROM ';
566 $sSQL .= '(SELECT distinct(postcode) as v FROM location_postcode) p';
568 if (!pg_query($oDB->connection, $sSQL)) {
569 fail(pg_last_error($oDB->connection));
573 if ($aCMDResult['osmosis-init']) {
574 $bDidSomething = true;
575 echo "Command 'osmosis-init' no longer available, please use utils/update.php --init-updates.\n";
578 if ($aCMDResult['index'] || $aCMDResult['all']) {
579 $bDidSomething = true;
581 $sBaseCmd = CONST_InstallPath.'/nominatim/nominatim -i -d '.$aDSNInfo['database'].' -P '.$aDSNInfo['port'].' -t '.$iInstances.$sOutputFile;
582 info('Index ranks 0 - 4');
583 passthruCheckReturn($sBaseCmd.' -R 4');
584 if (!$aCMDResult['index-noanalyse']) pgsqlRunScript('ANALYSE');
585 info('Index ranks 5 - 25');
586 passthruCheckReturn($sBaseCmd.' -r 5 -R 25');
587 if (!$aCMDResult['index-noanalyse']) pgsqlRunScript('ANALYSE');
588 info('Index ranks 26 - 30');
589 passthruCheckReturn($sBaseCmd.' -r 26');
591 info('Index postcodes');
593 $sSQL = 'UPDATE location_postcode SET indexed_status = 0';
594 if (!pg_query($oDB->connection, $sSQL)) fail(pg_last_error($oDB->connection));
597 if ($aCMDResult['create-search-indices'] || $aCMDResult['all']) {
598 info('Create Search indices');
599 $bDidSomething = true;
601 $sTemplate = file_get_contents(CONST_BasePath.'/sql/indices.src.sql');
602 $sTemplate = str_replace('{www-user}', CONST_Database_Web_User, $sTemplate);
603 $sTemplate = replace_tablespace(
604 '{ts:address-index}',
605 CONST_Tablespace_Address_Index,
608 $sTemplate = replace_tablespace(
610 CONST_Tablespace_Search_Index,
613 $sTemplate = replace_tablespace(
615 CONST_Tablespace_Aux_Index,
619 pgsqlRunScript($sTemplate);
622 if ($aCMDResult['create-country-names'] || $aCMDResult['all']) {
623 info('Create search index for default country names');
624 $bDidSomething = true;
626 pgsqlRunScript("select getorcreate_country(make_standard_name('uk'), 'gb')");
627 pgsqlRunScript("select getorcreate_country(make_standard_name('united states'), 'us')");
628 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');
629 pgsqlRunScript("select count(*) from (select getorcreate_country(make_standard_name(name->'name'), country_code) from country_name where name ? 'name') as x");
631 $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 ';
632 if (CONST_Languages) {
635 foreach (explode(',', CONST_Languages) as $sLang) {
636 $sSQL .= $sDelim."'name:$sLang'";
641 // all include all simple name tags
642 $sSQL .= "like 'name:%'";
645 pgsqlRunScript($sSQL);
648 if ($aCMDResult['drop']) {
649 info('Drop tables only required for updates');
650 // The implementation is potentially a bit dangerous because it uses
651 // a positive selection of tables to keep, and deletes everything else.
652 // Including any tables that the unsuspecting user might have manually
653 // created. USE AT YOUR OWN PERIL.
654 $bDidSomething = true;
656 // tables we want to keep. everything else goes.
657 $aKeepTables = array(
663 'location_property*',
676 $aDropTables = array();
677 $aHaveTables = chksql($oDB->getCol("SELECT tablename FROM pg_tables WHERE schemaname='public'"));
679 foreach ($aHaveTables as $sTable) {
681 foreach ($aKeepTables as $sKeep) {
682 if (fnmatch($sKeep, $sTable)) {
687 if (!$bFound) array_push($aDropTables, $sTable);
690 foreach ($aDropTables as $sDrop) {
691 if ($aCMDResult['verbose']) echo "dropping table $sDrop\n";
692 @pg_query($oDB->connection, "DROP TABLE $sDrop CASCADE");
693 // ignore warnings/errors as they might be caused by a table having
694 // been deleted already by CASCADE
697 if (!is_null(CONST_Osm2pgsql_Flatnode_File) && CONST_Osm2pgsql_Flatnode_File) {
698 if ($aCMDResult['verbose']) echo 'deleting '.CONST_Osm2pgsql_Flatnode_File."\n";
699 unlink(CONST_Osm2pgsql_Flatnode_File);
703 if (!$bDidSomething) {
704 showUsage($aCMDOptions, true);
706 echo "Summary of warnings:\n\n";
709 info('Setup finished.');
713 function pgsqlRunScriptFile($sFilename)
716 if (!file_exists($sFilename)) fail('unable to find '.$sFilename);
718 // Convert database DSN to psql parameters
719 $aDSNInfo = DB::parseDSN(CONST_Database_DSN);
720 if (!isset($aDSNInfo['port']) || !$aDSNInfo['port']) $aDSNInfo['port'] = 5432;
721 $sCMD = 'psql -p '.$aDSNInfo['port'].' -d '.$aDSNInfo['database'];
722 if (!$aCMDResult['verbose']) {
727 if (preg_match('/\\.gz$/', $sFilename)) {
728 $aDescriptors = array(
729 0 => array('pipe', 'r'),
730 1 => array('pipe', 'w'),
731 2 => array('file', '/dev/null', 'a')
733 $hGzipProcess = proc_open('zcat '.$sFilename, $aDescriptors, $ahGzipPipes);
734 if (!is_resource($hGzipProcess)) fail('unable to start zcat');
735 $aReadPipe = $ahGzipPipes[1];
736 fclose($ahGzipPipes[0]);
738 $sCMD .= ' -f '.$sFilename;
739 $aReadPipe = array('pipe', 'r');
742 $aDescriptors = array(
744 1 => array('pipe', 'w'),
745 2 => array('file', '/dev/null', 'a')
748 $hProcess = proc_open($sCMD, $aDescriptors, $ahPipes);
749 if (!is_resource($hProcess)) fail('unable to start pgsql');
752 // TODO: error checking
753 while (!feof($ahPipes[1])) {
754 echo fread($ahPipes[1], 4096);
758 $iReturn = proc_close($hProcess);
760 fail("pgsql returned with error code ($iReturn)");
763 fclose($ahGzipPipes[1]);
764 proc_close($hGzipProcess);
768 function pgsqlRunScript($sScript, $bfatal = true)
774 $aCMDResult['verbose'],
775 $aCMDResult['ignore-errors']
779 function pgsqlRunPartitionScript($sTemplate)
784 $sSQL = 'select distinct partition from country_name';
785 $aPartitions = chksql($oDB->getCol($sSQL));
786 if (!$aCMDResult['no-partitions']) $aPartitions[] = 0;
788 preg_match_all('#^-- start(.*?)^-- end#ms', $sTemplate, $aMatches, PREG_SET_ORDER);
789 foreach ($aMatches as $aMatch) {
791 foreach ($aPartitions as $sPartitionName) {
792 $sResult .= str_replace('-partition-', $sPartitionName, $aMatch[1]);
794 $sTemplate = str_replace($aMatch[0], $sResult, $sTemplate);
797 pgsqlRunScript($sTemplate);
800 function pgsqlRunRestoreData($sDumpFile)
802 // Convert database DSN to psql parameters
803 $aDSNInfo = DB::parseDSN(CONST_Database_DSN);
804 if (!isset($aDSNInfo['port']) || !$aDSNInfo['port']) $aDSNInfo['port'] = 5432;
805 $sCMD = 'pg_restore -p '.$aDSNInfo['port'].' -d '.$aDSNInfo['database'].' -Fc -a '.$sDumpFile;
807 $aDescriptors = array(
808 0 => array('pipe', 'r'),
809 1 => array('pipe', 'w'),
810 2 => array('file', '/dev/null', 'a')
813 $hProcess = proc_open($sCMD, $aDescriptors, $ahPipes);
814 if (!is_resource($hProcess)) fail('unable to start pg_restore');
818 // TODO: error checking
819 while (!feof($ahPipes[1])) {
820 echo fread($ahPipes[1], 4096);
824 $iReturn = proc_close($hProcess);
827 function pgsqlRunDropAndRestore($sDumpFile)
829 // Convert database DSN to psql parameters
830 $aDSNInfo = DB::parseDSN(CONST_Database_DSN);
831 if (!isset($aDSNInfo['port']) || !$aDSNInfo['port']) $aDSNInfo['port'] = 5432;
832 $sCMD = 'pg_restore -p '.$aDSNInfo['port'].' -d '.$aDSNInfo['database'].' -Fc --clean '.$sDumpFile;
834 $aDescriptors = array(
835 0 => array('pipe', 'r'),
836 1 => array('pipe', 'w'),
837 2 => array('file', '/dev/null', 'a')
840 $hProcess = proc_open($sCMD, $aDescriptors, $ahPipes);
841 if (!is_resource($hProcess)) fail('unable to start pg_restore');
845 // TODO: error checking
846 while (!feof($ahPipes[1])) {
847 echo fread($ahPipes[1], 4096);
851 $iReturn = proc_close($hProcess);
854 function passthruCheckReturn($cmd)
857 passthru($cmd, $result);
858 if ($result != 0) fail('Error executing external command: '.$cmd);
861 function replace_tablespace($sTemplate, $sTablespace, $sSql)
864 $sSql = str_replace($sTemplate, 'TABLESPACE "'.$sTablespace.'"', $sSql);
866 $sSql = str_replace($sTemplate, '', $sSql);
872 function create_sql_functions($aCMDResult)
874 $sTemplate = file_get_contents(CONST_BasePath.'/sql/functions.sql');
875 $sTemplate = str_replace('{modulepath}', CONST_InstallPath.'/module', $sTemplate);
876 if ($aCMDResult['enable-diff-updates']) {
877 $sTemplate = str_replace('RETURN NEW; -- %DIFFUPDATES%', '--', $sTemplate);
879 if ($aCMDResult['enable-debug-statements']) {
880 $sTemplate = str_replace('--DEBUG:', '', $sTemplate);
882 if (CONST_Limit_Reindexing) {
883 $sTemplate = str_replace('--LIMIT INDEXING:', '', $sTemplate);
885 if (!CONST_Use_US_Tiger_Data) {
886 $sTemplate = str_replace('-- %NOTIGERDATA% ', '', $sTemplate);
888 if (!CONST_Use_Aux_Location_data) {
889 $sTemplate = str_replace('-- %NOAUXDATA% ', '', $sTemplate);
891 pgsqlRunScript($sTemplate);