3 namespace Nominatim\Setup;
7 protected $iCacheMemory; // set in constructor
8 protected $iInstances; // set in constructor
9 protected $sModulePath; // set in constructor
10 protected $aDSNInfo; // set in constructor = DB::parseDSN(CONST_Database_DSN);
11 protected $sVerbose; // set in constructor
12 protected $sIgnoreErrors; // set in constructor
13 protected $bEnableDiffUpdates; // set in constructor
14 protected $bEnableDebugStatements; // set in constructor
15 protected $bNoPartitions; // set in constructor
16 protected $oDB = null; // set in setupDB (earliest) or later in loadData, importData, drop, createSqlFunctions, importTigerData
17 // pgsqlRunPartitionScript, calculatePostcodes, ..if no already set
19 public function __construct($aCMDResult)
21 // by default, use all but one processor, but never more than 15.
22 $this->iInstances = isset($aCMDResult['threads'])
23 ? $aCMDResult['threads']
24 : (min(16, getProcessorCount()) - 1);
26 if ($this->iInstances < 1) {
27 $this->iInstances = 1;
28 warn('resetting threads to ' . $this->iInstances);
31 // Assume we can steal all the cache memory in the box (unless told otherwise)
32 if (isset($aCMDResult['osm2pgsql-cache'])) {
33 $this->iCacheMemory = $aCMDResult['osm2pgsql-cache'];
35 $this->iCacheMemory = getCacheMemoryMB();
38 $this->sModulePath = CONST_Database_Module_Path;
39 info('module path: ' . $this->sModulePath);
41 // prepares DB for import or update, sets the Data Source Name
42 $this->aDSNInfo = \DB::parseDSN(CONST_Database_DSN);
43 if (!isset($this->aDSNInfo['port']) || !$this->aDSNInfo['port']) {
44 $this->aDSNInfo['port'] = 5432;
47 // setting member variables based on command line options stored in $aCMDResult
48 $this->sVerbose = $aCMDResult['verbose'];
49 $this->sIgnoreErrors = $aCMDResult['ignore-errors'];
50 $this->bEnableDiffUpdates = $aCMDResult['enable-diff-updates'];
51 $this->bEnableDebugStatements = $aCMDResult['enable-debug-statements'];
52 $this->bNoPartitions = $aCMDResult['no-partitions'];
55 public function createDB()
58 $sDB = \DB::connect(CONST_Database_DSN, false);
59 if (!\PEAR::isError($sDB)) {
60 fail('database already exists (' . CONST_Database_DSN . ')');
63 $sCreateDBCmd = 'createdb -E UTF-8 -p ' . $this->aDSNInfo['port'] . ' ' . $this->aDSNInfo['database'];
64 if (isset($this->aDSNInfo['username']) && $this->aDSNInfo['username']) {
65 $sCreateDBCmd .= ' -U ' . $this->aDSNInfo['username'];
68 if (isset($this->aDSNInfo['hostspec']) && $this->aDSNInfo['hostspec']) {
69 $sCreateDBCmd .= ' -h ' . $this->aDSNInfo['hostspec'];
73 if (isset($this->aDSNInfo['password']) && $this->aDSNInfo['password']) {
74 $aProcEnv = array_merge(array('PGPASSWORD' => $this->aDSNInfo['password']), $_ENV);
77 $result = runWithEnv($sCreateDBCmd, $aProcEnv);
79 fail('Error executing external command: ' . $sCreateDBCmd);
84 public function setupDB()
87 $this->oDB = &getDB();
89 $fPostgresVersion = getPostgresVersion($this->oDB);
90 echo 'Postgres version found: ' . $fPostgresVersion . "\n";
92 if ($fPostgresVersion < 9.1) {
93 fail('Minimum supported version of Postgresql is 9.1.');
96 $this->pgsqlRunScript('CREATE EXTENSION IF NOT EXISTS hstore');
97 $this->pgsqlRunScript('CREATE EXTENSION IF NOT EXISTS postgis');
99 // For extratags and namedetails the hstore_to_json converter is
100 // needed which is only available from Postgresql 9.3+. For older
101 // versions add a dummy function that returns nothing.
102 $iNumFunc = chksql($this->oDB->getOne("select count(*) from pg_proc where proname = 'hstore_to_json'"));
104 if ($iNumFunc == 0) {
105 $this->pgsqlRunScript("create function hstore_to_json(dummy hstore) returns text AS 'select null::text' language sql immutable");
106 warn('Postgresql is too old. extratags and namedetails API not available.');
109 $fPostgisVersion = getPostgisVersion($this->oDB);
110 echo 'Postgis version found: ' . $fPostgisVersion . "\n";
112 if ($fPostgisVersion < 2.1) {
113 // Functions were renamed in 2.1 and throw an annoying deprecation warning
114 $this->pgsqlRunScript('ALTER FUNCTION st_line_interpolate_point(geometry, double precision) RENAME TO ST_LineInterpolatePoint');
115 $this->pgsqlRunScript('ALTER FUNCTION ST_Line_Locate_Point(geometry, geometry) RENAME TO ST_LineLocatePoint');
117 if ($fPostgisVersion < 2.2) {
118 $this->pgsqlRunScript('ALTER FUNCTION ST_Distance_Spheroid(geometry, geometry, spheroid) RENAME TO ST_DistanceSpheroid');
121 $i = chksql($this->oDB->getOne("select count(*) from pg_user where usename = '" . CONST_Database_Web_User . "'"));
123 echo "\nERROR: Web user '" . CONST_Database_Web_User . "' does not exist. Create it with:\n";
124 echo "\n createuser " . CONST_Database_Web_User . "\n\n";
128 if (!file_exists(CONST_ExtraDataPath . '/country_osm_grid.sql.gz')) {
129 echo 'Error: you need to download the country_osm_grid first:';
130 echo "\n wget -O " . CONST_ExtraDataPath . "/country_osm_grid.sql.gz https://www.nominatim.org/data/country_grid.sql.gz\n";
133 $this->pgsqlRunScriptFile(CONST_BasePath . '/data/country_name.sql');
134 $this->pgsqlRunScriptFile(CONST_BasePath . '/data/country_naturalearthdata.sql');
135 $this->pgsqlRunScriptFile(CONST_BasePath . '/data/country_osm_grid.sql.gz');
136 $this->pgsqlRunScriptFile(CONST_BasePath . '/data/gb_postcode_table.sql');
138 if (file_exists(CONST_BasePath . '/data/gb_postcode_data.sql.gz')) {
139 $this->pgsqlRunScriptFile(CONST_BasePath . '/data/gb_postcode_data.sql.gz');
141 warn('external UK postcode table not found.');
144 if (CONST_Use_Extra_US_Postcodes) {
145 $this->pgsqlRunScriptFile(CONST_BasePath . '/data/us_postcode.sql');
148 if ($this->bNoPartitions) {
149 $this->pgsqlRunScript('update country_name set partition = 0');
152 // the following will be needed by create_functions later but
153 // is only defined in the subsequently called T
154 // Create dummies here that will be overwritten by the proper
155 // versions in create-tables.
156 $this->pgsqlRunScript('CREATE TABLE IF NOT EXISTS place_boundingbox ()');
157 $this->pgsqlRunScript('CREATE TYPE wikipedia_article_match AS ()', false);
160 public function importData($sOSMFile)
164 $osm2pgsql = CONST_Osm2pgsql_Binary;
165 if (!file_exists($osm2pgsql)) {
166 echo "Check CONST_Osm2pgsql_Binary in your local settings file.\n";
167 echo "Normally you should not need to set this manually.\n";
168 fail("osm2pgsql not found in '$osm2pgsql'");
171 if (!is_null(CONST_Osm2pgsql_Flatnode_File) && CONST_Osm2pgsql_Flatnode_File) {
172 $osm2pgsql .= ' --flat-nodes ' . CONST_Osm2pgsql_Flatnode_File;
175 if (CONST_Tablespace_Osm2pgsql_Data) {
176 $osm2pgsql .= ' --tablespace-slim-data ' . CONST_Tablespace_Osm2pgsql_Data;
179 if (CONST_Tablespace_Osm2pgsql_Index) {
180 $osm2pgsql .= ' --tablespace-slim-index ' . CONST_Tablespace_Osm2pgsql_Index;
183 if (CONST_Tablespace_Place_Data) {
184 $osm2pgsql .= ' --tablespace-main-data ' . CONST_Tablespace_Place_Data;
187 if (CONST_Tablespace_Place_Index) {
188 $osm2pgsql .= ' --tablespace-main-index ' . CONST_Tablespace_Place_Index;
191 $osm2pgsql .= ' -lsc -O gazetteer --hstore --number-processes 1';
192 $osm2pgsql .= ' -C ' . $this->iCacheMemory;
193 $osm2pgsql .= ' -P ' . $this->aDSNInfo['port'];
194 if (isset($this->aDSNInfo['username']) && $this->aDSNInfo['username']) {
195 $osm2pgsql .= ' -U ' . $this->aDSNInfo['username'];
197 if (isset($this->aDSNInfo['hostspec']) && $this->aDSNInfo['hostspec']) {
198 $osm2pgsql .= ' -H ' . $this->aDSNInfo['hostspec'];
201 if (isset($this->aDSNInfo['password']) && $this->aDSNInfo['password']) {
202 $aProcEnv = array_merge(array('PGPASSWORD' => $this->aDSNInfo['password']), $_ENV);
204 $osm2pgsql .= ' -d ' . $this->aDSNInfo['database'] . ' ' . $sOSMFile;
205 runWithEnv($osm2pgsql, $aProcEnv);
206 if ($this->oDB == null) {
207 $this->oDB = &getDB();
210 if (!$this->sIgnoreErrors && !chksql($this->oDB->getRow('select * from place limit 1'))) {
215 public function createFunctions()
217 info('Create Functions');
219 $this->createSqlFunctions();
222 public function createTables()
224 info('Create Tables');
226 $sTemplate = file_get_contents(CONST_BasePath . '/sql/tables.sql');
227 $sTemplate = str_replace('{www-user}', CONST_Database_Web_User, $sTemplate);
228 $sTemplate = $this->replaceTablespace(
230 CONST_Tablespace_Address_Data,
233 $sTemplate = $this->replaceTablespace(
234 '{ts:address-index}',
235 CONST_Tablespace_Address_Index,
238 $sTemplate = $this->replaceTablespace(
240 CONST_Tablespace_Search_Data,
243 $sTemplate = $this->replaceTablespace(
245 CONST_Tablespace_Search_Index,
248 $sTemplate = $this->replaceTablespace(
250 CONST_Tablespace_Aux_Data,
253 $sTemplate = $this->replaceTablespace(
255 CONST_Tablespace_Aux_Index,
259 $this->pgsqlRunScript($sTemplate, false);
262 public function createPartitionTables()
264 info('Create Partition Tables');
266 $sTemplate = file_get_contents(CONST_BasePath . '/sql/partition-tables.src.sql');
267 $sTemplate = $this->replaceTablespace(
269 CONST_Tablespace_Address_Data,
273 $sTemplate = $this->replaceTablespace(
274 '{ts:address-index}',
275 CONST_Tablespace_Address_Index,
279 $sTemplate = $this->replaceTablespace(
281 CONST_Tablespace_Search_Data,
285 $sTemplate = $this->replaceTablespace(
287 CONST_Tablespace_Search_Index,
291 $sTemplate = $this->replaceTablespace(
293 CONST_Tablespace_Aux_Data,
297 $sTemplate = $this->replaceTablespace(
299 CONST_Tablespace_Aux_Index,
303 $this->pgsqlRunPartitionScript($sTemplate);
306 public function createPartitionFunctions()
308 info('Create Partition Functions');
310 $sTemplate = file_get_contents(CONST_BasePath . '/sql/partition-functions.src.sql');
311 $this->pgsqlRunPartitionScript($sTemplate);
314 public function importWikipediaArticles()
316 $sWikiArticlesFile = CONST_Wikipedia_Data_Path . '/wikipedia_article.sql.bin';
317 $sWikiRedirectsFile = CONST_Wikipedia_Data_Path . '/wikipedia_redirect.sql.bin';
318 if (file_exists($sWikiArticlesFile)) {
319 info('Importing wikipedia articles');
320 $this->pgsqlRunDropAndRestore($sWikiArticlesFile);
322 warn('wikipedia article dump file not found - places will have default importance');
324 if (file_exists($sWikiRedirectsFile)) {
325 info('Importing wikipedia redirects');
326 $this->pgsqlRunDropAndRestore($sWikiRedirectsFile);
328 warn('wikipedia redirect dump file not found - some place importance values may be missing');
330 echo ' finish wikipedia';
333 public function loadData($bDisableTokenPrecalc)
335 info('Drop old Data');
337 if ($this->oDB == null) {
338 $this->oDB = &getDB();
341 if (!pg_query($this->oDB->connection, 'TRUNCATE word')) {
342 fail(pg_last_error($this->oDB->connection));
346 if (!pg_query($this->oDB->connection, 'TRUNCATE placex')) {
347 fail(pg_last_error($this->oDB->connection));
351 if (!pg_query($this->oDB->connection, 'TRUNCATE location_property_osmline')) {
352 fail(pg_last_error($this->oDB->connection));
356 if (!pg_query($this->oDB->connection, 'TRUNCATE place_addressline')) {
357 fail(pg_last_error($this->oDB->connection));
361 if (!pg_query($this->oDB->connection, 'TRUNCATE place_boundingbox')) {
362 fail(pg_last_error($this->oDB->connection));
366 if (!pg_query($this->oDB->connection, 'TRUNCATE location_area')) {
367 fail(pg_last_error($this->oDB->connection));
371 if (!pg_query($this->oDB->connection, 'TRUNCATE search_name')) {
372 fail(pg_last_error($this->oDB->connection));
376 if (!pg_query($this->oDB->connection, 'TRUNCATE search_name_blank')) {
377 fail(pg_last_error($this->oDB->connection));
381 if (!pg_query($this->oDB->connection, 'DROP SEQUENCE seq_place')) {
382 fail(pg_last_error($this->oDB->connection));
386 if (!pg_query($this->oDB->connection, 'CREATE SEQUENCE seq_place start 100000')) {
387 fail(pg_last_error($this->oDB->connection));
392 $sSQL = 'select distinct partition from country_name';
393 $aPartitions = chksql($this->oDB->getCol($sSQL));
394 if (!$this->bNoPartitions) {
398 foreach ($aPartitions as $sPartition) {
399 if (!pg_query($this->oDB->connection, 'TRUNCATE location_road_' . $sPartition)) {
400 fail(pg_last_error($this->oDB->connection));
406 // used by getorcreate_word_id to ignore frequent partial words
407 $sSQL = 'CREATE OR REPLACE FUNCTION get_maxwordfreq() RETURNS integer AS ';
408 $sSQL .= '$$ SELECT ' . CONST_Max_Word_Frequency . ' as maxwordfreq; $$ LANGUAGE SQL IMMUTABLE';
409 if (!pg_query($this->oDB->connection, $sSQL)) {
410 fail(pg_last_error($this->oDB->connection));
414 // pre-create the word list
415 if (!$bDisableTokenPrecalc) {
416 info('Loading word list');
417 $this->pgsqlRunScriptFile(CONST_BasePath . '/data/words.sql');
421 $sColumns = 'osm_type, osm_id, class, type, name, admin_level, address, extratags, geometry';
422 $aDBInstances = array();
423 $iLoadThreads = max(1, $this->iInstances - 1);
424 for ($i = 0; $i < $iLoadThreads; $i++) {
425 $aDBInstances[$i] = &getDB(true);
426 $sSQL = "INSERT INTO placex ($sColumns) SELECT $sColumns FROM place WHERE osm_id % $iLoadThreads = $i";
427 $sSQL .= " and not (class='place' and type='houses' and osm_type='W'";
428 $sSQL .= " and ST_GeometryType(geometry) = 'ST_LineString')";
429 $sSQL .= ' and ST_IsValid(geometry)';
430 if ($this->sVerbose) {
434 if (!pg_send_query($aDBInstances[$i]->connection, $sSQL)) {
435 fail(pg_last_error($aDBInstances[$i]->connection));
439 // last thread for interpolation lines
440 $aDBInstances[$iLoadThreads] = &getDB(true);
441 $sSQL = 'insert into location_property_osmline';
442 $sSQL .= ' (osm_id, address, linegeo)';
443 $sSQL .= ' SELECT osm_id, address, geometry from place where ';
444 $sSQL .= "class='place' and type='houses' and osm_type='W' and ST_GeometryType(geometry) = 'ST_LineString'";
445 if ($this->sVerbose) {
449 if (!pg_send_query($aDBInstances[$iLoadThreads]->connection, $sSQL)) {
450 fail(pg_last_error($aDBInstances[$iLoadThreads]->connection));
454 for ($i = 0; $i <= $iLoadThreads; $i++) {
455 while (($hPGresult = pg_get_result($aDBInstances[$i]->connection)) !== false) {
456 $resultStatus = pg_result_status($hPGresult);
457 // PGSQL_EMPTY_QUERY, PGSQL_COMMAND_OK, PGSQL_TUPLES_OK,
458 // PGSQL_COPY_OUT, PGSQL_COPY_IN, PGSQL_BAD_RESPONSE,
459 // PGSQL_NONFATAL_ERROR and PGSQL_FATAL_ERROR
460 echo 'Query result ' . $i . ' is: ' . $resultStatus . "\n";
461 if ($resultStatus != PGSQL_COMMAND_OK && $resultStatus != PGSQL_TUPLES_OK) {
462 $resultError = pg_result_error($hPGresult);
463 echo '-- error text ' . $i . ': ' . $resultError . "\n";
469 fail('SQL errors loading placex and/or location_property_osmline tables');
472 info('Reanalysing database');
473 $this->pgsqlRunScript('ANALYSE');
475 $sDatabaseDate = getDatabaseDate($this->oDB);
476 pg_query($this->oDB->connection, 'TRUNCATE import_status');
477 if ($sDatabaseDate === false) {
478 warn('could not determine database date.');
480 $sSQL = "INSERT INTO import_status (lastimportdate) VALUES('" . $sDatabaseDate . "')";
481 pg_query($this->oDB->connection, $sSQL);
482 echo "Latest data imported from $sDatabaseDate.\n";
486 public function importTigerData()
488 info('Import Tiger data');
490 $sTemplate = file_get_contents(CONST_BasePath . '/sql/tiger_import_start.sql');
491 $sTemplate = str_replace('{www-user}', CONST_Database_Web_User, $sTemplate);
492 $sTemplate = $this->replaceTablespace(
494 CONST_Tablespace_Aux_Data,
497 $sTemplate = $this->replaceTablespace(
499 CONST_Tablespace_Aux_Index,
502 $this->pgsqlRunScript($sTemplate, false);
504 $aDBInstances = array();
505 for ($i = 0; $i < $this->iInstances; $i++) {
506 $aDBInstances[$i] = &getDB(true);
509 foreach (glob(CONST_Tiger_Data_Path . '/*.sql') as $sFile) {
511 $hFile = fopen($sFile, 'r');
512 $sSQL = fgets($hFile, 100000);
515 for ($i = 0; $i < $this->iInstances; $i++) {
516 if (!pg_connection_busy($aDBInstances[$i]->connection)) {
517 while (pg_get_result($aDBInstances[$i]->connection));
518 $sSQL = fgets($hFile, 100000);
523 if (!pg_send_query($aDBInstances[$i]->connection, $sSQL)) {
524 fail(pg_last_error($this->oDB->connection));
528 if ($iLines == 1000) {
541 for ($i = 0; $i < $this->iInstances; $i++) {
542 if (pg_connection_busy($aDBInstances[$i]->connection)) {
552 info('Creating indexes on Tiger data');
553 $sTemplate = file_get_contents(CONST_BasePath . '/sql/tiger_import_finish.sql');
554 $sTemplate = str_replace('{www-user}', CONST_Database_Web_User, $sTemplate);
555 $sTemplate = $this->replaceTablespace(
557 CONST_Tablespace_Aux_Data,
560 $sTemplate = $this->replaceTablespace(
562 CONST_Tablespace_Aux_Index,
565 $this->pgsqlRunScript($sTemplate, false);
568 public function calculatePostcodes($bCMDResultAll)
570 info('Calculate Postcodes');
571 if ($this->oDB == null) {
572 $this->oDB = &getDB();
575 if (!pg_query($this->oDB->connection, 'TRUNCATE location_postcode')) {
576 fail(pg_last_error($this->oDB->connection));
579 $sSQL = 'INSERT INTO location_postcode';
580 $sSQL .= ' (place_id, indexed_status, country_code, postcode, geometry) ';
581 $sSQL .= "SELECT nextval('seq_place'), 1, country_code,";
582 $sSQL .= " upper(trim (both ' ' from address->'postcode')) as pc,";
583 $sSQL .= ' ST_Centroid(ST_Collect(ST_Centroid(geometry)))';
584 $sSQL .= ' FROM placex';
585 $sSQL .= " WHERE address ? 'postcode' AND address->'postcode' NOT SIMILAR TO '%(,|;)%'";
586 $sSQL .= ' AND geometry IS NOT null';
587 $sSQL .= ' GROUP BY country_code, pc';
589 if (!pg_query($this->oDB->connection, $sSQL)) {
590 fail(pg_last_error($this->oDB->connection));
593 if (CONST_Use_Extra_US_Postcodes) {
594 // only add postcodes that are not yet available in OSM
595 $sSQL = 'INSERT INTO location_postcode';
596 $sSQL .= ' (place_id, indexed_status, country_code, postcode, geometry) ';
597 $sSQL .= "SELECT nextval('seq_place'), 1, 'us', postcode,";
598 $sSQL .= ' ST_SetSRID(ST_Point(x,y),4326)';
599 $sSQL .= ' FROM us_postcode WHERE postcode NOT IN';
600 $sSQL .= ' (SELECT postcode FROM location_postcode';
601 $sSQL .= " WHERE country_code = 'us')";
602 if (!pg_query($this->oDB->connection, $sSQL)) {
603 fail(pg_last_error($this->oDB->connection));
608 // add missing postcodes for GB (if available)
609 $sSQL = 'INSERT INTO location_postcode';
610 $sSQL .= ' (place_id, indexed_status, country_code, postcode, geometry) ';
611 $sSQL .= "SELECT nextval('seq_place'), 1, 'gb', postcode, geometry";
612 $sSQL .= ' FROM gb_postcode WHERE postcode NOT IN';
613 $sSQL .= ' (SELECT postcode FROM location_postcode';
614 $sSQL .= " WHERE country_code = 'gb')";
615 if (!pg_query($this->oDB->connection, $sSQL)) {
616 fail(pg_last_error($this->oDB->connection));
619 if (!$bCMDResultAll) {
620 $sSQL = "DELETE FROM word WHERE class='place' and type='postcode'";
621 $sSQL .= 'and word NOT IN (SELECT postcode FROM location_postcode)';
622 if (!pg_query($this->oDB->connection, $sSQL)) {
623 fail(pg_last_error($this->oDB->connection));
626 $sSQL = 'SELECT count(getorcreate_postcode_id(v)) FROM ';
627 $sSQL .= '(SELECT distinct(postcode) as v FROM location_postcode) p';
629 if (!pg_query($this->oDB->connection, $sSQL)) {
630 fail(pg_last_error($this->oDB->connection));
634 public function index($bIndexNoanalyse)
637 $sBaseCmd = CONST_InstallPath . '/nominatim/nominatim -i -d ' . $this->aDSNInfo['database'] . ' -P '
638 . $this->aDSNInfo['port'] . ' -t ' . $this->iInstances . $sOutputFile;
639 if (isset($this->aDSNInfo['hostspec']) && $this->aDSNInfo['hostspec']) {
640 $sBaseCmd .= ' -H ' . $this->aDSNInfo['hostspec'];
642 if (isset($this->aDSNInfo['username']) && $this->aDSNInfo['username']) {
643 $sBaseCmd .= ' -U ' . $this->aDSNInfo['username'];
646 if (isset($this->aDSNInfo['password']) && $this->aDSNInfo['password']) {
647 $aProcEnv = array_merge(array('PGPASSWORD' => $this->aDSNInfo['password']), $_ENV);
650 info('Index ranks 0 - 4');
651 $iStatus = runWithEnv($sBaseCmd . ' -R 4', $aProcEnv);
653 fail('error status ' . $iStatus . ' running nominatim!');
655 if (!$bIndexNoanalyse) {
656 $this->pgsqlRunScript('ANALYSE');
659 info('Index ranks 5 - 25');
660 $iStatus = runWithEnv($sBaseCmd . ' -r 5 -R 25', $aProcEnv);
662 fail('error status ' . $iStatus . ' running nominatim!');
664 if (!$bIndexNoanalyse) {
665 $this->pgsqlRunScript('ANALYSE');
668 info('Index ranks 26 - 30');
669 $iStatus = runWithEnv($sBaseCmd . ' -r 26', $aProcEnv);
671 fail('error status ' . $iStatus . ' running nominatim!');
673 info('Index postcodes');
674 if ($this->oDB == null) {
675 $this->oDB = &getDB();
678 $sSQL = 'UPDATE location_postcode SET indexed_status = 0';
679 if (!pg_query($this->oDB->connection, $sSQL)) {
680 fail(pg_last_error($this->oDB->connection));
685 public function createSearchIndices()
687 info('Create Search indices');
689 $sTemplate = file_get_contents(CONST_BasePath . '/sql/indices.src.sql');
690 $sTemplate = str_replace('{www-user}', CONST_Database_Web_User, $sTemplate);
691 $sTemplate = $this->replaceTablespace(
692 '{ts:address-index}',
693 CONST_Tablespace_Address_Index,
696 $sTemplate = $this->replaceTablespace(
698 CONST_Tablespace_Search_Index,
701 $sTemplate = $this->replaceTablespace(
703 CONST_Tablespace_Aux_Index,
706 $this->pgsqlRunScript($sTemplate);
709 public function createCountryNames()
711 info('Create search index for default country names');
713 $this->pgsqlRunScript("select getorcreate_country(make_standard_name('uk'), 'gb')");
714 $this->pgsqlRunScript("select getorcreate_country(make_standard_name('united states'), 'us')");
715 $this->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');
716 $this->pgsqlRunScript("select count(*) from (select getorcreate_country(make_standard_name(name->'name'), country_code) from country_name where name ? 'name') as x");
717 $sSQL = 'select count(*) from (select getorcreate_country(make_standard_name(v),'
718 . 'country_code) from (select country_code, skeys(name) as k, svals(name) as v from country_name) x where k ';
719 if (CONST_Languages) {
722 foreach (explode(',', CONST_Languages) as $sLang) {
723 $sSQL .= $sDelim . "'name:$sLang'";
728 // all include all simple name tags
729 $sSQL .= "like 'name:%'";
732 $this->pgsqlRunScript($sSQL);
735 public function drop()
737 info('Drop tables only required for updates');
739 // The implementation is potentially a bit dangerous because it uses
740 // a positive selection of tables to keep, and deletes everything else.
741 // Including any tables that the unsuspecting user might have manually
742 // created. USE AT YOUR OWN PERIL.
743 // tables we want to keep. everything else goes.
744 $aKeepTables = array(
750 'location_property*',
762 if ($this->oDB = null) {
763 $this->oDB = &getDB();
766 $aDropTables = array();
767 $aHaveTables = chksql($this->oDB->getCol("SELECT tablename FROM pg_tables WHERE schemaname='public'"));
769 foreach ($aHaveTables as $sTable) {
771 foreach ($aKeepTables as $sKeep) {
772 if (fnmatch($sKeep, $sTable)) {
778 array_push($aDropTables, $sTable);
782 foreach ($aDropTables as $sDrop) {
783 if ($this->sVerbose) {
784 echo "dropping table $sDrop\n";
787 @pg_query($this->oDB->connection, "DROP TABLE $sDrop CASCADE");
788 // ignore warnings/errors as they might be caused by a table having
789 // been deleted already by CASCADE
792 if (!is_null(CONST_Osm2pgsql_Flatnode_File) && CONST_Osm2pgsql_Flatnode_File) {
794 echo 'deleting ' . CONST_Osm2pgsql_Flatnode_File . "\n";
797 unlink(CONST_Osm2pgsql_Flatnode_File);
801 private function pgsqlRunDropAndRestore($sDumpFile)
803 if (!isset($this->aDSNInfo['port']) || !$this->aDSNInfo['port']) {
804 $this->aDSNInfo['port'] = 5432;
807 $sCMD = 'pg_restore -p ' . $this->aDSNInfo['port'] . ' -d ' . $this->aDSNInfo['database'] . ' -Fc --clean ' . $sDumpFile;
808 if (isset($this->aDSNInfo['hostspec']) && $this->aDSNInfo['hostspec']) {
809 $sCMD .= ' -h ' . $this->aDSNInfo['hostspec'];
811 if (isset($this->aDSNInfo['username']) && $this->aDSNInfo['username']) {
812 $sCMD .= ' -U ' . $this->aDSNInfo['username'];
815 if (isset($this->aDSNInfo['password']) && $this->aDSNInfo['password']) {
816 $aProcEnv = array_merge(array('PGPASSWORD' => $this->aDSNInfo['password']), $_ENV);
818 $iReturn = runWithEnv($sCMD, $aProcEnv); // /lib/cmd.php "function runWithEnv($sCmd, $aEnv)"
821 private function pgsqlRunScript($sScript, $bfatal = true)
831 private function createSqlFunctions()
833 $sTemplate = file_get_contents(CONST_BasePath . '/sql/functions.sql');
834 $sTemplate = str_replace('{modulepath}', $this->sModulePath, $sTemplate);
835 if ($this->bEnableDiffUpdates) {
836 $sTemplate = str_replace('RETURN NEW; -- %DIFFUPDATES%', '--', $sTemplate);
838 if ($this->bEnableDebugStatements) {
839 $sTemplate = str_replace('--DEBUG:', '', $sTemplate);
841 if (CONST_Limit_Reindexing) {
842 $sTemplate = str_replace('--LIMIT INDEXING:', '', $sTemplate);
844 if (!CONST_Use_US_Tiger_Data) {
845 $sTemplate = str_replace('-- %NOTIGERDATA% ', '', $sTemplate);
847 if (!CONST_Use_Aux_Location_data) {
848 $sTemplate = str_replace('-- %NOAUXDATA% ', '', $sTemplate);
850 $this->pgsqlRunScript($sTemplate);
853 private function pgsqlRunPartitionScript($sTemplate)
855 if ($this->oDB == null) {
856 $this->oDB = &getDB();
859 $sSQL = 'select distinct partition from country_name';
860 $aPartitions = chksql($this->oDB->getCol($sSQL));
861 if (!$this->bNoPartitions) {
865 preg_match_all('#^-- start(.*?)^-- end#ms', $sTemplate, $aMatches, PREG_SET_ORDER);
866 foreach ($aMatches as $aMatch) {
868 foreach ($aPartitions as $sPartitionName) {
869 $sResult .= str_replace('-partition-', $sPartitionName, $aMatch[1]);
871 $sTemplate = str_replace($aMatch[0], $sResult, $sTemplate);
874 $this->pgsqlRunScript($sTemplate);
877 private function pgsqlRunScriptFile($sFilename)
879 if (!file_exists($sFilename)) {
880 fail('unable to find ' . $sFilename);
883 $sCMD = 'psql -p ' . $this->aDSNInfo['port'] . ' -d ' . $this->aDSNInfo['database'];
884 if (!$this->sVerbose) {
887 if (isset($this->aDSNInfo['hostspec']) && $this->aDSNInfo['hostspec']) {
888 $sCMD .= ' -h ' . $this->aDSNInfo['hostspec'];
890 if (isset($this->aDSNInfo['username']) && $this->aDSNInfo['username']) {
891 $sCMD .= ' -U ' . $this->aDSNInfo['username'];
894 if (isset($this->aDSNInfo['password']) && $this->aDSNInfo['password']) {
895 $aProcEnv = array_merge(array('PGPASSWORD' => $this->aDSNInfo['password']), $_ENV);
898 if (preg_match('/\\.gz$/', $sFilename)) {
899 $aDescriptors = array(
900 0 => array('pipe', 'r'),
901 1 => array('pipe', 'w'),
902 2 => array('file', '/dev/null', 'a'),
904 $hGzipProcess = proc_open('zcat ' . $sFilename, $aDescriptors, $ahGzipPipes);
905 if (!is_resource($hGzipProcess)) {
906 fail('unable to start zcat');
909 $aReadPipe = $ahGzipPipes[1];
910 fclose($ahGzipPipes[0]);
912 $sCMD .= ' -f ' . $sFilename;
913 $aReadPipe = array('pipe', 'r');
915 $aDescriptors = array(
917 1 => array('pipe', 'w'),
918 2 => array('file', '/dev/null', 'a'),
921 $hProcess = proc_open($sCMD, $aDescriptors, $ahPipes, null, $aProcEnv);
922 if (!is_resource($hProcess)) {
923 fail('unable to start pgsql');
926 // TODO: error checking
927 while (!feof($ahPipes[1])) {
928 echo fread($ahPipes[1], 4096);
931 $iReturn = proc_close($hProcess);
933 fail("pgsql returned with error code ($iReturn)");
936 fclose($ahGzipPipes[1]);
937 proc_close($hGzipProcess);
941 private function replaceTablespace($sTemplate, $sTablespace, $sSql)
944 $sSql = str_replace($sTemplate, 'TABLESPACE "' . $sTablespace . '"', $sSql);
946 $sSql = str_replace($sTemplate, '', $sSql);