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 "ERROR: Web user '".CONST_Database_Web_User."' does not exist. Create it with:\n";
136 echo "\n createuser ".CONST_Database_Web_User."\n\n";
140 if (!file_exists(CONST_ExtraDataPath.'/country_osm_grid.sql.gz')) {
141 echo "Error: you need to download the country_osm_grid first:";
142 echo "\n wget -O ".CONST_ExtraDataPath."/country_osm_grid.sql.gz http://www.nominatim.org/data/country_grid.sql.gz\n";
146 pgsqlRunScriptFile(CONST_BasePath.'/data/country_name.sql');
147 pgsqlRunScriptFile(CONST_BasePath.'/data/country_naturalearthdata.sql');
148 pgsqlRunScriptFile(CONST_BasePath.'/data/country_osm_grid.sql.gz');
149 pgsqlRunScriptFile(CONST_BasePath.'/data/gb_postcode_table.sql');
150 if (file_exists(CONST_BasePath.'/data/gb_postcode_data.sql.gz')) {
151 pgsqlRunScriptFile(CONST_BasePath.'/data/gb_postcode_data.sql.gz');
153 warn('external UK postcode table not found.');
155 if (CONST_Use_Extra_US_Postcodes) {
156 pgsqlRunScriptFile(CONST_BasePath.'/data/us_postcode.sql');
159 if ($aCMDResult['no-partitions']) {
160 pgsqlRunScript('update country_name set partition = 0');
163 // the following will be needed by create_functions later but
164 // is only defined in the subsequently called create_tables.
165 // Create dummies here that will be overwritten by the proper
166 // versions in create-tables.
167 pgsqlRunScript('CREATE TABLE place_boundingbox ()');
168 pgsqlRunScript('create type wikipedia_article_match as ()');
171 if ($aCMDResult['import-data'] || $aCMDResult['all']) {
173 $bDidSomething = true;
175 $osm2pgsql = CONST_Osm2pgsql_Binary;
176 if (!file_exists($osm2pgsql)) {
177 echo "Check CONST_Osm2pgsql_Binary in your local settings file.\n";
178 echo "Normally you should not need to set this manually.\n";
179 fail("osm2pgsql not found in '$osm2pgsql'");
182 if (!is_null(CONST_Osm2pgsql_Flatnode_File)) {
183 $osm2pgsql .= ' --flat-nodes '.CONST_Osm2pgsql_Flatnode_File;
185 if (CONST_Tablespace_Osm2pgsql_Data)
186 $osm2pgsql .= ' --tablespace-slim-data '.CONST_Tablespace_Osm2pgsql_Data;
187 if (CONST_Tablespace_Osm2pgsql_Index)
188 $osm2pgsql .= ' --tablespace-slim-index '.CONST_Tablespace_Osm2pgsql_Index;
189 if (CONST_Tablespace_Place_Data)
190 $osm2pgsql .= ' --tablespace-main-data '.CONST_Tablespace_Place_Data;
191 if (CONST_Tablespace_Place_Index)
192 $osm2pgsql .= ' --tablespace-main-index '.CONST_Tablespace_Place_Index;
193 $osm2pgsql .= ' -lsc -O gazetteer --hstore --number-processes 1';
194 $osm2pgsql .= ' -C '.$iCacheMemory;
195 $osm2pgsql .= ' -P '.$aDSNInfo['port'];
196 $osm2pgsql .= ' -d '.$aDSNInfo['database'].' '.$aCMDResult['osm-file'];
197 passthruCheckReturn($osm2pgsql);
200 if (!$aCMDResult['ignore-errors'] && !chksql($oDB->getRow('select * from place limit 1'))) {
205 if ($aCMDResult['create-functions'] || $aCMDResult['all']) {
206 info('Create Functions');
207 $bDidSomething = true;
208 if (!file_exists(CONST_InstallPath.'/module/nominatim.so')) {
209 fail("nominatim module not built");
211 create_sql_functions($aCMDResult);
214 if ($aCMDResult['create-tables'] || $aCMDResult['all']) {
215 info('Create Tables');
216 $bDidSomething = true;
218 $sTemplate = file_get_contents(CONST_BasePath.'/sql/tables.sql');
219 $sTemplate = str_replace('{www-user}', CONST_Database_Web_User, $sTemplate);
220 $sTemplate = replace_tablespace(
222 CONST_Tablespace_Address_Data,
225 $sTemplate = replace_tablespace(
226 '{ts:address-index}',
227 CONST_Tablespace_Address_Index,
230 $sTemplate = replace_tablespace(
232 CONST_Tablespace_Search_Data,
235 $sTemplate = replace_tablespace(
237 CONST_Tablespace_Search_Index,
240 $sTemplate = replace_tablespace(
242 CONST_Tablespace_Aux_Data,
245 $sTemplate = replace_tablespace(
247 CONST_Tablespace_Aux_Index,
250 pgsqlRunScript($sTemplate, false);
252 // re-run the functions
253 info('Recreate Functions');
254 create_sql_functions($aCMDResult);
257 if ($aCMDResult['create-partition-tables'] || $aCMDResult['all']) {
258 info('Create Partition Tables');
259 $bDidSomething = true;
261 $sTemplate = file_get_contents(CONST_BasePath.'/sql/partition-tables.src.sql');
262 $sTemplate = replace_tablespace(
264 CONST_Tablespace_Address_Data,
267 $sTemplate = replace_tablespace(
268 '{ts:address-index}',
269 CONST_Tablespace_Address_Index,
272 $sTemplate = replace_tablespace(
274 CONST_Tablespace_Search_Data,
277 $sTemplate = replace_tablespace(
279 CONST_Tablespace_Search_Index,
282 $sTemplate = replace_tablespace(
284 CONST_Tablespace_Aux_Data,
287 $sTemplate = replace_tablespace(
289 CONST_Tablespace_Aux_Index,
293 pgsqlRunPartitionScript($sTemplate);
297 if ($aCMDResult['create-partition-functions'] || $aCMDResult['all']) {
298 info('Create Partition Functions');
299 $bDidSomething = true;
301 $sTemplate = file_get_contents(CONST_BasePath.'/sql/partition-functions.src.sql');
303 pgsqlRunPartitionScript($sTemplate);
306 if ($aCMDResult['import-wikipedia-articles'] || $aCMDResult['all']) {
307 $bDidSomething = true;
308 $sWikiArticlesFile = CONST_Wikipedia_Data_Path.'/wikipedia_article.sql.bin';
309 $sWikiRedirectsFile = CONST_Wikipedia_Data_Path.'/wikipedia_redirect.sql.bin';
310 if (file_exists($sWikiArticlesFile)) {
311 info('Importing wikipedia articles');
312 pgsqlRunDropAndRestore($sWikiArticlesFile);
314 warn('wikipedia article dump file not found - places will have default importance');
316 if (file_exists($sWikiRedirectsFile)) {
317 info('Importing wikipedia redirects');
318 pgsqlRunDropAndRestore($sWikiRedirectsFile);
320 warn('wikipedia redirect dump file not found - some place importance values may be missing');
325 if ($aCMDResult['load-data'] || $aCMDResult['all']) {
326 info('Drop old Data');
327 $bDidSomething = true;
330 if (!pg_query($oDB->connection, 'TRUNCATE word')) fail(pg_last_error($oDB->connection));
332 if (!pg_query($oDB->connection, 'TRUNCATE placex')) fail(pg_last_error($oDB->connection));
334 if (!pg_query($oDB->connection, 'TRUNCATE location_property_osmline')) fail(pg_last_error($oDB->connection));
336 if (!pg_query($oDB->connection, 'TRUNCATE place_addressline')) fail(pg_last_error($oDB->connection));
338 if (!pg_query($oDB->connection, 'TRUNCATE place_boundingbox')) fail(pg_last_error($oDB->connection));
340 if (!pg_query($oDB->connection, 'TRUNCATE location_area')) fail(pg_last_error($oDB->connection));
342 if (!pg_query($oDB->connection, 'TRUNCATE search_name')) fail(pg_last_error($oDB->connection));
344 if (!pg_query($oDB->connection, 'TRUNCATE search_name_blank')) fail(pg_last_error($oDB->connection));
346 if (!pg_query($oDB->connection, 'DROP SEQUENCE seq_place')) fail(pg_last_error($oDB->connection));
348 if (!pg_query($oDB->connection, 'CREATE SEQUENCE seq_place start 100000')) fail(pg_last_error($oDB->connection));
351 $sSQL = 'select distinct partition from country_name';
352 $aPartitions = chksql($oDB->getCol($sSQL));
353 if (!$aCMDResult['no-partitions']) $aPartitions[] = 0;
354 foreach ($aPartitions as $sPartition) {
355 if (!pg_query($oDB->connection, 'TRUNCATE location_road_'.$sPartition)) fail(pg_last_error($oDB->connection));
359 // used by getorcreate_word_id to ignore frequent partial words
360 $sSQL = 'CREATE OR REPLACE FUNCTION get_maxwordfreq() RETURNS integer AS ';
361 $sSQL .= '$$ SELECT '.CONST_Max_Word_Frequency.' as maxwordfreq; $$ LANGUAGE SQL IMMUTABLE';
362 if (!pg_query($oDB->connection, $sSQL)) {
363 fail(pg_last_error($oDB->connection));
367 // pre-create the word list
368 if (!$aCMDResult['disable-token-precalc']) {
369 info('Loading word list');
370 pgsqlRunScriptFile(CONST_BasePath.'/data/words.sql');
374 $sColumns = 'osm_type, osm_id, class, type, name, admin_level, address, extratags, geometry';
376 $aDBInstances = array();
377 $iLoadThreads = max(1, $iInstances - 1);
378 for ($i = 0; $i < $iLoadThreads; $i++) {
379 $aDBInstances[$i] =& getDB(true);
380 $sSQL = "INSERT INTO placex ($sColumns) SELECT $sColumns FROM place WHERE osm_id % $iLoadThreads = $i";
381 $sSQL .= " and not (class='place' and type='houses' and osm_type='W'";
382 $sSQL .= " and ST_GeometryType(geometry) = 'ST_LineString')";
383 $sSQL .= " and ST_IsValid(geometry)";
384 if ($aCMDResult['verbose']) echo "$sSQL\n";
385 if (!pg_send_query($aDBInstances[$i]->connection, $sSQL)) {
386 fail(pg_last_error($aDBInstances[$i]->connection));
389 // last thread for interpolation lines
390 $aDBInstances[$iLoadThreads] =& getDB(true);
391 $sSQL = 'insert into location_property_osmline';
392 $sSQL .= ' (osm_id, address, linegeo)';
393 $sSQL .= ' SELECT osm_id, address, geometry from place where ';
394 $sSQL .= "class='place' and type='houses' and osm_type='W' and ST_GeometryType(geometry) = 'ST_LineString'";
395 if ($aCMDResult['verbose']) echo "$sSQL\n";
396 if (!pg_send_query($aDBInstances[$iLoadThreads]->connection, $sSQL)) {
397 fail(pg_last_error($aDBInstances[$iLoadThreads]->connection));
403 for ($i = 0; $i <= $iLoadThreads; $i++) {
404 if (pg_connection_busy($aDBInstances[$i]->connection)) $bAnyBusy = true;
410 info('Reanalysing database');
411 pgsqlRunScript('ANALYSE');
413 $sDatabaseDate = getDatabaseDate($oDB);
414 pg_query($oDB->connection, 'TRUNCATE import_status');
415 if ($sDatabaseDate === false) {
416 warn('could not determine database date.');
418 $sSQL = "INSERT INTO import_status (lastimportdate) VALUES('".$sDatabaseDate."')";
419 pg_query($oDB->connection, $sSQL);
420 echo "Latest data imported from $sDatabaseDate.\n";
424 if ($aCMDResult['import-tiger-data']) {
425 info('Import Tiger data');
426 $bDidSomething = true;
428 $sTemplate = file_get_contents(CONST_BasePath.'/sql/tiger_import_start.sql');
429 $sTemplate = str_replace('{www-user}', CONST_Database_Web_User, $sTemplate);
430 $sTemplate = replace_tablespace(
432 CONST_Tablespace_Aux_Data,
435 $sTemplate = replace_tablespace(
437 CONST_Tablespace_Aux_Index,
440 pgsqlRunScript($sTemplate, false);
442 $aDBInstances = array();
443 for ($i = 0; $i < $iInstances; $i++) {
444 $aDBInstances[$i] =& getDB(true);
447 foreach (glob(CONST_Tiger_Data_Path.'/*.sql') as $sFile) {
449 $hFile = fopen($sFile, "r");
450 $sSQL = fgets($hFile, 100000);
454 for ($i = 0; $i < $iInstances; $i++) {
455 if (!pg_connection_busy($aDBInstances[$i]->connection)) {
456 while (pg_get_result($aDBInstances[$i]->connection));
457 $sSQL = fgets($hFile, 100000);
459 if (!pg_send_query($aDBInstances[$i]->connection, $sSQL)) fail(pg_last_error($oDB->connection));
461 if ($iLines == 1000) {
475 for ($i = 0; $i < $iInstances; $i++) {
476 if (pg_connection_busy($aDBInstances[$i]->connection)) $bAnyBusy = true;
483 info('Creating indexes on Tiger data');
484 $sTemplate = file_get_contents(CONST_BasePath.'/sql/tiger_import_finish.sql');
485 $sTemplate = str_replace('{www-user}', CONST_Database_Web_User, $sTemplate);
486 $sTemplate = replace_tablespace(
488 CONST_Tablespace_Aux_Data,
491 $sTemplate = replace_tablespace(
493 CONST_Tablespace_Aux_Index,
496 pgsqlRunScript($sTemplate, false);
499 if ($aCMDResult['calculate-postcodes'] || $aCMDResult['all']) {
500 info('Calculate Postcodes');
501 $bDidSomething = true;
503 if (!pg_query($oDB->connection, 'TRUNCATE location_postcode')) {
504 fail(pg_last_error($oDB->connection));
507 $sSQL = "INSERT INTO location_postcode";
508 $sSQL .= " (place_id, indexed_status, country_code, postcode, geometry) ";
509 $sSQL .= "SELECT nextval('seq_place'), 1, country_code,";
510 $sSQL .= " upper(trim (both ' ' from address->'postcode')) as pc,";
511 $sSQL .= " ST_Centroid(ST_Collect(ST_Centroid(geometry)))";
512 $sSQL .= " FROM placex";
513 $sSQL .= " WHERE address ? 'postcode' AND address->'postcode' NOT SIMILAR TO '%(,|;)%'";
514 $sSQL .= " AND geometry IS NOT null";
515 $sSQL .= " GROUP BY country_code, pc";
517 if (!pg_query($oDB->connection, $sSQL)) {
518 fail(pg_last_error($oDB->connection));
521 if (CONST_Use_Extra_US_Postcodes) {
522 // only add postcodes that are not yet available in OSM
523 $sSQL = "INSERT INTO location_postcode";
524 $sSQL .= " (place_id, indexed_status, country_code, postcode, geometry) ";
525 $sSQL .= "SELECT nextval('seq_place'), 1, 'us', postcode,";
526 $sSQL .= " ST_SetSRID(ST_Point(x,y),4326)";
527 $sSQL .= " FROM us_postcode WHERE postcode NOT IN";
528 $sSQL .= " (SELECT postcode FROM location_postcode";
529 $sSQL .= " WHERE country_code = 'us')";
531 if (!pg_query($oDB->connection, $sSQL)) fail(pg_last_error($oDB->connection));
534 // add missing postcodes for GB (if available)
535 $sSQL = "INSERT INTO location_postcode";
536 $sSQL .= " (place_id, indexed_status, country_code, postcode, geometry) ";
537 $sSQL .= "SELECT nextval('seq_place'), 1, 'gb', postcode, geometry";
538 $sSQL .= " FROM gb_postcode WHERE postcode NOT IN";
539 $sSQL .= " (SELECT postcode FROM location_postcode";
540 $sSQL .= " WHERE country_code = 'gb')";
541 if (!pg_query($oDB->connection, $sSQL)) fail(pg_last_error($oDB->connection));
543 if (!$aCMDResult['all']) {
544 $sSQL = "DELETE FROM word WHERE class='place' and type='postcode'";
545 $sSQL .= "and word NOT IN (SELECT postcode FROM location_postcode)";
546 if (!pg_query($oDB->connection, $sSQL)) {
547 fail(pg_last_error($oDB->connection));
550 $sSQL = "SELECT count(getorcreate_postcode_id(v)) FROM ";
551 $sSQL .= "(SELECT distinct(postcode) as v FROM location_postcode) p";
553 if (!pg_query($oDB->connection, $sSQL)) {
554 fail(pg_last_error($oDB->connection));
558 if ($aCMDResult['osmosis-init']) {
559 $bDidSomething = true;
560 echo "Command 'osmosis-init' no longer available, please use utils/update.php --init-updates.\n";
563 if ($aCMDResult['index'] || $aCMDResult['all']) {
564 $bDidSomething = true;
566 $sBaseCmd = CONST_InstallPath.'/nominatim/nominatim -i -d '.$aDSNInfo['database'].' -P '.$aDSNInfo['port'].' -t '.$iInstances.$sOutputFile;
567 info('Index ranks 0 - 4');
568 passthruCheckReturn($sBaseCmd.' -R 4');
569 if (!$aCMDResult['index-noanalyse']) pgsqlRunScript('ANALYSE');
570 info('Index ranks 5 - 25');
571 passthruCheckReturn($sBaseCmd.' -r 5 -R 25');
572 if (!$aCMDResult['index-noanalyse']) pgsqlRunScript('ANALYSE');
573 info('Index ranks 26 - 30');
574 passthruCheckReturn($sBaseCmd.' -r 26');
576 info('Index postcodes');
578 $sSQL = 'UPDATE location_postcode SET indexed_status = 0';
579 if (!pg_query($oDB->connection, $sSQL)) fail(pg_last_error($oDB->connection));
582 if ($aCMDResult['create-search-indices'] || $aCMDResult['all']) {
583 info('Create Search indices');
584 $bDidSomething = true;
586 $sTemplate = file_get_contents(CONST_BasePath.'/sql/indices.src.sql');
587 $sTemplate = replace_tablespace(
588 '{ts:address-index}',
589 CONST_Tablespace_Address_Index,
592 $sTemplate = replace_tablespace(
594 CONST_Tablespace_Search_Index,
597 $sTemplate = replace_tablespace(
599 CONST_Tablespace_Aux_Index,
603 pgsqlRunScript($sTemplate);
606 if ($aCMDResult['create-country-names'] || $aCMDResult['all']) {
607 info('Create search index for default country names');
608 $bDidSomething = true;
610 pgsqlRunScript("select getorcreate_country(make_standard_name('uk'), 'gb')");
611 pgsqlRunScript("select getorcreate_country(make_standard_name('united states'), 'us')");
612 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");
613 pgsqlRunScript("select count(*) from (select getorcreate_country(make_standard_name(name->'name'), country_code) from country_name where name ? 'name') as x");
615 $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 ';
616 if (CONST_Languages) {
619 foreach (explode(',', CONST_Languages) as $sLang) {
620 $sSQL .= $sDelim."'name:$sLang'";
625 // all include all simple name tags
626 $sSQL .= "like 'name:%'";
629 pgsqlRunScript($sSQL);
632 if ($aCMDResult['drop']) {
633 info('Drop tables only required for updates');
634 // The implementation is potentially a bit dangerous because it uses
635 // a positive selection of tables to keep, and deletes everything else.
636 // Including any tables that the unsuspecting user might have manually
637 // created. USE AT YOUR OWN PERIL.
638 $bDidSomething = true;
640 // tables we want to keep. everything else goes.
641 $aKeepTables = array(
646 "location_property*",
660 $aDropTables = array();
661 $aHaveTables = chksql($oDB->getCol("SELECT tablename FROM pg_tables WHERE schemaname='public'"));
663 foreach ($aHaveTables as $sTable) {
665 foreach ($aKeepTables as $sKeep) {
666 if (fnmatch($sKeep, $sTable)) {
671 if (!$bFound) array_push($aDropTables, $sTable);
674 foreach ($aDropTables as $sDrop) {
675 if ($aCMDResult['verbose']) echo "dropping table $sDrop\n";
676 @pg_query($oDB->connection, "DROP TABLE $sDrop CASCADE");
677 // ignore warnings/errors as they might be caused by a table having
678 // been deleted already by CASCADE
681 if (!is_null(CONST_Osm2pgsql_Flatnode_File)) {
682 if ($aCMDResult['verbose']) echo "deleting ".CONST_Osm2pgsql_Flatnode_File."\n";
683 unlink(CONST_Osm2pgsql_Flatnode_File);
687 if (!$bDidSomething) {
688 showUsage($aCMDOptions, true);
690 echo "Summary of warnings:\n\n";
693 info('Setup finished.');
697 function pgsqlRunScriptFile($sFilename)
700 if (!file_exists($sFilename)) fail('unable to find '.$sFilename);
702 // Convert database DSN to psql parameters
703 $aDSNInfo = DB::parseDSN(CONST_Database_DSN);
704 if (!isset($aDSNInfo['port']) || !$aDSNInfo['port']) $aDSNInfo['port'] = 5432;
705 $sCMD = 'psql -p '.$aDSNInfo['port'].' -d '.$aDSNInfo['database'];
706 if (!$aCMDResult['verbose']) {
711 if (preg_match('/\\.gz$/', $sFilename)) {
712 $aDescriptors = array(
713 0 => array('pipe', 'r'),
714 1 => array('pipe', 'w'),
715 2 => array('file', '/dev/null', 'a')
717 $hGzipProcess = proc_open('zcat '.$sFilename, $aDescriptors, $ahGzipPipes);
718 if (!is_resource($hGzipProcess)) fail('unable to start zcat');
719 $aReadPipe = $ahGzipPipes[1];
720 fclose($ahGzipPipes[0]);
722 $sCMD .= ' -f '.$sFilename;
723 $aReadPipe = array('pipe', 'r');
726 $aDescriptors = array(
728 1 => array('pipe', 'w'),
729 2 => array('file', '/dev/null', 'a')
732 $hProcess = proc_open($sCMD, $aDescriptors, $ahPipes);
733 if (!is_resource($hProcess)) fail('unable to start pgsql');
736 // TODO: error checking
737 while (!feof($ahPipes[1])) {
738 echo fread($ahPipes[1], 4096);
742 $iReturn = proc_close($hProcess);
744 fail("pgsql returned with error code ($iReturn)");
747 fclose($ahGzipPipes[1]);
748 proc_close($hGzipProcess);
752 function pgsqlRunScript($sScript, $bfatal = true)
755 // Convert database DSN to psql parameters
756 $aDSNInfo = DB::parseDSN(CONST_Database_DSN);
757 if (!isset($aDSNInfo['port']) || !$aDSNInfo['port']) $aDSNInfo['port'] = 5432;
758 $sCMD = 'psql -p '.$aDSNInfo['port'].' -d '.$aDSNInfo['database'];
759 if (!$aCMDResult['verbose']) {
762 if ($bfatal && !$aCMDResult['ignore-errors'])
763 $sCMD .= ' -v ON_ERROR_STOP=1';
764 $aDescriptors = array(
765 0 => array('pipe', 'r'),
770 $hProcess = @proc_open($sCMD, $aDescriptors, $ahPipes);
771 if (!is_resource($hProcess)) fail('unable to start pgsql');
773 while (strlen($sScript)) {
774 $written = fwrite($ahPipes[0], $sScript);
775 if ($written <= 0) break;
776 $sScript = substr($sScript, $written);
779 $iReturn = proc_close($hProcess);
780 if ($bfatal && $iReturn > 0) {
781 fail("pgsql returned with error code ($iReturn)");
785 function pgsqlRunPartitionScript($sTemplate)
790 $sSQL = 'select distinct partition from country_name';
791 $aPartitions = chksql($oDB->getCol($sSQL));
792 if (!$aCMDResult['no-partitions']) $aPartitions[] = 0;
794 preg_match_all('#^-- start(.*?)^-- end#ms', $sTemplate, $aMatches, PREG_SET_ORDER);
795 foreach ($aMatches as $aMatch) {
797 foreach ($aPartitions as $sPartitionName) {
798 $sResult .= str_replace('-partition-', $sPartitionName, $aMatch[1]);
800 $sTemplate = str_replace($aMatch[0], $sResult, $sTemplate);
803 pgsqlRunScript($sTemplate);
806 function pgsqlRunRestoreData($sDumpFile)
808 // Convert database DSN to psql parameters
809 $aDSNInfo = DB::parseDSN(CONST_Database_DSN);
810 if (!isset($aDSNInfo['port']) || !$aDSNInfo['port']) $aDSNInfo['port'] = 5432;
811 $sCMD = 'pg_restore -p '.$aDSNInfo['port'].' -d '.$aDSNInfo['database'].' -Fc -a '.$sDumpFile;
813 $aDescriptors = array(
814 0 => array('pipe', 'r'),
815 1 => array('pipe', 'w'),
816 2 => array('file', '/dev/null', 'a')
819 $hProcess = proc_open($sCMD, $aDescriptors, $ahPipes);
820 if (!is_resource($hProcess)) fail('unable to start pg_restore');
824 // TODO: error checking
825 while (!feof($ahPipes[1])) {
826 echo fread($ahPipes[1], 4096);
830 $iReturn = proc_close($hProcess);
833 function pgsqlRunDropAndRestore($sDumpFile)
835 // Convert database DSN to psql parameters
836 $aDSNInfo = DB::parseDSN(CONST_Database_DSN);
837 if (!isset($aDSNInfo['port']) || !$aDSNInfo['port']) $aDSNInfo['port'] = 5432;
838 $sCMD = 'pg_restore -p '.$aDSNInfo['port'].' -d '.$aDSNInfo['database'].' -Fc --clean '.$sDumpFile;
840 $aDescriptors = array(
841 0 => array('pipe', 'r'),
842 1 => array('pipe', 'w'),
843 2 => array('file', '/dev/null', 'a')
846 $hProcess = proc_open($sCMD, $aDescriptors, $ahPipes);
847 if (!is_resource($hProcess)) fail('unable to start pg_restore');
851 // TODO: error checking
852 while (!feof($ahPipes[1])) {
853 echo fread($ahPipes[1], 4096);
857 $iReturn = proc_close($hProcess);
860 function passthruCheckReturn($cmd)
863 passthru($cmd, $result);
864 if ($result != 0) fail('Error executing external command: '.$cmd);
867 function replace_tablespace($sTemplate, $sTablespace, $sSql)
870 $sSql = str_replace($sTemplate, 'TABLESPACE "'.$sTablespace.'"', $sSql);
872 $sSql = str_replace($sTemplate, '', $sSql);
878 function create_sql_functions($aCMDResult)
880 $sTemplate = file_get_contents(CONST_BasePath.'/sql/functions.sql');
881 $sTemplate = str_replace('{modulepath}', CONST_InstallPath.'/module', $sTemplate);
882 if ($aCMDResult['enable-diff-updates']) {
883 $sTemplate = str_replace('RETURN NEW; -- %DIFFUPDATES%', '--', $sTemplate);
885 if ($aCMDResult['enable-debug-statements']) {
886 $sTemplate = str_replace('--DEBUG:', '', $sTemplate);
888 if (CONST_Limit_Reindexing) {
889 $sTemplate = str_replace('--LIMIT INDEXING:', '', $sTemplate);
891 if (!CONST_Use_US_Tiger_Data) {
892 $sTemplate = str_replace('-- %NOTIGERDATA% ', '', $sTemplate);
894 if (!CONST_Use_Aux_Location_data) {
895 $sTemplate = str_replace('-- %NOAUXDATA% ', '', $sTemplate);
897 pgsqlRunScript($sTemplate);