4 require_once(dirname(dirname(__FILE__)).'/settings/settings.php');
5 require_once(CONST_BasePath.'/lib/init-cmd.php');
6 ini_set('memory_limit', '800M');
10 'Create and setup nominatim search system',
11 array('help', 'h', 0, 1, 0, 0, false, 'Show Help'),
12 array('quiet', 'q', 0, 1, 0, 0, 'bool', 'Quiet output'),
13 array('verbose', 'v', 0, 1, 0, 0, 'bool', 'Verbose output'),
15 array('osm-file', '', 0, 1, 1, 1, 'realpath', 'File to import'),
16 array('threads', '', 0, 1, 1, 1, 'int', 'Number of threads (where possible)'),
18 array('all', '', 0, 1, 0, 0, 'bool', 'Do the complete process'),
20 array('create-db', '', 0, 1, 0, 0, 'bool', 'Create nominatim db'),
21 array('setup-db', '', 0, 1, 0, 0, 'bool', 'Build a blank nominatim db'),
22 array('import-data', '', 0, 1, 0, 0, 'bool', 'Import a osm file'),
23 array('osm2pgsql-cache', '', 0, 1, 1, 1, 'int', 'Cache size used by osm2pgsql'),
24 array('create-functions', '', 0, 1, 0, 0, 'bool', 'Create functions'),
25 array('enable-diff-updates', '', 0, 1, 0, 0, 'bool', 'Turn on the code required to make diff updates work'),
26 array('enable-debug-statements', '', 0, 1, 0, 0, 'bool', 'Include debug warning statements in pgsql commands'),
27 array('ignore-errors', '', 0, 1, 0, 0, 'bool', 'Continue import even when errors in SQL are present (EXPERT)'),
28 array('create-tables', '', 0, 1, 0, 0, 'bool', 'Create main tables'),
29 array('create-partition-tables', '', 0, 1, 0, 0, 'bool', 'Create required partition tables'),
30 array('create-partition-functions', '', 0, 1, 0, 0, 'bool', 'Create required partition triggers'),
31 array('no-partitions', '', 0, 1, 0, 0, 'bool', 'Do not partition search indices (speeds up import of single country extracts)'),
32 array('import-wikipedia-articles', '', 0, 1, 0, 0, 'bool', 'Import wikipedia article dump'),
33 array('load-data', '', 0, 1, 0, 0, 'bool', 'Copy data to live tables from import table'),
34 array('disable-token-precalc', '', 0, 1, 0, 0, 'bool', 'Disable name precalculation (EXPERT)'),
35 array('import-tiger-data', '', 0, 1, 0, 0, 'bool', 'Import tiger data (not included in \'all\')'),
36 array('calculate-postcodes', '', 0, 1, 0, 0, 'bool', 'Calculate postcode centroids'),
37 array('osmosis-init', '', 0, 1, 0, 0, 'bool', 'Generate default osmosis configuration'),
38 array('index', '', 0, 1, 0, 0, 'bool', 'Index the data'),
39 array('index-noanalyse', '', 0, 1, 0, 0, 'bool', 'Do not perform analyse operations during index (EXPERT)'),
40 array('create-search-indices', '', 0, 1, 0, 0, 'bool', 'Create additional indices required for search and update'),
41 array('create-country-names', '', 0, 1, 0, 0, 'bool', 'Create default list of searchable country names'),
42 array('drop', '', 0, 1, 0, 0, 'bool', 'Drop tables needed for updates, making the database readonly (EXPERIMENTAL)'),
44 getCmdOpt($_SERVER['argv'], $aCMDOptions, $aCMDResult, true, true);
46 $bDidSomething = false;
48 // Check if osm-file is set and points to a valid file if --all or --import-data is given
49 if ($aCMDResult['import-data'] || $aCMDResult['all']) {
50 if (!isset($aCMDResult['osm-file'])) {
51 fail('missing --osm-file for data import');
54 if (!file_exists($aCMDResult['osm-file'])) {
55 fail('the path supplied to --osm-file does not exist');
58 if (!is_readable($aCMDResult['osm-file'])) {
59 fail('osm-file "'.$aCMDResult['osm-file'].'" not readable');
64 // This is a pretty hard core default - the number of processors in the box - 1
65 $iInstances = isset($aCMDResult['threads'])?$aCMDResult['threads']:(getProcessorCount()-1);
66 if ($iInstances < 1) {
68 warn("resetting threads to $iInstances");
70 if ($iInstances > getProcessorCount()) {
71 $iInstances = getProcessorCount();
72 warn("resetting threads to $iInstances");
75 // Assume we can steal all the cache memory in the box (unless told otherwise)
76 if (isset($aCMDResult['osm2pgsql-cache'])) {
77 $iCacheMemory = $aCMDResult['osm2pgsql-cache'];
79 $iCacheMemory = getCacheMemoryMB();
82 $aDSNInfo = DB::parseDSN(CONST_Database_DSN);
83 if (!isset($aDSNInfo['port']) || !$aDSNInfo['port']) $aDSNInfo['port'] = 5432;
85 if ($aCMDResult['create-db'] || $aCMDResult['all']) {
87 $bDidSomething = true;
88 $oDB = DB::connect(CONST_Database_DSN, false);
89 if (!PEAR::isError($oDB)) {
90 fail('database already exists ('.CONST_Database_DSN.')');
92 passthruCheckReturn('createdb -E UTF-8 -p '.$aDSNInfo['port'].' '.$aDSNInfo['database']);
95 if ($aCMDResult['setup-db'] || $aCMDResult['all']) {
97 $bDidSomething = true;
101 $fPostgresVersion = getPostgresVersion($oDB);
102 echo 'Postgres version found: '.$fPostgresVersion."\n";
104 if ($fPostgresVersion < 9.1) {
105 fail('Minimum supported version of Postgresql is 9.1.');
108 pgsqlRunScript('CREATE EXTENSION IF NOT EXISTS hstore');
109 pgsqlRunScript('CREATE EXTENSION IF NOT EXISTS postgis');
111 // For extratags and namedetails the hstore_to_json converter is
112 // needed which is only available from Postgresql 9.3+. For older
113 // versions add a dummy function that returns nothing.
114 $iNumFunc = chksql($oDB->getOne("select count(*) from pg_proc where proname = 'hstore_to_json'"));
116 if ($iNumFunc == 0) {
117 pgsqlRunScript("create function hstore_to_json(dummy hstore) returns text AS 'select null::text' language sql immutable");
118 warn('Postgresql is too old. extratags and namedetails API not available.');
121 $fPostgisVersion = getPostgisVersion($oDB);
122 echo 'Postgis version found: '.$fPostgisVersion."\n";
124 if ($fPostgisVersion < 2.1) {
125 // Functions were renamed in 2.1 and throw an annoying deprecation warning
126 pgsqlRunScript('ALTER FUNCTION st_line_interpolate_point(geometry, double precision) RENAME TO ST_LineInterpolatePoint');
127 pgsqlRunScript('ALTER FUNCTION ST_Line_Locate_Point(geometry, geometry) RENAME TO ST_LineLocatePoint');
129 if ($fPostgisVersion < 2.2) {
130 pgsqlRunScript('ALTER FUNCTION ST_Distance_Spheroid(geometry, geometry, spheroid) RENAME TO ST_DistanceSpheroid');
133 $i = chksql($oDB->getOne("select count(*) from pg_user where usename = '".CONST_Database_Web_User."'"));
135 echo "\nERROR: Web user '".CONST_Database_Web_User."' does not exist. Create it with:\n";
136 echo "\n createuser ".CONST_Database_Web_User."\n\n";
140 // Try accessing the C module, so we know early if something is wrong
141 // and can simply error out.
142 $sSQL = "CREATE FUNCTION nominatim_test_import_func(text) RETURNS text AS '";
143 $sSQL .= CONST_InstallPath."/module/nominatim.so', 'transliteration' LANGUAGE c IMMUTABLE STRICT";
144 $sSQL .= ';DROP FUNCTION nominatim_test_import_func(text);';
145 $oResult = $oDB->query($sSQL);
147 if (PEAR::isError($oResult)) {
148 echo "\nERROR: Failed to load nominatim module. Reason:\n";
149 echo $oResult->userinfo."\n\n";
153 if (!file_exists(CONST_ExtraDataPath.'/country_osm_grid.sql.gz')) {
154 echo 'Error: you need to download the country_osm_grid first:';
155 echo "\n wget -O ".CONST_ExtraDataPath."/country_osm_grid.sql.gz http://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)) {
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')";
544 $sSQL .= 'TRUNCATE TABLE us_postcode';
546 if (!pg_query($oDB->connection, $sSQL)) fail(pg_last_error($oDB->connection));
548 // add missing postcodes for GB (if available)
549 $sSQL = 'INSERT INTO location_postcode';
550 $sSQL .= ' (place_id, indexed_status, country_code, postcode, geometry) ';
551 $sSQL .= "SELECT nextval('seq_place'), 1, 'gb', postcode, geometry";
552 $sSQL .= ' FROM gb_postcode WHERE postcode NOT IN';
553 $sSQL .= ' (SELECT postcode FROM location_postcode';
554 $sSQL .= " WHERE country_code = 'gb')";
555 if (!pg_query($oDB->connection, $sSQL)) fail(pg_last_error($oDB->connection));
557 if (!$aCMDResult['all']) {
558 $sSQL = "DELETE FROM word WHERE class='place' and type='postcode'";
559 $sSQL .= 'and word NOT IN (SELECT postcode FROM location_postcode)';
560 if (!pg_query($oDB->connection, $sSQL)) {
561 fail(pg_last_error($oDB->connection));
564 $sSQL = 'SELECT count(getorcreate_postcode_id(v)) FROM ';
565 $sSQL .= '(SELECT distinct(postcode) as v FROM location_postcode) p';
567 if (!pg_query($oDB->connection, $sSQL)) {
568 fail(pg_last_error($oDB->connection));
572 if ($aCMDResult['osmosis-init']) {
573 $bDidSomething = true;
574 echo "Command 'osmosis-init' no longer available, please use utils/update.php --init-updates.\n";
577 if ($aCMDResult['index'] || $aCMDResult['all']) {
578 $bDidSomething = true;
580 $sBaseCmd = CONST_InstallPath.'/nominatim/nominatim -i -d '.$aDSNInfo['database'].' -P '.$aDSNInfo['port'].' -t '.$iInstances.$sOutputFile;
581 info('Index ranks 0 - 4');
582 passthruCheckReturn($sBaseCmd.' -R 4');
583 if (!$aCMDResult['index-noanalyse']) pgsqlRunScript('ANALYSE');
584 info('Index ranks 5 - 25');
585 passthruCheckReturn($sBaseCmd.' -r 5 -R 25');
586 if (!$aCMDResult['index-noanalyse']) pgsqlRunScript('ANALYSE');
587 info('Index ranks 26 - 30');
588 passthruCheckReturn($sBaseCmd.' -r 26');
590 info('Index postcodes');
592 $sSQL = 'UPDATE location_postcode SET indexed_status = 0';
593 if (!pg_query($oDB->connection, $sSQL)) fail(pg_last_error($oDB->connection));
596 if ($aCMDResult['create-search-indices'] || $aCMDResult['all']) {
597 info('Create Search indices');
598 $bDidSomething = true;
600 $sTemplate = file_get_contents(CONST_BasePath.'/sql/indices.src.sql');
601 $sTemplate = replace_tablespace(
602 '{ts:address-index}',
603 CONST_Tablespace_Address_Index,
606 $sTemplate = replace_tablespace(
608 CONST_Tablespace_Search_Index,
611 $sTemplate = replace_tablespace(
613 CONST_Tablespace_Aux_Index,
617 pgsqlRunScript($sTemplate);
620 if ($aCMDResult['create-country-names'] || $aCMDResult['all']) {
621 info('Create search index for default country names');
622 $bDidSomething = true;
624 pgsqlRunScript("select getorcreate_country(make_standard_name('uk'), 'gb')");
625 pgsqlRunScript("select getorcreate_country(make_standard_name('united states'), 'us')");
626 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');
627 pgsqlRunScript("select count(*) from (select getorcreate_country(make_standard_name(name->'name'), country_code) from country_name where name ? 'name') as x");
629 $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 ';
630 if (CONST_Languages) {
633 foreach (explode(',', CONST_Languages) as $sLang) {
634 $sSQL .= $sDelim."'name:$sLang'";
639 // all include all simple name tags
640 $sSQL .= "like 'name:%'";
643 pgsqlRunScript($sSQL);
646 if ($aCMDResult['drop']) {
647 info('Drop tables only required for updates');
648 // The implementation is potentially a bit dangerous because it uses
649 // a positive selection of tables to keep, and deletes everything else.
650 // Including any tables that the unsuspecting user might have manually
651 // created. USE AT YOUR OWN PERIL.
652 $bDidSomething = true;
654 // tables we want to keep. everything else goes.
655 $aKeepTables = array(
660 'location_property*',
674 $aDropTables = array();
675 $aHaveTables = chksql($oDB->getCol("SELECT tablename FROM pg_tables WHERE schemaname='public'"));
677 foreach ($aHaveTables as $sTable) {
679 foreach ($aKeepTables as $sKeep) {
680 if (fnmatch($sKeep, $sTable)) {
685 if (!$bFound) array_push($aDropTables, $sTable);
688 foreach ($aDropTables as $sDrop) {
689 if ($aCMDResult['verbose']) echo "dropping table $sDrop\n";
690 @pg_query($oDB->connection, "DROP TABLE $sDrop CASCADE");
691 // ignore warnings/errors as they might be caused by a table having
692 // been deleted already by CASCADE
695 if (!is_null(CONST_Osm2pgsql_Flatnode_File)) {
696 if ($aCMDResult['verbose']) echo 'deleting '.CONST_Osm2pgsql_Flatnode_File."\n";
697 unlink(CONST_Osm2pgsql_Flatnode_File);
701 if (!$bDidSomething) {
702 showUsage($aCMDOptions, true);
704 echo "Summary of warnings:\n\n";
707 info('Setup finished.');
711 function pgsqlRunScriptFile($sFilename)
714 if (!file_exists($sFilename)) fail('unable to find '.$sFilename);
716 // Convert database DSN to psql parameters
717 $aDSNInfo = DB::parseDSN(CONST_Database_DSN);
718 if (!isset($aDSNInfo['port']) || !$aDSNInfo['port']) $aDSNInfo['port'] = 5432;
719 $sCMD = 'psql -p '.$aDSNInfo['port'].' -d '.$aDSNInfo['database'];
720 if (!$aCMDResult['verbose']) {
725 if (preg_match('/\\.gz$/', $sFilename)) {
726 $aDescriptors = array(
727 0 => array('pipe', 'r'),
728 1 => array('pipe', 'w'),
729 2 => array('file', '/dev/null', 'a')
731 $hGzipProcess = proc_open('zcat '.$sFilename, $aDescriptors, $ahGzipPipes);
732 if (!is_resource($hGzipProcess)) fail('unable to start zcat');
733 $aReadPipe = $ahGzipPipes[1];
734 fclose($ahGzipPipes[0]);
736 $sCMD .= ' -f '.$sFilename;
737 $aReadPipe = array('pipe', 'r');
740 $aDescriptors = array(
742 1 => array('pipe', 'w'),
743 2 => array('file', '/dev/null', 'a')
746 $hProcess = proc_open($sCMD, $aDescriptors, $ahPipes);
747 if (!is_resource($hProcess)) fail('unable to start pgsql');
750 // TODO: error checking
751 while (!feof($ahPipes[1])) {
752 echo fread($ahPipes[1], 4096);
756 $iReturn = proc_close($hProcess);
758 fail("pgsql returned with error code ($iReturn)");
761 fclose($ahGzipPipes[1]);
762 proc_close($hGzipProcess);
766 function pgsqlRunScript($sScript, $bfatal = true)
772 $aCMDResult['verbose'],
773 $aCMDResult['ignore-errors']
777 function pgsqlRunPartitionScript($sTemplate)
782 $sSQL = 'select distinct partition from country_name';
783 $aPartitions = chksql($oDB->getCol($sSQL));
784 if (!$aCMDResult['no-partitions']) $aPartitions[] = 0;
786 preg_match_all('#^-- start(.*?)^-- end#ms', $sTemplate, $aMatches, PREG_SET_ORDER);
787 foreach ($aMatches as $aMatch) {
789 foreach ($aPartitions as $sPartitionName) {
790 $sResult .= str_replace('-partition-', $sPartitionName, $aMatch[1]);
792 $sTemplate = str_replace($aMatch[0], $sResult, $sTemplate);
795 pgsqlRunScript($sTemplate);
798 function pgsqlRunRestoreData($sDumpFile)
800 // Convert database DSN to psql parameters
801 $aDSNInfo = DB::parseDSN(CONST_Database_DSN);
802 if (!isset($aDSNInfo['port']) || !$aDSNInfo['port']) $aDSNInfo['port'] = 5432;
803 $sCMD = 'pg_restore -p '.$aDSNInfo['port'].' -d '.$aDSNInfo['database'].' -Fc -a '.$sDumpFile;
805 $aDescriptors = array(
806 0 => array('pipe', 'r'),
807 1 => array('pipe', 'w'),
808 2 => array('file', '/dev/null', 'a')
811 $hProcess = proc_open($sCMD, $aDescriptors, $ahPipes);
812 if (!is_resource($hProcess)) fail('unable to start pg_restore');
816 // TODO: error checking
817 while (!feof($ahPipes[1])) {
818 echo fread($ahPipes[1], 4096);
822 $iReturn = proc_close($hProcess);
825 function pgsqlRunDropAndRestore($sDumpFile)
827 // Convert database DSN to psql parameters
828 $aDSNInfo = DB::parseDSN(CONST_Database_DSN);
829 if (!isset($aDSNInfo['port']) || !$aDSNInfo['port']) $aDSNInfo['port'] = 5432;
830 $sCMD = 'pg_restore -p '.$aDSNInfo['port'].' -d '.$aDSNInfo['database'].' -Fc --clean '.$sDumpFile;
832 $aDescriptors = array(
833 0 => array('pipe', 'r'),
834 1 => array('pipe', 'w'),
835 2 => array('file', '/dev/null', 'a')
838 $hProcess = proc_open($sCMD, $aDescriptors, $ahPipes);
839 if (!is_resource($hProcess)) fail('unable to start pg_restore');
843 // TODO: error checking
844 while (!feof($ahPipes[1])) {
845 echo fread($ahPipes[1], 4096);
849 $iReturn = proc_close($hProcess);
852 function passthruCheckReturn($cmd)
855 passthru($cmd, $result);
856 if ($result != 0) fail('Error executing external command: '.$cmd);
859 function replace_tablespace($sTemplate, $sTablespace, $sSql)
862 $sSql = str_replace($sTemplate, 'TABLESPACE "'.$sTablespace.'"', $sSql);
864 $sSql = str_replace($sTemplate, '', $sSql);
870 function create_sql_functions($aCMDResult)
872 $sTemplate = file_get_contents(CONST_BasePath.'/sql/functions.sql');
873 $sTemplate = str_replace('{modulepath}', CONST_InstallPath.'/module', $sTemplate);
874 if ($aCMDResult['enable-diff-updates']) {
875 $sTemplate = str_replace('RETURN NEW; -- %DIFFUPDATES%', '--', $sTemplate);
877 if ($aCMDResult['enable-debug-statements']) {
878 $sTemplate = str_replace('--DEBUG:', '', $sTemplate);
880 if (CONST_Limit_Reindexing) {
881 $sTemplate = str_replace('--LIMIT INDEXING:', '', $sTemplate);
883 if (!CONST_Use_US_Tiger_Data) {
884 $sTemplate = str_replace('-- %NOTIGERDATA% ', '', $sTemplate);
886 if (!CONST_Use_Aux_Location_data) {
887 $sTemplate = str_replace('-- %NOAUXDATA% ', '', $sTemplate);
889 pgsqlRunScript($sTemplate);