]> git.openstreetmap.org Git - nominatim.git/blob - utils/setup.php
replace deprecated postgis functions
[nominatim.git] / utils / setup.php
1 #!/usr/bin/php -Cq
2 <?php
3
4 require_once(dirname(dirname(__FILE__)).'/settings/settings.php');
5 require_once(CONST_BasePath.'/lib/init-cmd.php');
6 ini_set('memory_limit', '800M');
7
8 $aCMDOptions
9 = array(
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'),
14
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)'),
17
18    array('all', '', 0, 1, 0, 0, 'bool', 'Do the complete process'),
19
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)'),
43   );
44 getCmdOpt($_SERVER['argv'], $aCMDOptions, $aCMDResult, true, true);
45
46 $bDidSomething = false;
47
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');
52     }
53
54     if (!file_exists($aCMDResult['osm-file'])) {
55         fail('the path supplied to --osm-file does not exist');
56     }
57
58     if (!is_readable($aCMDResult['osm-file'])) {
59         fail('osm-file "'.$aCMDResult['osm-file'].'" not readable');
60     }
61 }
62
63
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) {
67     $iInstances = 1;
68     echo "WARNING: resetting threads to $iInstances\n";
69 }
70 if ($iInstances > getProcessorCount()) {
71     $iInstances = getProcessorCount();
72     echo "WARNING: resetting threads to $iInstances\n";
73 }
74
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'];
78 } else {
79     $iCacheMemory = getCacheMemoryMB();
80 }
81
82 $aDSNInfo = DB::parseDSN(CONST_Database_DSN);
83 if (!isset($aDSNInfo['port']) || !$aDSNInfo['port']) $aDSNInfo['port'] = 5432;
84
85 if ($aCMDResult['create-db'] || $aCMDResult['all']) {
86     echo "Create DB\n";
87     $bDidSomething = true;
88     $oDB = DB::connect(CONST_Database_DSN, false);
89     if (!PEAR::isError($oDB)) {
90         fail('database already exists ('.CONST_Database_DSN.')');
91     }
92     passthruCheckReturn('createdb -E UTF-8 -p '.$aDSNInfo['port'].' '.$aDSNInfo['database']);
93 }
94
95 if ($aCMDResult['setup-db'] || $aCMDResult['all']) {
96     echo "Setup DB\n";
97     $bDidSomething = true;
98
99     // TODO: path detection, detection memory, etc.
100     //
101     $oDB =& getDB();
102
103     $fPostgresVersion = getPostgresVersion($oDB);
104     echo 'Postgres version found: '.$fPostgresVersion."\n";
105
106     if ($fPostgresVersion < 9.1) {
107         fail("Minimum supported version of Postgresql is 9.1.");
108     }
109
110     pgsqlRunScript('CREATE EXTENSION IF NOT EXISTS hstore');
111     pgsqlRunScript('CREATE EXTENSION IF NOT EXISTS postgis');
112
113     // For extratags and namedetails the hstore_to_json converter is
114     // needed which is only available from Postgresql 9.3+. For older
115     // versions add a dummy function that returns nothing.
116     $iNumFunc = chksql($oDB->getOne("select count(*) from pg_proc where proname = 'hstore_to_json'"));
117
118     if ($iNumFunc == 0) {
119         pgsqlRunScript("create function hstore_to_json(dummy hstore) returns text AS 'select null::text' language sql immutable");
120         echo "WARNING: Postgresql is too old. extratags and namedetails API not available.";
121     }
122
123     $fPostgisVersion = getPostgisVersion($oDB);
124     echo 'Postgis version found: '.$fPostgisVersion."\n";
125
126     if ($fPostgisVersion < 2.1) {
127         // Functions were renamed in 2.1 and throw an annoying deprecation warning
128         pgsqlRunScript('ALTER FUNCTION st_line_interpolate_point(geometry, double precision) RENAME TO ST_LineInterpolatePoint');
129         pgsqlRunScript('ALTER FUNCTION ST_Line_Locate_Point(geometry, double precision) RENAME TO ST_LineLocatePoint');
130     }
131     if ($fPostgisVersion < 2.2) {
132         pgsqlRunScript('ALTER FUNCTION ST_Distance_Spheroid(geometry, double precision) RENAME TO ST_DistanceSpheroid');
133     }
134
135     pgsqlRunScriptFile(CONST_BasePath.'/data/country_name.sql');
136     pgsqlRunScriptFile(CONST_BasePath.'/data/country_naturalearthdata.sql');
137     pgsqlRunScriptFile(CONST_BasePath.'/data/country_osm_grid.sql');
138     pgsqlRunScriptFile(CONST_BasePath.'/data/gb_postcode_table.sql');
139     if (file_exists(CONST_BasePath.'/data/gb_postcode_data.sql.gz')) {
140         pgsqlRunScriptFile(CONST_BasePath.'/data/gb_postcode_data.sql.gz');
141     } else {
142         echo "WARNING: external UK postcode table not found.\n";
143     }
144     if (CONST_Use_Extra_US_Postcodes) {
145         pgsqlRunScriptFile(CONST_BasePath.'/data/us_postcode.sql');
146     }
147
148     if ($aCMDResult['no-partitions']) {
149         pgsqlRunScript('update country_name set partition = 0');
150     }
151
152     // the following will be needed by create_functions later but
153     // is only defined in the subsequently called create_tables.
154     // Create dummies here that will be overwritten by the proper
155     // versions in create-tables.
156     pgsqlRunScript('CREATE TABLE place_boundingbox ()');
157     pgsqlRunScript('create type wikipedia_article_match as ()');
158 }
159
160 if ($aCMDResult['import-data'] || $aCMDResult['all']) {
161     echo "Import\n";
162     $bDidSomething = true;
163
164     $osm2pgsql = CONST_Osm2pgsql_Binary;
165     if (!file_exists($osm2pgsql)) {
166         echo "Please download and build osm2pgsql.\nIf it is already installed, check the path in your local settings (settings/local.php) file.\n";
167         fail("osm2pgsql not found in '$osm2pgsql'");
168     }
169
170     if (!is_null(CONST_Osm2pgsql_Flatnode_File)) {
171         $osm2pgsql .= ' --flat-nodes '.CONST_Osm2pgsql_Flatnode_File;
172     }
173     if (CONST_Tablespace_Osm2pgsql_Data)
174         $osm2pgsql .= ' --tablespace-slim-data '.CONST_Tablespace_Osm2pgsql_Data;
175     if (CONST_Tablespace_Osm2pgsql_Index)
176         $osm2pgsql .= ' --tablespace-slim-index '.CONST_Tablespace_Osm2pgsql_Index;
177     if (CONST_Tablespace_Place_Data)
178         $osm2pgsql .= ' --tablespace-main-data '.CONST_Tablespace_Place_Data;
179     if (CONST_Tablespace_Place_Index)
180         $osm2pgsql .= ' --tablespace-main-index '.CONST_Tablespace_Place_Index;
181     $osm2pgsql .= ' -lsc -O gazetteer --hstore --number-processes 1';
182     $osm2pgsql .= ' -C '.$iCacheMemory;
183     $osm2pgsql .= ' -P '.$aDSNInfo['port'];
184     $osm2pgsql .= ' -d '.$aDSNInfo['database'].' '.$aCMDResult['osm-file'];
185     passthruCheckReturn($osm2pgsql);
186
187     $oDB =& getDB();
188     if (!chksql($oDB->getRow('select * from place limit 1'))) {
189         fail('No Data');
190     }
191 }
192
193 if ($aCMDResult['create-functions'] || $aCMDResult['all']) {
194     echo "Functions\n";
195     $bDidSomething = true;
196     if (!file_exists(CONST_InstallPath.'/module/nominatim.so')) fail("nominatim module not built");
197     create_sql_functions($aCMDResult);
198 }
199
200 if ($aCMDResult['create-tables'] || $aCMDResult['all']) {
201     $bDidSomething = true;
202
203     echo "Tables\n";
204     $sTemplate = file_get_contents(CONST_BasePath.'/sql/tables.sql');
205     $sTemplate = str_replace('{www-user}', CONST_Database_Web_User, $sTemplate);
206     $sTemplate = replace_tablespace(
207         '{ts:address-data}',
208         CONST_Tablespace_Address_Data,
209         $sTemplate
210     );
211     $sTemplate = replace_tablespace(
212         '{ts:address-index}',
213         CONST_Tablespace_Address_Index,
214         $sTemplate
215     );
216     $sTemplate = replace_tablespace(
217         '{ts:search-data}',
218         CONST_Tablespace_Search_Data,
219         $sTemplate
220     );
221     $sTemplate = replace_tablespace(
222         '{ts:search-index}',
223         CONST_Tablespace_Search_Index,
224         $sTemplate
225     );
226     $sTemplate = replace_tablespace(
227         '{ts:aux-data}',
228         CONST_Tablespace_Aux_Data,
229         $sTemplate
230     );
231     $sTemplate = replace_tablespace(
232         '{ts:aux-index}',
233         CONST_Tablespace_Aux_Index,
234         $sTemplate
235     );
236     pgsqlRunScript($sTemplate, false);
237
238     // re-run the functions
239     echo "Functions\n";
240     create_sql_functions($aCMDResult);
241 }
242
243 if ($aCMDResult['create-partition-tables'] || $aCMDResult['all']) {
244     echo "Partition Tables\n";
245     $bDidSomething = true;
246
247     $sTemplate = file_get_contents(CONST_BasePath.'/sql/partition-tables.src.sql');
248     $sTemplate = replace_tablespace(
249         '{ts:address-data}',
250         CONST_Tablespace_Address_Data,
251         $sTemplate
252     );
253     $sTemplate = replace_tablespace(
254         '{ts:address-index}',
255         CONST_Tablespace_Address_Index,
256         $sTemplate
257     );
258     $sTemplate = replace_tablespace(
259         '{ts:search-data}',
260         CONST_Tablespace_Search_Data,
261         $sTemplate
262     );
263     $sTemplate = replace_tablespace(
264         '{ts:search-index}',
265         CONST_Tablespace_Search_Index,
266         $sTemplate
267     );
268     $sTemplate = replace_tablespace(
269         '{ts:aux-data}',
270         CONST_Tablespace_Aux_Data,
271         $sTemplate
272     );
273     $sTemplate = replace_tablespace(
274         '{ts:aux-index}',
275         CONST_Tablespace_Aux_Index,
276         $sTemplate
277     );
278
279     pgsqlRunPartitionScript($sTemplate);
280 }
281
282
283 if ($aCMDResult['create-partition-functions'] || $aCMDResult['all']) {
284     echo "Partition Functions\n";
285     $bDidSomething = true;
286
287     $sTemplate = file_get_contents(CONST_BasePath.'/sql/partition-functions.src.sql');
288
289     pgsqlRunPartitionScript($sTemplate);
290 }
291
292 if ($aCMDResult['import-wikipedia-articles'] || $aCMDResult['all']) {
293     $bDidSomething = true;
294     $sWikiArticlesFile = CONST_BasePath.'/data/wikipedia_article.sql.bin';
295     $sWikiRedirectsFile = CONST_BasePath.'/data/wikipedia_redirect.sql.bin';
296     if (file_exists($sWikiArticlesFile)) {
297         echo "Importing wikipedia articles...";
298         pgsqlRunDropAndRestore($sWikiArticlesFile);
299         echo "...done\n";
300     } else {
301         echo "WARNING: wikipedia article dump file not found - places will have default importance\n";
302     }
303     if (file_exists($sWikiRedirectsFile)) {
304         echo "Importing wikipedia redirects...";
305         pgsqlRunDropAndRestore($sWikiRedirectsFile);
306         echo "...done\n";
307     } else {
308         echo "WARNING: wikipedia redirect dump file not found - some place importance values may be missing\n";
309     }
310 }
311
312
313 if ($aCMDResult['load-data'] || $aCMDResult['all']) {
314     echo "Drop old Data\n";
315     $bDidSomething = true;
316
317     $oDB =& getDB();
318     if (!pg_query($oDB->connection, 'TRUNCATE word')) fail(pg_last_error($oDB->connection));
319     echo '.';
320     if (!pg_query($oDB->connection, 'TRUNCATE placex')) fail(pg_last_error($oDB->connection));
321     echo '.';
322     if (!pg_query($oDB->connection, 'TRUNCATE location_property_osmline')) fail(pg_last_error($oDB->connection));
323     echo '.';
324     if (!pg_query($oDB->connection, 'TRUNCATE place_addressline')) fail(pg_last_error($oDB->connection));
325     echo '.';
326     if (!pg_query($oDB->connection, 'TRUNCATE place_boundingbox')) fail(pg_last_error($oDB->connection));
327     echo '.';
328     if (!pg_query($oDB->connection, 'TRUNCATE location_area')) fail(pg_last_error($oDB->connection));
329     echo '.';
330     if (!pg_query($oDB->connection, 'TRUNCATE search_name')) fail(pg_last_error($oDB->connection));
331     echo '.';
332     if (!pg_query($oDB->connection, 'TRUNCATE search_name_blank')) fail(pg_last_error($oDB->connection));
333     echo '.';
334     if (!pg_query($oDB->connection, 'DROP SEQUENCE seq_place')) fail(pg_last_error($oDB->connection));
335     echo '.';
336     if (!pg_query($oDB->connection, 'CREATE SEQUENCE seq_place start 100000')) fail(pg_last_error($oDB->connection));
337     echo '.';
338
339     $sSQL = 'select distinct partition from country_name';
340     $aPartitions = chksql($oDB->getCol($sSQL));
341     if (!$aCMDResult['no-partitions']) $aPartitions[] = 0;
342     foreach ($aPartitions as $sPartition) {
343         if (!pg_query($oDB->connection, 'TRUNCATE location_road_'.$sPartition)) fail(pg_last_error($oDB->connection));
344         echo '.';
345     }
346
347     // used by getorcreate_word_id to ignore frequent partial words
348     if (!pg_query($oDB->connection, 'CREATE OR REPLACE FUNCTION get_maxwordfreq() RETURNS integer AS $$ SELECT '.CONST_Max_Word_Frequency.' as maxwordfreq; $$ LANGUAGE SQL IMMUTABLE')) fail(pg_last_error($oDB->connection));
349     echo ".\n";
350
351     // pre-create the word list
352     if (!$aCMDResult['disable-token-precalc']) {
353         echo "Loading word list\n";
354         pgsqlRunScriptFile(CONST_BasePath.'/data/words.sql');
355     }
356
357     echo "Load Data\n";
358     $aDBInstances = array();
359     $iLoadThreads = max(1, $iInstances - 1);
360     for ($i = 0; $i < $iLoadThreads; $i++) {
361         $aDBInstances[$i] =& getDB(true);
362         $sSQL = 'insert into placex (osm_type, osm_id, class, type, name, admin_level, ';
363         $sSQL .= 'housenumber, street, addr_place, isin, postcode, country_code, extratags, ';
364         $sSQL .= 'geometry) select * from place where osm_id % '.$iLoadThreads.' = '.$i;
365         $sSQL .= " and not (class='place' and type='houses' and osm_type='W' and ST_GeometryType(geometry) = 'ST_LineString')";
366         if ($aCMDResult['verbose']) echo "$sSQL\n";
367         if (!pg_send_query($aDBInstances[$i]->connection, $sSQL)) fail(pg_last_error($oDB->connection));
368     }
369     // last thread for interpolation lines
370     $aDBInstances[$iLoadThreads] =& getDB(true);
371     $sSQL = 'select insert_osmline (osm_id, housenumber, street, addr_place, postcode, country_code, ';
372     $sSQL .= 'geometry) from place where ';
373     $sSQL .= "class='place' and type='houses' and osm_type='W' and ST_GeometryType(geometry) = 'ST_LineString'";
374     if ($aCMDResult['verbose']) echo "$sSQL\n";
375     if (!pg_send_query($aDBInstances[$i]->connection, $sSQL)) fail(pg_last_error($oDB->connection));
376
377     $bAnyBusy = true;
378     while ($bAnyBusy) {
379         $bAnyBusy = false;
380         for ($i = 0; $i <= $iLoadThreads; $i++) {
381             if (pg_connection_busy($aDBInstances[$i]->connection)) $bAnyBusy = true;
382         }
383         sleep(1);
384         echo '.';
385     }
386     echo "\n";
387     echo "Reanalysing database...\n";
388     pgsqlRunScript('ANALYSE');
389 }
390
391 if ($aCMDResult['import-tiger-data']) {
392     $bDidSomething = true;
393
394     $sTemplate = file_get_contents(CONST_BasePath.'/sql/tiger_import_start.sql');
395     $sTemplate = str_replace('{www-user}', CONST_Database_Web_User, $sTemplate);
396     $sTemplate = replace_tablespace(
397         '{ts:aux-data}',
398         CONST_Tablespace_Aux_Data,
399         $sTemplate
400     );
401     $sTemplate = replace_tablespace(
402         '{ts:aux-index}',
403         CONST_Tablespace_Aux_Index,
404         $sTemplate
405     );
406     pgsqlRunScript($sTemplate, false);
407
408     $aDBInstances = array();
409     for ($i = 0; $i < $iInstances; $i++) {
410         $aDBInstances[$i] =& getDB(true);
411     }
412
413     foreach (glob(CONST_Tiger_Data_Path.'/*.sql') as $sFile) {
414         echo $sFile.': ';
415         $hFile = fopen($sFile, "r");
416         $sSQL = fgets($hFile, 100000);
417         $iLines = 0;
418
419         while (true) {
420             for ($i = 0; $i < $iInstances; $i++) {
421                 if (!pg_connection_busy($aDBInstances[$i]->connection)) {
422                     while (pg_get_result($aDBInstances[$i]->connection));
423                     $sSQL = fgets($hFile, 100000);
424                     if (!$sSQL) break 2;
425                     if (!pg_send_query($aDBInstances[$i]->connection, $sSQL)) fail(pg_last_error($oDB->connection));
426                     $iLines++;
427                     if ($iLines == 1000) {
428                         echo ".";
429                         $iLines = 0;
430                     }
431                 }
432             }
433             usleep(10);
434         }
435
436         fclose($hFile);
437
438         $bAnyBusy = true;
439         while ($bAnyBusy) {
440             $bAnyBusy = false;
441             for ($i = 0; $i < $iInstances; $i++) {
442                 if (pg_connection_busy($aDBInstances[$i]->connection)) $bAnyBusy = true;
443             }
444             usleep(10);
445         }
446         echo "\n";
447     }
448
449     echo "Creating indexes\n";
450     $sTemplate = file_get_contents(CONST_BasePath.'/sql/tiger_import_finish.sql');
451     $sTemplate = str_replace('{www-user}', CONST_Database_Web_User, $sTemplate);
452     $sTemplate = replace_tablespace(
453         '{ts:aux-data}',
454         CONST_Tablespace_Aux_Data,
455         $sTemplate
456     );
457     $sTemplate = replace_tablespace(
458         '{ts:aux-index}',
459         CONST_Tablespace_Aux_Index,
460         $sTemplate
461     );
462     pgsqlRunScript($sTemplate, false);
463 }
464
465 if ($aCMDResult['calculate-postcodes'] || $aCMDResult['all']) {
466     $bDidSomething = true;
467     $oDB =& getDB();
468     if (!pg_query($oDB->connection, 'DELETE from placex where osm_type=\'P\'')) fail(pg_last_error($oDB->connection));
469     $sSQL = "insert into placex (osm_type,osm_id,class,type,postcode,calculated_country_code,geometry) ";
470     $sSQL .= "select 'P',nextval('seq_postcodes'),'place','postcode',postcode,calculated_country_code,";
471     $sSQL .= "ST_SetSRID(ST_Point(x,y),4326) as geometry from (select calculated_country_code,postcode,";
472     $sSQL .= "avg(st_x(st_centroid(geometry))) as x,avg(st_y(st_centroid(geometry))) as y ";
473     $sSQL .= "from placex where postcode is not null group by calculated_country_code,postcode) as x";
474     if (!pg_query($oDB->connection, $sSQL)) fail(pg_last_error($oDB->connection));
475
476     if (CONST_Use_Extra_US_Postcodes) {
477         $sSQL = "insert into placex (osm_type,osm_id,class,type,postcode,calculated_country_code,geometry) ";
478         $sSQL .= "select 'P',nextval('seq_postcodes'),'place','postcode',postcode,'us',";
479         $sSQL .= "ST_SetSRID(ST_Point(x,y),4326) as geometry from us_postcode";
480         if (!pg_query($oDB->connection, $sSQL)) fail(pg_last_error($oDB->connection));
481     }
482 }
483
484 if ($aCMDResult['osmosis-init'] || ($aCMDResult['all'] && !$aCMDResult['drop'])) { // no use doing osmosis-init when dropping update tables
485     $bDidSomething = true;
486     $oDB =& getDB();
487
488     if (!file_exists(CONST_Osmosis_Binary)) {
489         echo "Please download osmosis.\nIf it is already installed, check the path in your local settings (settings/local.php) file.\n";
490         if (!$aCMDResult['all']) {
491             fail("osmosis not found in '".CONST_Osmosis_Binary."'");
492         }
493     } else {
494         if (file_exists(CONST_InstallPath.'/settings/configuration.txt')) {
495             echo "settings/configuration.txt already exists\n";
496         } else {
497             passthru(CONST_Osmosis_Binary.' --read-replication-interval-init '.CONST_InstallPath.'/settings');
498             // update osmosis configuration.txt with our settings
499             passthru("sed -i 's!baseUrl=.*!baseUrl=".CONST_Replication_Url."!' ".CONST_InstallPath.'/settings/configuration.txt');
500             passthru("sed -i 's:maxInterval = .*:maxInterval = ".CONST_Replication_MaxInterval.":' ".CONST_InstallPath.'/settings/configuration.txt');
501         }
502
503         // Find the last node in the DB
504         $iLastOSMID = $oDB->getOne("select max(osm_id) from place where osm_type = 'N'");
505
506         // Lookup the timestamp that node was created (less 3 hours for margin for changsets to be closed)
507         $sLastNodeURL = 'http://www.openstreetmap.org/api/0.6/node/'.$iLastOSMID."/1";
508         $sLastNodeXML = file_get_contents($sLastNodeURL);
509         preg_match('#timestamp="(([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})Z)"#', $sLastNodeXML, $aLastNodeDate);
510         $iLastNodeTimestamp = strtotime($aLastNodeDate[1]) - (3*60*60);
511
512         // Search for the correct state file - uses file timestamps so need to sort by date descending
513         $sRepURL = CONST_Replication_Url."/";
514         $sRep = file_get_contents($sRepURL."?C=M;O=D;F=1");
515         // download.geofabrik.de:    <a href="000/">000/</a></td><td align="right">26-Feb-2013 11:53  </td>
516         // planet.openstreetmap.org: <a href="273/">273/</a>                    2013-03-11 07:41    -
517         preg_match_all('#<a href="[0-9]{3}/">([0-9]{3}/)</a>\s*([-0-9a-zA-Z]+ [0-9]{2}:[0-9]{2})#', $sRep, $aRepMatches, PREG_SET_ORDER);
518         if ($aRepMatches) {
519             $aPrevRepMatch = false;
520             foreach ($aRepMatches as $aRepMatch) {
521                 if (strtotime($aRepMatch[2]) < $iLastNodeTimestamp) break;
522                 $aPrevRepMatch = $aRepMatch;
523             }
524             if ($aPrevRepMatch) $aRepMatch = $aPrevRepMatch;
525
526             $sRepURL .= $aRepMatch[1];
527             $sRep = file_get_contents($sRepURL."?C=M;O=D;F=1");
528             preg_match_all('#<a href="[0-9]{3}/">([0-9]{3}/)</a>\s*([-0-9a-zA-Z]+ [0-9]{2}:[0-9]{2})#', $sRep, $aRepMatches, PREG_SET_ORDER);
529             $aPrevRepMatch = false;
530             foreach ($aRepMatches as $aRepMatch) {
531                 if (strtotime($aRepMatch[2]) < $iLastNodeTimestamp) break;
532                 $aPrevRepMatch = $aRepMatch;
533             }
534             if ($aPrevRepMatch) $aRepMatch = $aPrevRepMatch;
535
536             $sRepURL .= $aRepMatch[1];
537             $sRep = file_get_contents($sRepURL."?C=M;O=D;F=1");
538             preg_match_all('#<a href="[0-9]{3}.state.txt">([0-9]{3}).state.txt</a>\s*([-0-9a-zA-Z]+ [0-9]{2}:[0-9]{2})#', $sRep, $aRepMatches, PREG_SET_ORDER);
539             $aPrevRepMatch = false;
540             foreach ($aRepMatches as $aRepMatch) {
541                 if (strtotime($aRepMatch[2]) < $iLastNodeTimestamp) break;
542                 $aPrevRepMatch = $aRepMatch;
543             }
544             if ($aPrevRepMatch) $aRepMatch = $aPrevRepMatch;
545
546             $sRepURL .= $aRepMatch[1].'.state.txt';
547             echo "Getting state file: $sRepURL\n";
548             $sStateFile = file_get_contents($sRepURL);
549             if (!$sStateFile || strlen($sStateFile) > 1000) fail("unable to obtain state file");
550             file_put_contents(CONST_InstallPath.'/settings/state.txt', $sStateFile);
551             echo "Updating DB status\n";
552             pg_query($oDB->connection, 'TRUNCATE import_status');
553             $sSQL = "INSERT INTO import_status VALUES('".$aRepMatch[2]."')";
554             pg_query($oDB->connection, $sSQL);
555         } else {
556             if (!$aCMDResult['all']) {
557                 fail("Cannot read state file directory.");
558             }
559         }
560     }
561 }
562
563 if ($aCMDResult['index'] || $aCMDResult['all']) {
564     $bDidSomething = true;
565     $sOutputFile = '';
566     $sBaseCmd = CONST_InstallPath.'/nominatim/nominatim -i -d '.$aDSNInfo['database'].' -P '.$aDSNInfo['port'].' -t '.$iInstances.$sOutputFile;
567     passthruCheckReturn($sBaseCmd.' -R 4');
568     if (!$aCMDResult['index-noanalyse']) pgsqlRunScript('ANALYSE');
569     passthruCheckReturn($sBaseCmd.' -r 5 -R 25');
570     if (!$aCMDResult['index-noanalyse']) pgsqlRunScript('ANALYSE');
571     passthruCheckReturn($sBaseCmd.' -r 26');
572 }
573
574 if ($aCMDResult['create-search-indices'] || $aCMDResult['all']) {
575     echo "Search indices\n";
576     $bDidSomething = true;
577
578     $sTemplate = file_get_contents(CONST_BasePath.'/sql/indices.src.sql');
579     $sTemplate = replace_tablespace(
580         '{ts:address-index}',
581         CONST_Tablespace_Address_Index,
582         $sTemplate
583     );
584     $sTemplate = replace_tablespace(
585         '{ts:search-index}',
586         CONST_Tablespace_Search_Index,
587         $sTemplate
588     );
589     $sTemplate = replace_tablespace(
590         '{ts:aux-index}',
591         CONST_Tablespace_Aux_Index,
592         $sTemplate
593     );
594
595     pgsqlRunScript($sTemplate);
596 }
597
598 if ($aCMDResult['create-country-names'] || $aCMDResult['all']) {
599     echo 'Creating search index for default country names';
600     $bDidSomething = true;
601
602     pgsqlRunScript("select getorcreate_country(make_standard_name('uk'), 'gb')");
603     pgsqlRunScript("select getorcreate_country(make_standard_name('united states'), 'us')");
604     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");
605     pgsqlRunScript("select count(*) from (select getorcreate_country(make_standard_name(name->'name'), country_code) from country_name where name ? 'name') as x");
606
607     $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 ';
608     if (CONST_Languages) {
609         $sSQL .= 'in ';
610         $sDelim = '(';
611         foreach (explode(',', CONST_Languages) as $sLang) {
612             $sSQL .= $sDelim."'name:$sLang'";
613             $sDelim = ',';
614         }
615         $sSQL .= ')';
616     } else {
617         // all include all simple name tags
618         $sSQL .= "like 'name:%'";
619     }
620     $sSQL .= ') v';
621     pgsqlRunScript($sSQL);
622 }
623
624 if ($aCMDResult['drop']) {
625     // The implementation is potentially a bit dangerous because it uses
626     // a positive selection of tables to keep, and deletes everything else.
627     // Including any tables that the unsuspecting user might have manually
628     // created. USE AT YOUR OWN PERIL.
629     $bDidSomething = true;
630
631     // tables we want to keep. everything else goes.
632     $aKeepTables = array(
633                     "*columns",
634                     "import_polygon_*",
635                     "import_status",
636                     "place_addressline",
637                     "location_property*",
638                     "placex",
639                     "search_name",
640                     "seq_*",
641                     "word",
642                     "query_log",
643                     "new_query_log",
644                     "gb_postcode",
645                     "spatial_ref_sys",
646                     "country_name",
647                     "place_classtype_*"
648                    );
649
650     $oDB =& getDB();
651     $aDropTables = array();
652     $aHaveTables = chksql($oDB->getCol("SELECT tablename FROM pg_tables WHERE schemaname='public'"));
653
654     foreach ($aHaveTables as $sTable) {
655         $bFound = false;
656         foreach ($aKeepTables as $sKeep) {
657             if (fnmatch($sKeep, $sTable)) {
658                 $bFound = true;
659                 break;
660             }
661         }
662         if (!$bFound) array_push($aDropTables, $sTable);
663     }
664
665     foreach ($aDropTables as $sDrop) {
666         if ($aCMDResult['verbose']) echo "dropping table $sDrop\n";
667         @pg_query($oDB->connection, "DROP TABLE $sDrop CASCADE");
668         // ignore warnings/errors as they might be caused by a table having
669         // been deleted already by CASCADE
670     }
671
672     if (!is_null(CONST_Osm2pgsql_Flatnode_File)) {
673         if ($aCMDResult['verbose']) echo "deleting ".CONST_Osm2pgsql_Flatnode_File."\n";
674         unlink(CONST_Osm2pgsql_Flatnode_File);
675     }
676 }
677
678 if (!$bDidSomething) {
679     showUsage($aCMDOptions, true);
680 } else {
681     echo "Setup finished.\n";
682 }
683
684
685 function pgsqlRunScriptFile($sFilename)
686 {
687     if (!file_exists($sFilename)) fail('unable to find '.$sFilename);
688
689     // Convert database DSN to psql parameters
690     $aDSNInfo = DB::parseDSN(CONST_Database_DSN);
691     if (!isset($aDSNInfo['port']) || !$aDSNInfo['port']) $aDSNInfo['port'] = 5432;
692     $sCMD = 'psql -p '.$aDSNInfo['port'].' -d '.$aDSNInfo['database'];
693
694     $ahGzipPipes = null;
695     if (preg_match('/\\.gz$/', $sFilename)) {
696         $aDescriptors = array(
697                          0 => array('pipe', 'r'),
698                          1 => array('pipe', 'w'),
699                          2 => array('file', '/dev/null', 'a')
700                         );
701         $hGzipProcess = proc_open('zcat '.$sFilename, $aDescriptors, $ahGzipPipes);
702         if (!is_resource($hGzipProcess)) fail('unable to start zcat');
703         $aReadPipe = $ahGzipPipes[1];
704         fclose($ahGzipPipes[0]);
705     } else {
706         $sCMD .= ' -f '.$sFilename;
707         $aReadPipe = array('pipe', 'r');
708     }
709
710     $aDescriptors = array(
711                      0 => $aReadPipe,
712                      1 => array('pipe', 'w'),
713                      2 => array('file', '/dev/null', 'a')
714                     );
715     $ahPipes = null;
716     $hProcess = proc_open($sCMD, $aDescriptors, $ahPipes);
717     if (!is_resource($hProcess)) fail('unable to start pgsql');
718
719
720     // TODO: error checking
721     while (!feof($ahPipes[1])) {
722         echo fread($ahPipes[1], 4096);
723     }
724     fclose($ahPipes[1]);
725
726     $iReturn = proc_close($hProcess);
727     if ($iReturn > 0) {
728         fail("pgsql returned with error code ($iReturn)");
729     }
730     if ($ahGzipPipes) {
731         fclose($ahGzipPipes[1]);
732         proc_close($hGzipProcess);
733     }
734 }
735
736 function pgsqlRunScript($sScript, $bfatal = true)
737 {
738     global $aCMDResult;
739     // Convert database DSN to psql parameters
740     $aDSNInfo = DB::parseDSN(CONST_Database_DSN);
741     if (!isset($aDSNInfo['port']) || !$aDSNInfo['port']) $aDSNInfo['port'] = 5432;
742     $sCMD = 'psql -p '.$aDSNInfo['port'].' -d '.$aDSNInfo['database'];
743     if ($bfatal && !$aCMDResult['ignore-errors'])
744         $sCMD .= ' -v ON_ERROR_STOP=1';
745     $aDescriptors = array(
746                      0 => array('pipe', 'r'),
747                      1 => STDOUT,
748                      2 => STDERR
749                     );
750     $ahPipes = null;
751     $hProcess = @proc_open($sCMD, $aDescriptors, $ahPipes);
752     if (!is_resource($hProcess)) fail('unable to start pgsql');
753
754     while (strlen($sScript)) {
755         $written = fwrite($ahPipes[0], $sScript);
756         if ($written <= 0) break;
757         $sScript = substr($sScript, $written);
758     }
759     fclose($ahPipes[0]);
760     $iReturn = proc_close($hProcess);
761     if ($bfatal && $iReturn > 0) {
762         fail("pgsql returned with error code ($iReturn)");
763     }
764 }
765
766 function pgsqlRunPartitionScript($sTemplate)
767 {
768     global $aCMDResult;
769     $oDB =& getDB();
770
771     $sSQL = 'select distinct partition from country_name';
772     $aPartitions = chksql($oDB->getCol($sSQL));
773     if (!$aCMDResult['no-partitions']) $aPartitions[] = 0;
774
775     preg_match_all('#^-- start(.*?)^-- end#ms', $sTemplate, $aMatches, PREG_SET_ORDER);
776     foreach ($aMatches as $aMatch) {
777         $sResult = '';
778         foreach ($aPartitions as $sPartitionName) {
779             $sResult .= str_replace('-partition-', $sPartitionName, $aMatch[1]);
780         }
781         $sTemplate = str_replace($aMatch[0], $sResult, $sTemplate);
782     }
783
784     pgsqlRunScript($sTemplate);
785 }
786
787 function pgsqlRunRestoreData($sDumpFile)
788 {
789     // Convert database DSN to psql parameters
790     $aDSNInfo = DB::parseDSN(CONST_Database_DSN);
791     if (!isset($aDSNInfo['port']) || !$aDSNInfo['port']) $aDSNInfo['port'] = 5432;
792     $sCMD = 'pg_restore -p '.$aDSNInfo['port'].' -d '.$aDSNInfo['database'].' -Fc -a '.$sDumpFile;
793
794     $aDescriptors = array(
795                      0 => array('pipe', 'r'),
796                      1 => array('pipe', 'w'),
797                      2 => array('file', '/dev/null', 'a')
798                     );
799     $ahPipes = null;
800     $hProcess = proc_open($sCMD, $aDescriptors, $ahPipes);
801     if (!is_resource($hProcess)) fail('unable to start pg_restore');
802
803     fclose($ahPipes[0]);
804
805     // TODO: error checking
806     while (!feof($ahPipes[1])) {
807         echo fread($ahPipes[1], 4096);
808     }
809     fclose($ahPipes[1]);
810
811     $iReturn = proc_close($hProcess);
812 }
813
814 function pgsqlRunDropAndRestore($sDumpFile)
815 {
816     // Convert database DSN to psql parameters
817     $aDSNInfo = DB::parseDSN(CONST_Database_DSN);
818     if (!isset($aDSNInfo['port']) || !$aDSNInfo['port']) $aDSNInfo['port'] = 5432;
819     $sCMD = 'pg_restore -p '.$aDSNInfo['port'].' -d '.$aDSNInfo['database'].' -Fc --clean '.$sDumpFile;
820
821     $aDescriptors = array(
822                      0 => array('pipe', 'r'),
823                      1 => array('pipe', 'w'),
824                      2 => array('file', '/dev/null', 'a')
825                     );
826     $ahPipes = null;
827     $hProcess = proc_open($sCMD, $aDescriptors, $ahPipes);
828     if (!is_resource($hProcess)) fail('unable to start pg_restore');
829
830     fclose($ahPipes[0]);
831
832     // TODO: error checking
833     while (!feof($ahPipes[1])) {
834         echo fread($ahPipes[1], 4096);
835     }
836     fclose($ahPipes[1]);
837
838     $iReturn = proc_close($hProcess);
839 }
840
841 function passthruCheckReturn($cmd)
842 {
843     $result = -1;
844     passthru($cmd, $result);
845     if ($result != 0) fail('Error executing external command: '.$cmd);
846 }
847
848 function replace_tablespace($sTemplate, $sTablespace, $sSql)
849 {
850     if ($sTablespace) {
851         $sSql = str_replace($sTemplate, 'TABLESPACE "'.$sTablespace.'"', $sSql);
852     } else {
853         $sSql = str_replace($sTemplate, '', $sSql);
854     }
855
856     return $sSql;
857 }
858
859 function create_sql_functions($aCMDResult)
860 {
861     $sTemplate = file_get_contents(CONST_BasePath.'/sql/functions.sql');
862     $sTemplate = str_replace('{modulepath}', CONST_InstallPath.'/module', $sTemplate);
863     if ($aCMDResult['enable-diff-updates']) {
864         $sTemplate = str_replace('RETURN NEW; -- %DIFFUPDATES%', '--', $sTemplate);
865     }
866     if ($aCMDResult['enable-debug-statements']) {
867         $sTemplate = str_replace('--DEBUG:', '', $sTemplate);
868     }
869     if (CONST_Limit_Reindexing) {
870         $sTemplate = str_replace('--LIMIT INDEXING:', '', $sTemplate);
871     }
872     if (!CONST_Use_US_Tiger_Data) {
873         $sTemplate = str_replace('-- %NOTIGERDATA% ', '', $sTemplate);
874     }
875     if (!CONST_Use_Aux_Location_data) {
876         $sTemplate = str_replace('-- %NOAUXDATA% ', '', $sTemplate);
877     }
878     pgsqlRunScript($sTemplate);
879 }