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')";
545 $sSQL .= "TRUNCATE TABLE us_postcode";
547 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 = replace_tablespace(
603 '{ts:address-index}',
604 CONST_Tablespace_Address_Index,
607 $sTemplate = replace_tablespace(
609 CONST_Tablespace_Search_Index,
612 $sTemplate = replace_tablespace(
614 CONST_Tablespace_Aux_Index,
618 pgsqlRunScript($sTemplate);
621 if ($aCMDResult['create-country-names'] || $aCMDResult['all']) {
622 info('Create search index for default country names');
623 $bDidSomething = true;
625 pgsqlRunScript("select getorcreate_country(make_standard_name('uk'), 'gb')");
626 pgsqlRunScript("select getorcreate_country(make_standard_name('united states'), 'us')");
627 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");
628 pgsqlRunScript("select count(*) from (select getorcreate_country(make_standard_name(name->'name'), country_code) from country_name where name ? 'name') as x");
630 $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 ';
631 if (CONST_Languages) {
634 foreach (explode(',', CONST_Languages) as $sLang) {
635 $sSQL .= $sDelim."'name:$sLang'";
640 // all include all simple name tags
641 $sSQL .= "like 'name:%'";
644 pgsqlRunScript($sSQL);
647 if ($aCMDResult['drop']) {
648 info('Drop tables only required for updates');
649 // The implementation is potentially a bit dangerous because it uses
650 // a positive selection of tables to keep, and deletes everything else.
651 // Including any tables that the unsuspecting user might have manually
652 // created. USE AT YOUR OWN PERIL.
653 $bDidSomething = true;
655 // tables we want to keep. everything else goes.
656 $aKeepTables = array(
661 "location_property*",
675 $aDropTables = array();
676 $aHaveTables = chksql($oDB->getCol("SELECT tablename FROM pg_tables WHERE schemaname='public'"));
678 foreach ($aHaveTables as $sTable) {
680 foreach ($aKeepTables as $sKeep) {
681 if (fnmatch($sKeep, $sTable)) {
686 if (!$bFound) array_push($aDropTables, $sTable);
689 foreach ($aDropTables as $sDrop) {
690 if ($aCMDResult['verbose']) echo "dropping table $sDrop\n";
691 @pg_query($oDB->connection, "DROP TABLE $sDrop CASCADE");
692 // ignore warnings/errors as they might be caused by a table having
693 // been deleted already by CASCADE
696 if (!is_null(CONST_Osm2pgsql_Flatnode_File)) {
697 if ($aCMDResult['verbose']) echo "deleting ".CONST_Osm2pgsql_Flatnode_File."\n";
698 unlink(CONST_Osm2pgsql_Flatnode_File);
702 if (!$bDidSomething) {
703 showUsage($aCMDOptions, true);
705 echo "Summary of warnings:\n\n";
708 info('Setup finished.');
712 function pgsqlRunScriptFile($sFilename)
715 if (!file_exists($sFilename)) fail('unable to find '.$sFilename);
717 // Convert database DSN to psql parameters
718 $aDSNInfo = DB::parseDSN(CONST_Database_DSN);
719 if (!isset($aDSNInfo['port']) || !$aDSNInfo['port']) $aDSNInfo['port'] = 5432;
720 $sCMD = 'psql -p '.$aDSNInfo['port'].' -d '.$aDSNInfo['database'];
721 if (!$aCMDResult['verbose']) {
726 if (preg_match('/\\.gz$/', $sFilename)) {
727 $aDescriptors = array(
728 0 => array('pipe', 'r'),
729 1 => array('pipe', 'w'),
730 2 => array('file', '/dev/null', 'a')
732 $hGzipProcess = proc_open('zcat '.$sFilename, $aDescriptors, $ahGzipPipes);
733 if (!is_resource($hGzipProcess)) fail('unable to start zcat');
734 $aReadPipe = $ahGzipPipes[1];
735 fclose($ahGzipPipes[0]);
737 $sCMD .= ' -f '.$sFilename;
738 $aReadPipe = array('pipe', 'r');
741 $aDescriptors = array(
743 1 => array('pipe', 'w'),
744 2 => array('file', '/dev/null', 'a')
747 $hProcess = proc_open($sCMD, $aDescriptors, $ahPipes);
748 if (!is_resource($hProcess)) fail('unable to start pgsql');
751 // TODO: error checking
752 while (!feof($ahPipes[1])) {
753 echo fread($ahPipes[1], 4096);
757 $iReturn = proc_close($hProcess);
759 fail("pgsql returned with error code ($iReturn)");
762 fclose($ahGzipPipes[1]);
763 proc_close($hGzipProcess);
767 function pgsqlRunScript($sScript, $bfatal = true)
773 $aCMDResult['verbose'],
774 $aCMDResult['ignore-errors']
778 function pgsqlRunPartitionScript($sTemplate)
783 $sSQL = 'select distinct partition from country_name';
784 $aPartitions = chksql($oDB->getCol($sSQL));
785 if (!$aCMDResult['no-partitions']) $aPartitions[] = 0;
787 preg_match_all('#^-- start(.*?)^-- end#ms', $sTemplate, $aMatches, PREG_SET_ORDER);
788 foreach ($aMatches as $aMatch) {
790 foreach ($aPartitions as $sPartitionName) {
791 $sResult .= str_replace('-partition-', $sPartitionName, $aMatch[1]);
793 $sTemplate = str_replace($aMatch[0], $sResult, $sTemplate);
796 pgsqlRunScript($sTemplate);
799 function pgsqlRunRestoreData($sDumpFile)
801 // Convert database DSN to psql parameters
802 $aDSNInfo = DB::parseDSN(CONST_Database_DSN);
803 if (!isset($aDSNInfo['port']) || !$aDSNInfo['port']) $aDSNInfo['port'] = 5432;
804 $sCMD = 'pg_restore -p '.$aDSNInfo['port'].' -d '.$aDSNInfo['database'].' -Fc -a '.$sDumpFile;
806 $aDescriptors = array(
807 0 => array('pipe', 'r'),
808 1 => array('pipe', 'w'),
809 2 => array('file', '/dev/null', 'a')
812 $hProcess = proc_open($sCMD, $aDescriptors, $ahPipes);
813 if (!is_resource($hProcess)) fail('unable to start pg_restore');
817 // TODO: error checking
818 while (!feof($ahPipes[1])) {
819 echo fread($ahPipes[1], 4096);
823 $iReturn = proc_close($hProcess);
826 function pgsqlRunDropAndRestore($sDumpFile)
828 // Convert database DSN to psql parameters
829 $aDSNInfo = DB::parseDSN(CONST_Database_DSN);
830 if (!isset($aDSNInfo['port']) || !$aDSNInfo['port']) $aDSNInfo['port'] = 5432;
831 $sCMD = 'pg_restore -p '.$aDSNInfo['port'].' -d '.$aDSNInfo['database'].' -Fc --clean '.$sDumpFile;
833 $aDescriptors = array(
834 0 => array('pipe', 'r'),
835 1 => array('pipe', 'w'),
836 2 => array('file', '/dev/null', 'a')
839 $hProcess = proc_open($sCMD, $aDescriptors, $ahPipes);
840 if (!is_resource($hProcess)) fail('unable to start pg_restore');
844 // TODO: error checking
845 while (!feof($ahPipes[1])) {
846 echo fread($ahPipes[1], 4096);
850 $iReturn = proc_close($hProcess);
853 function passthruCheckReturn($cmd)
856 passthru($cmd, $result);
857 if ($result != 0) fail('Error executing external command: '.$cmd);
860 function replace_tablespace($sTemplate, $sTablespace, $sSql)
863 $sSql = str_replace($sTemplate, 'TABLESPACE "'.$sTablespace.'"', $sSql);
865 $sSql = str_replace($sTemplate, '', $sSql);
871 function create_sql_functions($aCMDResult)
873 $sTemplate = file_get_contents(CONST_BasePath.'/sql/functions.sql');
874 $sTemplate = str_replace('{modulepath}', CONST_InstallPath.'/module', $sTemplate);
875 if ($aCMDResult['enable-diff-updates']) {
876 $sTemplate = str_replace('RETURN NEW; -- %DIFFUPDATES%', '--', $sTemplate);
878 if ($aCMDResult['enable-debug-statements']) {
879 $sTemplate = str_replace('--DEBUG:', '', $sTemplate);
881 if (CONST_Limit_Reindexing) {
882 $sTemplate = str_replace('--LIMIT INDEXING:', '', $sTemplate);
884 if (!CONST_Use_US_Tiger_Data) {
885 $sTemplate = str_replace('-- %NOTIGERDATA% ', '', $sTemplate);
887 if (!CONST_Use_Aux_Location_data) {
888 $sTemplate = str_replace('-- %NOAUXDATA% ', '', $sTemplate);
890 pgsqlRunScript($sTemplate);