]> git.openstreetmap.org Git - nominatim.git/blob - utils/setup.php
update osm2pgsql (revert use of "char")
[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, geometry) RENAME TO ST_LineLocatePoint');
130     }
131     if ($fPostgisVersion < 2.2) {
132         pgsqlRunScript('ALTER FUNCTION ST_Distance_Spheroid(geometry, geometry, spheroid) RENAME TO ST_DistanceSpheroid');
133     }
134
135     if (!file_exists(CONST_ExtraDataPath.'/country_osm_grid.sql.gz')) {
136         echo "Error: you need to download the country_osm_grid first:";
137         echo "\n    wget -O ".CONST_ExtraDataPath."/country_osm_grid.sql.gz http://www.nominatim.org/data/country_grid.sql.gz\n";
138         exit(1);
139     }
140
141     pgsqlRunScriptFile(CONST_BasePath.'/data/country_name.sql');
142     pgsqlRunScriptFile(CONST_BasePath.'/data/country_naturalearthdata.sql');
143     pgsqlRunScriptFile(CONST_BasePath.'/data/country_osm_grid.sql.gz');
144     pgsqlRunScriptFile(CONST_BasePath.'/data/gb_postcode_table.sql');
145     if (file_exists(CONST_BasePath.'/data/gb_postcode_data.sql.gz')) {
146         pgsqlRunScriptFile(CONST_BasePath.'/data/gb_postcode_data.sql.gz');
147     } else {
148         echo "WARNING: external UK postcode table not found.\n";
149     }
150     if (CONST_Use_Extra_US_Postcodes) {
151         pgsqlRunScriptFile(CONST_BasePath.'/data/us_postcode.sql');
152     }
153
154     if ($aCMDResult['no-partitions']) {
155         pgsqlRunScript('update country_name set partition = 0');
156     }
157
158     // the following will be needed by create_functions later but
159     // is only defined in the subsequently called create_tables.
160     // Create dummies here that will be overwritten by the proper
161     // versions in create-tables.
162     pgsqlRunScript('CREATE TABLE place_boundingbox ()');
163     pgsqlRunScript('create type wikipedia_article_match as ()');
164 }
165
166 if ($aCMDResult['import-data'] || $aCMDResult['all']) {
167     echo "Import\n";
168     $bDidSomething = true;
169
170     $osm2pgsql = CONST_Osm2pgsql_Binary;
171     if (!file_exists($osm2pgsql)) {
172         echo "Check CONST_Osm2pgsql_Binary in your local settings file.\n";
173         echo "Normally you should not need to set this manually.\n";
174         fail("osm2pgsql not found in '$osm2pgsql'");
175     }
176
177     if (!is_null(CONST_Osm2pgsql_Flatnode_File)) {
178         $osm2pgsql .= ' --flat-nodes '.CONST_Osm2pgsql_Flatnode_File;
179     }
180     if (CONST_Tablespace_Osm2pgsql_Data)
181         $osm2pgsql .= ' --tablespace-slim-data '.CONST_Tablespace_Osm2pgsql_Data;
182     if (CONST_Tablespace_Osm2pgsql_Index)
183         $osm2pgsql .= ' --tablespace-slim-index '.CONST_Tablespace_Osm2pgsql_Index;
184     if (CONST_Tablespace_Place_Data)
185         $osm2pgsql .= ' --tablespace-main-data '.CONST_Tablespace_Place_Data;
186     if (CONST_Tablespace_Place_Index)
187         $osm2pgsql .= ' --tablespace-main-index '.CONST_Tablespace_Place_Index;
188     $osm2pgsql .= ' -lsc -O gazetteer --hstore --number-processes 1';
189     $osm2pgsql .= ' -C '.$iCacheMemory;
190     $osm2pgsql .= ' -P '.$aDSNInfo['port'];
191     $osm2pgsql .= ' -d '.$aDSNInfo['database'].' '.$aCMDResult['osm-file'];
192     passthruCheckReturn($osm2pgsql);
193
194     $oDB =& getDB();
195     if (!$aCMDResult['ignore-errors'] && !chksql($oDB->getRow('select * from place limit 1'))) {
196         fail('No Data');
197     }
198 }
199
200 if ($aCMDResult['create-functions'] || $aCMDResult['all']) {
201     echo "Functions\n";
202     $bDidSomething = true;
203     if (!file_exists(CONST_InstallPath.'/module/nominatim.so')) fail("nominatim module not built");
204     create_sql_functions($aCMDResult);
205 }
206
207 if ($aCMDResult['create-tables'] || $aCMDResult['all']) {
208     $bDidSomething = true;
209
210     echo "Tables\n";
211     $sTemplate = file_get_contents(CONST_BasePath.'/sql/tables.sql');
212     $sTemplate = str_replace('{www-user}', CONST_Database_Web_User, $sTemplate);
213     $sTemplate = replace_tablespace(
214         '{ts:address-data}',
215         CONST_Tablespace_Address_Data,
216         $sTemplate
217     );
218     $sTemplate = replace_tablespace(
219         '{ts:address-index}',
220         CONST_Tablespace_Address_Index,
221         $sTemplate
222     );
223     $sTemplate = replace_tablespace(
224         '{ts:search-data}',
225         CONST_Tablespace_Search_Data,
226         $sTemplate
227     );
228     $sTemplate = replace_tablespace(
229         '{ts:search-index}',
230         CONST_Tablespace_Search_Index,
231         $sTemplate
232     );
233     $sTemplate = replace_tablespace(
234         '{ts:aux-data}',
235         CONST_Tablespace_Aux_Data,
236         $sTemplate
237     );
238     $sTemplate = replace_tablespace(
239         '{ts:aux-index}',
240         CONST_Tablespace_Aux_Index,
241         $sTemplate
242     );
243     pgsqlRunScript($sTemplate, false);
244
245     // re-run the functions
246     echo "Functions\n";
247     create_sql_functions($aCMDResult);
248 }
249
250 if ($aCMDResult['create-partition-tables'] || $aCMDResult['all']) {
251     echo "Partition Tables\n";
252     $bDidSomething = true;
253
254     $sTemplate = file_get_contents(CONST_BasePath.'/sql/partition-tables.src.sql');
255     $sTemplate = replace_tablespace(
256         '{ts:address-data}',
257         CONST_Tablespace_Address_Data,
258         $sTemplate
259     );
260     $sTemplate = replace_tablespace(
261         '{ts:address-index}',
262         CONST_Tablespace_Address_Index,
263         $sTemplate
264     );
265     $sTemplate = replace_tablespace(
266         '{ts:search-data}',
267         CONST_Tablespace_Search_Data,
268         $sTemplate
269     );
270     $sTemplate = replace_tablespace(
271         '{ts:search-index}',
272         CONST_Tablespace_Search_Index,
273         $sTemplate
274     );
275     $sTemplate = replace_tablespace(
276         '{ts:aux-data}',
277         CONST_Tablespace_Aux_Data,
278         $sTemplate
279     );
280     $sTemplate = replace_tablespace(
281         '{ts:aux-index}',
282         CONST_Tablespace_Aux_Index,
283         $sTemplate
284     );
285
286     pgsqlRunPartitionScript($sTemplate);
287 }
288
289
290 if ($aCMDResult['create-partition-functions'] || $aCMDResult['all']) {
291     echo "Partition Functions\n";
292     $bDidSomething = true;
293
294     $sTemplate = file_get_contents(CONST_BasePath.'/sql/partition-functions.src.sql');
295
296     pgsqlRunPartitionScript($sTemplate);
297 }
298
299 if ($aCMDResult['import-wikipedia-articles'] || $aCMDResult['all']) {
300     $bDidSomething = true;
301     $sWikiArticlesFile = CONST_Wikipedia_Data_Path.'/wikipedia_article.sql.bin';
302     $sWikiRedirectsFile = CONST_Wikipedia_Data_Path.'/wikipedia_redirect.sql.bin';
303     if (file_exists($sWikiArticlesFile)) {
304         echo "Importing wikipedia articles...";
305         pgsqlRunDropAndRestore($sWikiArticlesFile);
306         echo "...done\n";
307     } else {
308         echo "WARNING: wikipedia article dump file not found - places will have default importance\n";
309     }
310     if (file_exists($sWikiRedirectsFile)) {
311         echo "Importing wikipedia redirects...";
312         pgsqlRunDropAndRestore($sWikiRedirectsFile);
313         echo "...done\n";
314     } else {
315         echo "WARNING: wikipedia redirect dump file not found - some place importance values may be missing\n";
316     }
317 }
318
319
320 if ($aCMDResult['load-data'] || $aCMDResult['all']) {
321     echo "Drop old Data\n";
322     $bDidSomething = true;
323
324     $oDB =& getDB();
325     if (!pg_query($oDB->connection, 'TRUNCATE word')) fail(pg_last_error($oDB->connection));
326     echo '.';
327     if (!pg_query($oDB->connection, 'TRUNCATE placex')) fail(pg_last_error($oDB->connection));
328     echo '.';
329     if (!pg_query($oDB->connection, 'TRUNCATE location_property_osmline')) fail(pg_last_error($oDB->connection));
330     echo '.';
331     if (!pg_query($oDB->connection, 'TRUNCATE place_addressline')) fail(pg_last_error($oDB->connection));
332     echo '.';
333     if (!pg_query($oDB->connection, 'TRUNCATE place_boundingbox')) fail(pg_last_error($oDB->connection));
334     echo '.';
335     if (!pg_query($oDB->connection, 'TRUNCATE location_area')) fail(pg_last_error($oDB->connection));
336     echo '.';
337     if (!pg_query($oDB->connection, 'TRUNCATE search_name')) fail(pg_last_error($oDB->connection));
338     echo '.';
339     if (!pg_query($oDB->connection, 'TRUNCATE search_name_blank')) fail(pg_last_error($oDB->connection));
340     echo '.';
341     if (!pg_query($oDB->connection, 'DROP SEQUENCE seq_place')) fail(pg_last_error($oDB->connection));
342     echo '.';
343     if (!pg_query($oDB->connection, 'CREATE SEQUENCE seq_place start 100000')) fail(pg_last_error($oDB->connection));
344     echo '.';
345
346     $sSQL = 'select distinct partition from country_name';
347     $aPartitions = chksql($oDB->getCol($sSQL));
348     if (!$aCMDResult['no-partitions']) $aPartitions[] = 0;
349     foreach ($aPartitions as $sPartition) {
350         if (!pg_query($oDB->connection, 'TRUNCATE location_road_'.$sPartition)) fail(pg_last_error($oDB->connection));
351         echo '.';
352     }
353
354     // used by getorcreate_word_id to ignore frequent partial words
355     $sSQL = 'CREATE OR REPLACE FUNCTION get_maxwordfreq() RETURNS integer AS ';
356     $sSQL .= '$$ SELECT '.CONST_Max_Word_Frequency.' as maxwordfreq; $$ LANGUAGE SQL IMMUTABLE';
357     if (!pg_query($oDB->connection, $sSQL)) {
358         fail(pg_last_error($oDB->connection));
359     }
360     echo ".\n";
361
362     // pre-create the word list
363     if (!$aCMDResult['disable-token-precalc']) {
364         echo "Loading word list\n";
365         pgsqlRunScriptFile(CONST_BasePath.'/data/words.sql');
366     }
367
368     echo "Load Data\n";
369     $sColumns = 'osm_type, osm_id, class, type, name, admin_level, address, extratags, geometry';
370
371     $aDBInstances = array();
372     $iLoadThreads = max(1, $iInstances - 1);
373     for ($i = 0; $i < $iLoadThreads; $i++) {
374         $aDBInstances[$i] =& getDB(true);
375         $sSQL = "INSERT INTO placex ($sColumns) SELECT $sColumns FROM place WHERE osm_id % $iLoadThreads = $i";
376         $sSQL .= " and not (class='place' and type='houses' and osm_type='W'";
377         $sSQL .= "          and ST_GeometryType(geometry) = 'ST_LineString')";
378         $sSQL .= " and ST_IsValid(geometry)";
379         if ($aCMDResult['verbose']) echo "$sSQL\n";
380         if (!pg_send_query($aDBInstances[$i]->connection, $sSQL)) {
381             fail(pg_last_error($aDBInstances[$i]->connection));
382         }
383     }
384     // last thread for interpolation lines
385     $aDBInstances[$iLoadThreads] =& getDB(true);
386     $sSQL = 'insert into location_property_osmline';
387     $sSQL .= ' (osm_id, address, linegeo)';
388     $sSQL .= ' SELECT osm_id, address, geometry from place where ';
389     $sSQL .= "class='place' and type='houses' and osm_type='W' and ST_GeometryType(geometry) = 'ST_LineString'";
390     if ($aCMDResult['verbose']) echo "$sSQL\n";
391     if (!pg_send_query($aDBInstances[$iLoadThreads]->connection, $sSQL)) {
392         fail(pg_last_error($aDBInstances[$iLoadThreads]->connection));
393     }
394
395     $bAnyBusy = true;
396     while ($bAnyBusy) {
397         $bAnyBusy = false;
398         for ($i = 0; $i <= $iLoadThreads; $i++) {
399             if (pg_connection_busy($aDBInstances[$i]->connection)) $bAnyBusy = true;
400         }
401         sleep(1);
402         echo '.';
403     }
404     echo "\n";
405     echo "Reanalysing database...\n";
406     pgsqlRunScript('ANALYSE');
407
408     $sDatabaseDate = getDatabaseDate($oDB);
409     pg_query($oDB->connection, 'TRUNCATE import_status');
410     $sSQL = "INSERT INTO import_status (lastimportdate) VALUES('".$sDatabaseDate."')";
411     pg_query($oDB->connection, $sSQL);
412     echo "Latest data imported from $sDatabaseDate.\n";
413 }
414
415 if ($aCMDResult['import-tiger-data']) {
416     $bDidSomething = true;
417
418     $sTemplate = file_get_contents(CONST_BasePath.'/sql/tiger_import_start.sql');
419     $sTemplate = str_replace('{www-user}', CONST_Database_Web_User, $sTemplate);
420     $sTemplate = replace_tablespace(
421         '{ts:aux-data}',
422         CONST_Tablespace_Aux_Data,
423         $sTemplate
424     );
425     $sTemplate = replace_tablespace(
426         '{ts:aux-index}',
427         CONST_Tablespace_Aux_Index,
428         $sTemplate
429     );
430     pgsqlRunScript($sTemplate, false);
431
432     $aDBInstances = array();
433     for ($i = 0; $i < $iInstances; $i++) {
434         $aDBInstances[$i] =& getDB(true);
435     }
436
437     foreach (glob(CONST_Tiger_Data_Path.'/*.sql') as $sFile) {
438         echo $sFile.': ';
439         $hFile = fopen($sFile, "r");
440         $sSQL = fgets($hFile, 100000);
441         $iLines = 0;
442
443         while (true) {
444             for ($i = 0; $i < $iInstances; $i++) {
445                 if (!pg_connection_busy($aDBInstances[$i]->connection)) {
446                     while (pg_get_result($aDBInstances[$i]->connection));
447                     $sSQL = fgets($hFile, 100000);
448                     if (!$sSQL) break 2;
449                     if (!pg_send_query($aDBInstances[$i]->connection, $sSQL)) fail(pg_last_error($oDB->connection));
450                     $iLines++;
451                     if ($iLines == 1000) {
452                         echo ".";
453                         $iLines = 0;
454                     }
455                 }
456             }
457             usleep(10);
458         }
459
460         fclose($hFile);
461
462         $bAnyBusy = true;
463         while ($bAnyBusy) {
464             $bAnyBusy = false;
465             for ($i = 0; $i < $iInstances; $i++) {
466                 if (pg_connection_busy($aDBInstances[$i]->connection)) $bAnyBusy = true;
467             }
468             usleep(10);
469         }
470         echo "\n";
471     }
472
473     echo "Creating indexes\n";
474     $sTemplate = file_get_contents(CONST_BasePath.'/sql/tiger_import_finish.sql');
475     $sTemplate = str_replace('{www-user}', CONST_Database_Web_User, $sTemplate);
476     $sTemplate = replace_tablespace(
477         '{ts:aux-data}',
478         CONST_Tablespace_Aux_Data,
479         $sTemplate
480     );
481     $sTemplate = replace_tablespace(
482         '{ts:aux-index}',
483         CONST_Tablespace_Aux_Index,
484         $sTemplate
485     );
486     pgsqlRunScript($sTemplate, false);
487 }
488
489 if ($aCMDResult['calculate-postcodes'] || $aCMDResult['all']) {
490     $bDidSomething = true;
491     $oDB =& getDB();
492     if (!pg_query($oDB->connection, 'DELETE from placex where osm_type=\'P\'')) fail(pg_last_error($oDB->connection));
493     $sSQL = "insert into placex (osm_type,osm_id,class,type,postcode,country_code,geometry) ";
494     $sSQL .= "select 'P',nextval('seq_postcodes'),'place','postcode',postcode,country_code,";
495     $sSQL .= "ST_SetSRID(ST_Point(x,y),4326) as geometry from (select country_code,postcode,";
496     $sSQL .= "avg(st_x(st_centroid(geometry))) as x,avg(st_y(st_centroid(geometry))) as y ";
497     $sSQL .= "from placex where postcode is not null group by country_code,postcode) as x ";
498     $sSQL .= "where ST_Point(x,y) is not null";
499     if (!pg_query($oDB->connection, $sSQL)) fail(pg_last_error($oDB->connection));
500
501     if (CONST_Use_Extra_US_Postcodes) {
502         $sSQL = "insert into placex (osm_type,osm_id,class,type,postcode,country_code,geometry) ";
503         $sSQL .= "select 'P',nextval('seq_postcodes'),'place','postcode',postcode,'us',";
504         $sSQL .= "ST_SetSRID(ST_Point(x,y),4326) as geometry from us_postcode";
505         if (!pg_query($oDB->connection, $sSQL)) fail(pg_last_error($oDB->connection));
506     }
507 }
508
509 if ($aCMDResult['osmosis-init']) {
510     $bDidSomething = true;
511     echo "Command 'osmosis-init' no longer available, please use utils/update.php --init-updates.\n";
512 }
513
514 if ($aCMDResult['index'] || $aCMDResult['all']) {
515     $bDidSomething = true;
516     $sOutputFile = '';
517     $sBaseCmd = CONST_InstallPath.'/nominatim/nominatim -i -d '.$aDSNInfo['database'].' -P '.$aDSNInfo['port'].' -t '.$iInstances.$sOutputFile;
518     passthruCheckReturn($sBaseCmd.' -R 4');
519     if (!$aCMDResult['index-noanalyse']) pgsqlRunScript('ANALYSE');
520     passthruCheckReturn($sBaseCmd.' -r 5 -R 25');
521     if (!$aCMDResult['index-noanalyse']) pgsqlRunScript('ANALYSE');
522     passthruCheckReturn($sBaseCmd.' -r 26');
523 }
524
525 if ($aCMDResult['create-search-indices'] || $aCMDResult['all']) {
526     echo "Search indices\n";
527     $bDidSomething = true;
528
529     $sTemplate = file_get_contents(CONST_BasePath.'/sql/indices.src.sql');
530     $sTemplate = replace_tablespace(
531         '{ts:address-index}',
532         CONST_Tablespace_Address_Index,
533         $sTemplate
534     );
535     $sTemplate = replace_tablespace(
536         '{ts:search-index}',
537         CONST_Tablespace_Search_Index,
538         $sTemplate
539     );
540     $sTemplate = replace_tablespace(
541         '{ts:aux-index}',
542         CONST_Tablespace_Aux_Index,
543         $sTemplate
544     );
545
546     pgsqlRunScript($sTemplate);
547 }
548
549 if ($aCMDResult['create-country-names'] || $aCMDResult['all']) {
550     echo 'Creating search index for default country names';
551     $bDidSomething = true;
552
553     pgsqlRunScript("select getorcreate_country(make_standard_name('uk'), 'gb')");
554     pgsqlRunScript("select getorcreate_country(make_standard_name('united states'), 'us')");
555     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");
556     pgsqlRunScript("select count(*) from (select getorcreate_country(make_standard_name(name->'name'), country_code) from country_name where name ? 'name') as x");
557
558     $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 ';
559     if (CONST_Languages) {
560         $sSQL .= 'in ';
561         $sDelim = '(';
562         foreach (explode(',', CONST_Languages) as $sLang) {
563             $sSQL .= $sDelim."'name:$sLang'";
564             $sDelim = ',';
565         }
566         $sSQL .= ')';
567     } else {
568         // all include all simple name tags
569         $sSQL .= "like 'name:%'";
570     }
571     $sSQL .= ') v';
572     pgsqlRunScript($sSQL);
573 }
574
575 if ($aCMDResult['drop']) {
576     // The implementation is potentially a bit dangerous because it uses
577     // a positive selection of tables to keep, and deletes everything else.
578     // Including any tables that the unsuspecting user might have manually
579     // created. USE AT YOUR OWN PERIL.
580     $bDidSomething = true;
581
582     // tables we want to keep. everything else goes.
583     $aKeepTables = array(
584                     "*columns",
585                     "import_polygon_*",
586                     "import_status",
587                     "place_addressline",
588                     "location_property*",
589                     "placex",
590                     "search_name",
591                     "seq_*",
592                     "word",
593                     "query_log",
594                     "new_query_log",
595                     "gb_postcode",
596                     "spatial_ref_sys",
597                     "country_name",
598                     "place_classtype_*"
599                    );
600
601     $oDB =& getDB();
602     $aDropTables = array();
603     $aHaveTables = chksql($oDB->getCol("SELECT tablename FROM pg_tables WHERE schemaname='public'"));
604
605     foreach ($aHaveTables as $sTable) {
606         $bFound = false;
607         foreach ($aKeepTables as $sKeep) {
608             if (fnmatch($sKeep, $sTable)) {
609                 $bFound = true;
610                 break;
611             }
612         }
613         if (!$bFound) array_push($aDropTables, $sTable);
614     }
615
616     foreach ($aDropTables as $sDrop) {
617         if ($aCMDResult['verbose']) echo "dropping table $sDrop\n";
618         @pg_query($oDB->connection, "DROP TABLE $sDrop CASCADE");
619         // ignore warnings/errors as they might be caused by a table having
620         // been deleted already by CASCADE
621     }
622
623     if (!is_null(CONST_Osm2pgsql_Flatnode_File)) {
624         if ($aCMDResult['verbose']) echo "deleting ".CONST_Osm2pgsql_Flatnode_File."\n";
625         unlink(CONST_Osm2pgsql_Flatnode_File);
626     }
627 }
628
629 if (!$bDidSomething) {
630     showUsage($aCMDOptions, true);
631 } else {
632     echo "Setup finished.\n";
633 }
634
635
636 function pgsqlRunScriptFile($sFilename)
637 {
638     if (!file_exists($sFilename)) fail('unable to find '.$sFilename);
639
640     // Convert database DSN to psql parameters
641     $aDSNInfo = DB::parseDSN(CONST_Database_DSN);
642     if (!isset($aDSNInfo['port']) || !$aDSNInfo['port']) $aDSNInfo['port'] = 5432;
643     $sCMD = 'psql -p '.$aDSNInfo['port'].' -d '.$aDSNInfo['database'];
644
645     $ahGzipPipes = null;
646     if (preg_match('/\\.gz$/', $sFilename)) {
647         $aDescriptors = array(
648                          0 => array('pipe', 'r'),
649                          1 => array('pipe', 'w'),
650                          2 => array('file', '/dev/null', 'a')
651                         );
652         $hGzipProcess = proc_open('zcat '.$sFilename, $aDescriptors, $ahGzipPipes);
653         if (!is_resource($hGzipProcess)) fail('unable to start zcat');
654         $aReadPipe = $ahGzipPipes[1];
655         fclose($ahGzipPipes[0]);
656     } else {
657         $sCMD .= ' -f '.$sFilename;
658         $aReadPipe = array('pipe', 'r');
659     }
660
661     $aDescriptors = array(
662                      0 => $aReadPipe,
663                      1 => array('pipe', 'w'),
664                      2 => array('file', '/dev/null', 'a')
665                     );
666     $ahPipes = null;
667     $hProcess = proc_open($sCMD, $aDescriptors, $ahPipes);
668     if (!is_resource($hProcess)) fail('unable to start pgsql');
669
670
671     // TODO: error checking
672     while (!feof($ahPipes[1])) {
673         echo fread($ahPipes[1], 4096);
674     }
675     fclose($ahPipes[1]);
676
677     $iReturn = proc_close($hProcess);
678     if ($iReturn > 0) {
679         fail("pgsql returned with error code ($iReturn)");
680     }
681     if ($ahGzipPipes) {
682         fclose($ahGzipPipes[1]);
683         proc_close($hGzipProcess);
684     }
685 }
686
687 function pgsqlRunScript($sScript, $bfatal = true)
688 {
689     global $aCMDResult;
690     // Convert database DSN to psql parameters
691     $aDSNInfo = DB::parseDSN(CONST_Database_DSN);
692     if (!isset($aDSNInfo['port']) || !$aDSNInfo['port']) $aDSNInfo['port'] = 5432;
693     $sCMD = 'psql -p '.$aDSNInfo['port'].' -d '.$aDSNInfo['database'];
694     if ($bfatal && !$aCMDResult['ignore-errors'])
695         $sCMD .= ' -v ON_ERROR_STOP=1';
696     $aDescriptors = array(
697                      0 => array('pipe', 'r'),
698                      1 => STDOUT,
699                      2 => STDERR
700                     );
701     $ahPipes = null;
702     $hProcess = @proc_open($sCMD, $aDescriptors, $ahPipes);
703     if (!is_resource($hProcess)) fail('unable to start pgsql');
704
705     while (strlen($sScript)) {
706         $written = fwrite($ahPipes[0], $sScript);
707         if ($written <= 0) break;
708         $sScript = substr($sScript, $written);
709     }
710     fclose($ahPipes[0]);
711     $iReturn = proc_close($hProcess);
712     if ($bfatal && $iReturn > 0) {
713         fail("pgsql returned with error code ($iReturn)");
714     }
715 }
716
717 function pgsqlRunPartitionScript($sTemplate)
718 {
719     global $aCMDResult;
720     $oDB =& getDB();
721
722     $sSQL = 'select distinct partition from country_name';
723     $aPartitions = chksql($oDB->getCol($sSQL));
724     if (!$aCMDResult['no-partitions']) $aPartitions[] = 0;
725
726     preg_match_all('#^-- start(.*?)^-- end#ms', $sTemplate, $aMatches, PREG_SET_ORDER);
727     foreach ($aMatches as $aMatch) {
728         $sResult = '';
729         foreach ($aPartitions as $sPartitionName) {
730             $sResult .= str_replace('-partition-', $sPartitionName, $aMatch[1]);
731         }
732         $sTemplate = str_replace($aMatch[0], $sResult, $sTemplate);
733     }
734
735     pgsqlRunScript($sTemplate);
736 }
737
738 function pgsqlRunRestoreData($sDumpFile)
739 {
740     // Convert database DSN to psql parameters
741     $aDSNInfo = DB::parseDSN(CONST_Database_DSN);
742     if (!isset($aDSNInfo['port']) || !$aDSNInfo['port']) $aDSNInfo['port'] = 5432;
743     $sCMD = 'pg_restore -p '.$aDSNInfo['port'].' -d '.$aDSNInfo['database'].' -Fc -a '.$sDumpFile;
744
745     $aDescriptors = array(
746                      0 => array('pipe', 'r'),
747                      1 => array('pipe', 'w'),
748                      2 => array('file', '/dev/null', 'a')
749                     );
750     $ahPipes = null;
751     $hProcess = proc_open($sCMD, $aDescriptors, $ahPipes);
752     if (!is_resource($hProcess)) fail('unable to start pg_restore');
753
754     fclose($ahPipes[0]);
755
756     // TODO: error checking
757     while (!feof($ahPipes[1])) {
758         echo fread($ahPipes[1], 4096);
759     }
760     fclose($ahPipes[1]);
761
762     $iReturn = proc_close($hProcess);
763 }
764
765 function pgsqlRunDropAndRestore($sDumpFile)
766 {
767     // Convert database DSN to psql parameters
768     $aDSNInfo = DB::parseDSN(CONST_Database_DSN);
769     if (!isset($aDSNInfo['port']) || !$aDSNInfo['port']) $aDSNInfo['port'] = 5432;
770     $sCMD = 'pg_restore -p '.$aDSNInfo['port'].' -d '.$aDSNInfo['database'].' -Fc --clean '.$sDumpFile;
771
772     $aDescriptors = array(
773                      0 => array('pipe', 'r'),
774                      1 => array('pipe', 'w'),
775                      2 => array('file', '/dev/null', 'a')
776                     );
777     $ahPipes = null;
778     $hProcess = proc_open($sCMD, $aDescriptors, $ahPipes);
779     if (!is_resource($hProcess)) fail('unable to start pg_restore');
780
781     fclose($ahPipes[0]);
782
783     // TODO: error checking
784     while (!feof($ahPipes[1])) {
785         echo fread($ahPipes[1], 4096);
786     }
787     fclose($ahPipes[1]);
788
789     $iReturn = proc_close($hProcess);
790 }
791
792 function passthruCheckReturn($cmd)
793 {
794     $result = -1;
795     passthru($cmd, $result);
796     if ($result != 0) fail('Error executing external command: '.$cmd);
797 }
798
799 function replace_tablespace($sTemplate, $sTablespace, $sSql)
800 {
801     if ($sTablespace) {
802         $sSql = str_replace($sTemplate, 'TABLESPACE "'.$sTablespace.'"', $sSql);
803     } else {
804         $sSql = str_replace($sTemplate, '', $sSql);
805     }
806
807     return $sSql;
808 }
809
810 function create_sql_functions($aCMDResult)
811 {
812     $sTemplate = file_get_contents(CONST_BasePath.'/sql/functions.sql');
813     $sTemplate = str_replace('{modulepath}', CONST_InstallPath.'/module', $sTemplate);
814     if ($aCMDResult['enable-diff-updates']) {
815         $sTemplate = str_replace('RETURN NEW; -- %DIFFUPDATES%', '--', $sTemplate);
816     }
817     if ($aCMDResult['enable-debug-statements']) {
818         $sTemplate = str_replace('--DEBUG:', '', $sTemplate);
819     }
820     if (CONST_Limit_Reindexing) {
821         $sTemplate = str_replace('--LIMIT INDEXING:', '', $sTemplate);
822     }
823     if (!CONST_Use_US_Tiger_Data) {
824         $sTemplate = str_replace('-- %NOTIGERDATA% ', '', $sTemplate);
825     }
826     if (!CONST_Use_Aux_Location_data) {
827         $sTemplate = str_replace('-- %NOAUXDATA% ', '', $sTemplate);
828     }
829     pgsqlRunScript($sTemplate);
830 }