]> git.openstreetmap.org Git - nominatim.git/commitdiff
Merge remote-tracking branch 'upstream/master'
authorSarah Hoffmann <lonvia@denofr.de>
Tue, 27 Nov 2018 21:38:58 +0000 (22:38 +0100)
committerSarah Hoffmann <lonvia@denofr.de>
Tue, 27 Nov 2018 21:38:58 +0000 (22:38 +0100)
16 files changed:
docs/admin/Import-and-Update.md
lib/ParameterParser.php
lib/Result.php
lib/db.php
lib/setup/SetupClass.php
lib/template/error-json.php
settings/settings.php
sql/functions.sql
sql/indices.src.sql
sql/indices_search.src.sql [new file with mode: 0644]
sql/partition-functions.src.sql
test/Makefile [new file with mode: 0644]
test/README.md
test/php/Nominatim/ParameterParserTest.php
utils/setup.php
utils/update.php

index 847aa37d8eb98956d92b4789d0c7a976ada7df96..179b729ee11a93616c93d49beef4509fc450a258 100644 (file)
@@ -98,6 +98,15 @@ you also need to enable these key phrases like this:
 
 Note that this command downloads the phrases from the wiki link above.
 
+### Reverse-only Imports
+
+If you only want to use the Nominatim database for reverse lookups or
+if you plan to use the installation only for exports to a
+[photon](http://photon.komoot.de/) database, then you can set up a database
+without search indexes. Add `--reverse-only` to your setup command above.
+
+This saves about 5% of disk space.
+
 
 ## Installing Tiger housenumber data for the US
 
index 26b648bb2865165d8b9ee897263b2ebed17016f1..ad281d70b634260bfcac15d4656c425d4299e4f1 100644 (file)
@@ -91,7 +91,7 @@ class ParameterParser
         $sLangString = $this->getString('accept-language', $sFallback);
 
         if ($sLangString) {
-            if (preg_match_all('/(([a-z]{1,8})(-[a-z]{1,8})?)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/i', $sLangString, $aLanguagesParse, PREG_SET_ORDER)) {
+            if (preg_match_all('/(([a-z]{1,8})([-_][a-z]{1,8})?)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/i', $sLangString, $aLanguagesParse, PREG_SET_ORDER)) {
                 foreach ($aLanguagesParse as $iLang => $aLanguage) {
                     $aLanguages[$aLanguage[1]] = isset($aLanguage[5])?(float)$aLanguage[5]:1 - ($iLang/100);
                     if (!isset($aLanguages[$aLanguage[2]])) $aLanguages[$aLanguage[2]] = $aLanguages[$aLanguage[1]]/10;
index 5c39f473746b1dffd7d9ffd93d7343a73b59c750..a67c2fe474910a20356ac05edafdac68e3168c17 100644 (file)
@@ -72,7 +72,7 @@ class Result
     /**
      * Split a result array into highest ranked result and the rest
      *
-     * @param object[] $aResults  List of results to split.
+     * @param object[] $aResults List of results to split.
      *
      * @return array[]
      */
index 493d25f5f8860c489d81e2b8a2c6b8d53fd65933..8dbe45355fd2ff7068fb37fc189703d998557463 100644 (file)
@@ -30,14 +30,14 @@ function getArraySQL($a)
 
 function getPostgresVersion(&$oDB)
 {
-    $sVersionString = $oDB->getOne('select version()');
-    preg_match('#PostgreSQL ([0-9]+)[.]([0-9]+)[^0-9]#', $sVersionString, $aMatches);
+    $sVersionString = $oDB->getOne('SHOW server_version_num');
+    preg_match('#([0-9]?[0-9])([0-9][0-9])[0-9][0-9]#', $sVersionString, $aMatches);
     return (float) ($aMatches[1].'.'.$aMatches[2]);
 }
 
 function getPostgisVersion(&$oDB)
 {
-    $sVersionString = $oDB->getOne('select postgis_full_version()');
-    preg_match('#POSTGIS="([0-9]+)[.]([0-9]+)[.]([0-9]+)( r([0-9]+))?"#', $sVersionString, $aMatches);
+    $sVersionString = $oDB->getOne('select postgis_lib_version()');
+    preg_match('#^([0-9]+)[.]([0-9]+)[.]#', $sVersionString, $aMatches);
     return (float) ($aMatches[1].'.'.$aMatches[2]);
 }
index 5e5b16d6c04f08f68f648ba06d8f5f36d3516e51..5c6d69e43bcf52160b943008b05c7a506a27d642 100755 (executable)
@@ -4,17 +4,16 @@ namespace Nominatim\Setup;
 
 class SetupFunctions
 {
-    protected $iCacheMemory;            // set in constructor
-    protected $iInstances;              // set in constructor
-    protected $sModulePath;             // set in constructor
-    protected $aDSNInfo;                // set in constructor = DB::parseDSN(CONST_Database_DSN);
-    protected $sVerbose;                // set in constructor
-    protected $sIgnoreErrors;           // set in constructor
-    protected $bEnableDiffUpdates;      // set in constructor
-    protected $bEnableDebugStatements;  // set in constructor
-    protected $bNoPartitions;           // set in constructor
-    protected $oDB = null;              // set in setupDB (earliest) or later in loadData, importData, drop, createSqlFunctions, importTigerData
-                                                // pgsqlRunPartitionScript, calculatePostcodes, ..if no already set
+    protected $iCacheMemory;
+    protected $iInstances;
+    protected $sModulePath;
+    protected $aDSNInfo;
+    protected $sVerbose;
+    protected $sIgnoreErrors;
+    protected $bEnableDiffUpdates;
+    protected $bEnableDebugStatements;
+    protected $bNoPartitions;
+    protected $oDB = null;
 
     public function __construct(array $aCMDResult)
     {
@@ -38,9 +37,11 @@ class SetupFunctions
         $this->sModulePath = CONST_Database_Module_Path;
         info('module path: ' . $this->sModulePath);
 
-        // prepares DB for import or update, sets the Data Source Name
-        $this->aDSNInfo = \DB::parseDSN(CONST_Database_DSN);
-        if (!isset($this->aDSNInfo['port']) || !$this->aDSNInfo['port']) $this->aDSNInfo['port'] = 5432;
+        // parse database string
+        $this->aDSNInfo = array_filter(\DB::parseDSN(CONST_Database_DSN));
+        if (!isset($this->aDSNInfo['port'])) {
+            $this->aDSNInfo['port'] = 5432;
+        }
 
         // setting member variables based on command line options stored in $aCMDResult
         $this->sVerbose = $aCMDResult['verbose'];
@@ -77,32 +78,31 @@ class SetupFunctions
         }
 
         $sCreateDBCmd = 'createdb -E UTF-8 -p '.$this->aDSNInfo['port'].' '.$this->aDSNInfo['database'];
-        if (isset($this->aDSNInfo['username']) && $this->aDSNInfo['username']) {
+        if (isset($this->aDSNInfo['username'])) {
             $sCreateDBCmd .= ' -U '.$this->aDSNInfo['username'];
         }
 
-        if (isset($this->aDSNInfo['hostspec']) && $this->aDSNInfo['hostspec']) {
+        if (isset($this->aDSNInfo['hostspec'])) {
             $sCreateDBCmd .= ' -h '.$this->aDSNInfo['hostspec'];
         }
 
-        $aProcEnv = null;
-        if (isset($this->aDSNInfo['password']) && $this->aDSNInfo['password']) {
-            $aProcEnv = array_merge(array('PGPASSWORD' => $this->aDSNInfo['password']), $_ENV);
-        }
-
-        $result = runWithEnv($sCreateDBCmd, $aProcEnv);
+        $result = $this->runWithPgEnv($sCreateDBCmd);
         if ($result != 0) fail('Error executing external command: '.$sCreateDBCmd);
     }
 
+    public function connect()
+    {
+        $this->oDB =& getDB();
+    }
+
     public function setupDB()
     {
         info('Setup DB');
-        $this->oDB =& getDB();
 
         $fPostgresVersion = getPostgresVersion($this->oDB);
         echo 'Postgres version found: '.$fPostgresVersion."\n";
 
-        if ($fPostgresVersion < 9.1) {
+        if ($fPostgresVersion < 9.01) {
             fail('Minimum supported version of Postgresql is 9.1.');
         }
 
@@ -203,19 +203,16 @@ class SetupFunctions
         $osm2pgsql .= ' -lsc -O gazetteer --hstore --number-processes 1';
         $osm2pgsql .= ' -C '.$this->iCacheMemory;
         $osm2pgsql .= ' -P '.$this->aDSNInfo['port'];
-        if (isset($this->aDSNInfo['username']) && $this->aDSNInfo['username']) {
+        if (isset($this->aDSNInfo['username'])) {
             $osm2pgsql .= ' -U '.$this->aDSNInfo['username'];
         }
-        if (isset($this->aDSNInfo['hostspec']) && $this->aDSNInfo['hostspec']) {
+        if (isset($this->aDSNInfo['hostspec'])) {
             $osm2pgsql .= ' -H '.$this->aDSNInfo['hostspec'];
         }
-        $aProcEnv = null;
-        if (isset($this->aDSNInfo['password']) && $this->aDSNInfo['password']) {
-            $aProcEnv = array_merge(array('PGPASSWORD' => $this->aDSNInfo['password']), $_ENV);
-        }
         $osm2pgsql .= ' -d '.$this->aDSNInfo['database'].' '.$sOSMFile;
-        runWithEnv($osm2pgsql, $aProcEnv);
-        if ($this->oDB == null) $this->oDB =& getDB();
+
+        $this->runWithPgEnv($osm2pgsql);
+
         if (!$this->sIgnoreErrors && !chksql($this->oDB->getRow('select * from place limit 1'))) {
             fail('No Data');
         }
@@ -233,7 +230,7 @@ class SetupFunctions
         $this->createSqlFunctions();
     }
 
-    public function createTables()
+    public function createTables($bReverseOnly = false)
     {
         info('Create Tables');
 
@@ -271,6 +268,10 @@ class SetupFunctions
         );
 
         $this->pgsqlRunScript($sTemplate, false);
+
+        if ($bReverseOnly) {
+            $this->pgExec('DROP TABLE search_name');
+        }
     }
 
     public function createPartitionTables()
@@ -347,43 +348,41 @@ class SetupFunctions
     {
         info('Drop old Data');
 
-        if ($this->oDB == null) $this->oDB =& getDB();
-
-        if (!pg_query($this->oDB->connection, 'TRUNCATE word')) fail(pg_last_error($this->oDB->connection));
+        $this->pgExec('TRUNCATE word');
         echo '.';
-        if (!pg_query($this->oDB->connection, 'TRUNCATE placex')) fail(pg_last_error($this->oDB->connection));
+        $this->pgExec('TRUNCATE placex');
         echo '.';
-        if (!pg_query($this->oDB->connection, 'TRUNCATE location_property_osmline')) fail(pg_last_error($this->oDB->connection));
+        $this->pgExec('TRUNCATE location_property_osmline');
         echo '.';
-        if (!pg_query($this->oDB->connection, 'TRUNCATE place_addressline')) fail(pg_last_error($this->oDB->connection));
+        $this->pgExec('TRUNCATE place_addressline');
         echo '.';
-        if (!pg_query($this->oDB->connection, 'TRUNCATE place_boundingbox')) fail(pg_last_error($this->oDB->connection));
+        $this->pgExec('TRUNCATE place_boundingbox');
         echo '.';
-        if (!pg_query($this->oDB->connection, 'TRUNCATE location_area')) fail(pg_last_error($this->oDB->connection));
+        $this->pgExec('TRUNCATE location_area');
         echo '.';
-        if (!pg_query($this->oDB->connection, 'TRUNCATE search_name')) fail(pg_last_error($this->oDB->connection));
-        echo '.';
-        if (!pg_query($this->oDB->connection, 'TRUNCATE search_name_blank')) fail(pg_last_error($this->oDB->connection));
+        if (!$this->dbReverseOnly()) {
+            $this->pgExec('TRUNCATE search_name');
+            echo '.';
+        }
+        $this->pgExec('TRUNCATE search_name_blank');
         echo '.';
-        if (!pg_query($this->oDB->connection, 'DROP SEQUENCE seq_place')) fail(pg_last_error($this->oDB->connection));
+        $this->pgExec('DROP SEQUENCE seq_place');
         echo '.';
-        if (!pg_query($this->oDB->connection, 'CREATE SEQUENCE seq_place start 100000')) fail(pg_last_error($this->oDB->connection));
+        $this->pgExec('CREATE SEQUENCE seq_place start 100000');
         echo '.';
 
         $sSQL = 'select distinct partition from country_name';
         $aPartitions = chksql($this->oDB->getCol($sSQL));
         if (!$this->bNoPartitions) $aPartitions[] = 0;
         foreach ($aPartitions as $sPartition) {
-            if (!pg_query($this->oDB->connection, 'TRUNCATE location_road_'.$sPartition)) fail(pg_last_error($this->oDB->connection));
+            $this->pgExec('TRUNCATE location_road_'.$sPartition);
             echo '.';
         }
 
         // used by getorcreate_word_id to ignore frequent partial words
         $sSQL = 'CREATE OR REPLACE FUNCTION get_maxwordfreq() RETURNS integer AS ';
         $sSQL .= '$$ SELECT '.CONST_Max_Word_Frequency.' as maxwordfreq; $$ LANGUAGE SQL IMMUTABLE';
-        if (!pg_query($this->oDB->connection, $sSQL)) {
-            fail(pg_last_error($this->oDB->connection));
-        }
+        $this->pgExec($sSQL);
         echo ".\n";
 
         // pre-create the word list
@@ -528,11 +527,7 @@ class SetupFunctions
     public function calculatePostcodes($bCMDResultAll)
     {
         info('Calculate Postcodes');
-        if ($this->oDB == null) $this->oDB =& getDB();
-        if (!pg_query($this->oDB->connection, 'TRUNCATE location_postcode')) {
-            fail(pg_last_error($this->oDB->connection));
-        }
-
+        $this->pgExec('TRUNCATE location_postcode');
 
         $sSQL  = 'INSERT INTO location_postcode';
         $sSQL .= ' (place_id, indexed_status, country_code, postcode, geometry) ';
@@ -543,10 +538,7 @@ class SetupFunctions
         $sSQL .= " WHERE address ? 'postcode' AND address->'postcode' NOT SIMILAR TO '%(,|;)%'";
         $sSQL .= '       AND geometry IS NOT null';
         $sSQL .= ' GROUP BY country_code, pc';
-
-        if (!pg_query($this->oDB->connection, $sSQL)) {
-            fail(pg_last_error($this->oDB->connection));
-        }
+        $this->pgExec($sSQL);
 
         if (CONST_Use_Extra_US_Postcodes) {
             // only add postcodes that are not yet available in OSM
@@ -557,7 +549,7 @@ class SetupFunctions
             $sSQL .= '  FROM us_postcode WHERE postcode NOT IN';
             $sSQL .= '        (SELECT postcode FROM location_postcode';
             $sSQL .= "          WHERE country_code = 'us')";
-            if (!pg_query($this->oDB->connection, $sSQL)) fail(pg_last_error($this->oDB->connection));
+            $this->pgExec($sSQL);
         }
 
         // add missing postcodes for GB (if available)
@@ -567,21 +559,17 @@ class SetupFunctions
         $sSQL .= '  FROM gb_postcode WHERE postcode NOT IN';
         $sSQL .= '           (SELECT postcode FROM location_postcode';
         $sSQL .= "             WHERE country_code = 'gb')";
-        if (!pg_query($this->oDB->connection, $sSQL)) fail(pg_last_error($this->oDB->connection));
+        $this->pgExec($sSQL);
 
         if (!$bCMDResultAll) {
             $sSQL = "DELETE FROM word WHERE class='place' and type='postcode'";
             $sSQL .= 'and word NOT IN (SELECT postcode FROM location_postcode)';
-            if (!pg_query($this->oDB->connection, $sSQL)) {
-                fail(pg_last_error($this->oDB->connection));
-            }
+            $this->pgExec($sSQL);
         }
+
         $sSQL = 'SELECT count(getorcreate_postcode_id(v)) FROM ';
         $sSQL .= '(SELECT distinct(postcode) as v FROM location_postcode) p';
-
-        if (!pg_query($this->oDB->connection, $sSQL)) {
-            fail(pg_last_error($this->oDB->connection));
-        }
+        $this->pgExec($sSQL);
     }
 
     public function index($bIndexNoanalyse)
@@ -589,38 +577,36 @@ class SetupFunctions
         $sOutputFile = '';
         $sBaseCmd = CONST_InstallPath.'/nominatim/nominatim -i -d '.$this->aDSNInfo['database'].' -P '
             .$this->aDSNInfo['port'].' -t '.$this->iInstances.$sOutputFile;
-        if (isset($this->aDSNInfo['hostspec']) && $this->aDSNInfo['hostspec']) {
+        if (isset($this->aDSNInfo['hostspec'])) {
             $sBaseCmd .= ' -H '.$this->aDSNInfo['hostspec'];
         }
-        if (isset($this->aDSNInfo['username']) && $this->aDSNInfo['username']) {
+        if (isset($this->aDSNInfo['username'])) {
             $sBaseCmd .= ' -U '.$this->aDSNInfo['username'];
         }
-        $aProcEnv = null;
-        if (isset($this->aDSNInfo['password']) && $this->aDSNInfo['password']) {
-            $aProcEnv = array_merge(array('PGPASSWORD' => $this->aDSNInfo['password']), $_ENV);
-        }
 
         info('Index ranks 0 - 4');
-        $iStatus = runWithEnv($sBaseCmd.' -R 4', $aProcEnv);
+        $iStatus = $this->runWithPgEnv($sBaseCmd.' -R 4');
         if ($iStatus != 0) {
             fail('error status ' . $iStatus . ' running nominatim!');
         }
         if (!$bIndexNoanalyse) $this->pgsqlRunScript('ANALYSE');
+
         info('Index ranks 5 - 25');
-        $iStatus = runWithEnv($sBaseCmd.' -r 5 -R 25', $aProcEnv);
+        $iStatus = $this->runWithPgEnv($sBaseCmd.' -r 5 -R 25');
         if ($iStatus != 0) {
             fail('error status ' . $iStatus . ' running nominatim!');
         }
         if (!$bIndexNoanalyse) $this->pgsqlRunScript('ANALYSE');
+
         info('Index ranks 26 - 30');
-        $iStatus = runWithEnv($sBaseCmd.' -r 26', $aProcEnv);
+        $iStatus = $this->runWithPgEnv($sBaseCmd.' -r 26');
         if ($iStatus != 0) {
             fail('error status ' . $iStatus . ' running nominatim!');
         }
+
         info('Index postcodes');
-        if ($this->oDB == null) $this->oDB =& getDB();
         $sSQL = 'UPDATE location_postcode SET indexed_status = 0';
-        if (!pg_query($this->oDB->connection, $sSQL)) fail(pg_last_error($this->oDB->connection));
+        $this->pgExec($sSQL);
     }
 
     public function createSearchIndices()
@@ -628,6 +614,9 @@ class SetupFunctions
         info('Create Search indices');
 
         $sTemplate = file_get_contents(CONST_BasePath.'/sql/indices.src.sql');
+        if (!$this->dbReverseOnly()) {
+            $sTemplate .= file_get_contents(CONST_BasePath.'/sql/indices_search.src.sql');
+        }
         $sTemplate = str_replace('{www-user}', CONST_Database_Web_User, $sTemplate);
         $sTemplate = $this->replaceTablespace(
             '{ts:address-index}',
@@ -700,7 +689,6 @@ class SetupFunctions
                         'place_classtype_*'
                        );
 
-        if ($this->oDB = null) $this->oDB =& getDB();
         $aDropTables = array();
         $aHaveTables = chksql($this->oDB->getCol("SELECT tablename FROM pg_tables WHERE schemaname='public'"));
 
@@ -715,33 +703,31 @@ class SetupFunctions
             if (!$bFound) array_push($aDropTables, $sTable);
         }
         foreach ($aDropTables as $sDrop) {
-            if ($this->sVerbose) echo "dropping table $sDrop\n";
+            if ($this->sVerbose) echo "Dropping table $sDrop\n";
             @pg_query($this->oDB->connection, "DROP TABLE $sDrop CASCADE");
             // ignore warnings/errors as they might be caused by a table having
             // been deleted already by CASCADE
         }
 
         if (!is_null(CONST_Osm2pgsql_Flatnode_File) && CONST_Osm2pgsql_Flatnode_File) {
-            if ($sVerbose) echo 'deleting '.CONST_Osm2pgsql_Flatnode_File."\n";
-            unlink(CONST_Osm2pgsql_Flatnode_File);
+            if (file_exists(CONST_Osm2pgsql_Flatnode_File)) {
+                if ($this->sVerbose) echo 'Deleting '.CONST_Osm2pgsql_Flatnode_File."\n";
+                unlink(CONST_Osm2pgsql_Flatnode_File);
+            }
         }
     }
 
     private function pgsqlRunDropAndRestore($sDumpFile)
     {
-        if (!isset($this->aDSNInfo['port']) || !$this->aDSNInfo['port']) $this->aDSNInfo['port'] = 5432;
         $sCMD = 'pg_restore -p '.$this->aDSNInfo['port'].' -d '.$this->aDSNInfo['database'].' -Fc --clean '.$sDumpFile;
-        if (isset($this->aDSNInfo['hostspec']) && $this->aDSNInfo['hostspec']) {
+        if (isset($this->aDSNInfo['hostspec'])) {
             $sCMD .= ' -h '.$this->aDSNInfo['hostspec'];
         }
-        if (isset($this->aDSNInfo['username']) && $this->aDSNInfo['username']) {
+        if (isset($this->aDSNInfo['username'])) {
             $sCMD .= ' -U '.$this->aDSNInfo['username'];
         }
-        $aProcEnv = null;
-        if (isset($this->aDSNInfo['password']) && $this->aDSNInfo['password']) {
-            $aProcEnv = array_merge(array('PGPASSWORD' => $this->aDSNInfo['password']), $_ENV);
-        }
-        $iReturn = runWithEnv($sCMD, $aProcEnv);    // /lib/cmd.php "function runWithEnv($sCmd, $aEnv)"
+
+        $this->runWithPgEnv($sCMD);
     }
 
     private function pgsqlRunScript($sScript, $bfatal = true)
@@ -773,13 +759,15 @@ class SetupFunctions
         if (!CONST_Use_Aux_Location_data) {
             $sTemplate = str_replace('-- %NOAUXDATA% ', '', $sTemplate);
         }
+
+        $sReverseOnly = $this->dbReverseOnly() ? 'true' : 'false';
+        $sTemplate = str_replace('%REVERSE-ONLY%', $sReverseOnly, $sTemplate);
+
         $this->pgsqlRunScript($sTemplate);
     }
 
     private function pgsqlRunPartitionScript($sTemplate)
     {
-        if ($this->oDB == null) $this->oDB =& getDB();
-
         $sSQL = 'select distinct partition from country_name';
         $aPartitions = chksql($this->oDB->getCol($sSQL));
         if (!$this->bNoPartitions) $aPartitions[] = 0;
@@ -804,14 +792,14 @@ class SetupFunctions
         if (!$this->sVerbose) {
             $sCMD .= ' -q';
         }
-        if (isset($this->aDSNInfo['hostspec']) && $this->aDSNInfo['hostspec']) {
+        if (isset($this->aDSNInfo['hostspec'])) {
             $sCMD .= ' -h '.$this->aDSNInfo['hostspec'];
         }
-        if (isset($this->aDSNInfo['username']) && $this->aDSNInfo['username']) {
+        if (isset($this->aDSNInfo['username'])) {
             $sCMD .= ' -U '.$this->aDSNInfo['username'];
         }
         $aProcEnv = null;
-        if (isset($this->aDSNInfo['password']) && $this->aDSNInfo['password']) {
+        if (isset($this->aDSNInfo['password'])) {
             $aProcEnv = array_merge(array('PGPASSWORD' => $this->aDSNInfo['password']), $_ENV);
         }
         $ahGzipPipes = null;
@@ -861,4 +849,42 @@ class SetupFunctions
         }
         return $sSql;
     }
+
+    private function runWithPgEnv($sCmd)
+    {
+        $aProcEnv = null;
+
+        if (isset($this->aDSNInfo['password'])) {
+            $aProcEnv = array_merge(array('PGPASSWORD' => $this->aDSNInfo['password']), $_ENV);
+        }
+
+        return runWithEnv($sCmd, $aProcEnv);
+    }
+
+    /**
+     * Execute the SQL command on the open database.
+     *
+     * @param string $sSQL SQL command to execute.
+     *
+     * @return null
+     *
+     * @pre connect() must have been called.
+     */
+    private function pgExec($sSQL)
+    {
+        if (!pg_query($this->oDB->connection, $sSQL)) {
+            fail(pg_last_error($this->oDB->connection));
+        }
+    }
+
+    /**
+     * Check if the database is in reverse-only mode.
+     *
+     * @return True if there is no search_name table and infrastructure.
+     */
+    private function dbReverseOnly()
+    {
+        $sSQL = "SELECT count(*) FROM pg_tables WHERE tablename = 'search_name'";
+        return !(chksql($this->oDB->getOne($sSQL)));
+    }
 }
index 81caa71d807522ea18907b3a741817c3c98bbfab..67297dd1dcf3a6f992a50a099d7f89b25a06a906 100644 (file)
@@ -2,7 +2,7 @@
     $error = array(
               'code' => $exception->getCode(),
               'message' => $exception->getMessage()
-    );
+             );
 
     if (CONST_Debug) {
         $error['details'] = $exception->getFile() . '('. $exception->getLine() . ')';
index d45a42376cb3cfe055b62b7dd672b5b927fd91f0..907a9fa0b530804bd776532abf6a3000ba5f2b39 100644 (file)
@@ -1,4 +1,4 @@
 <?php
 
 echo "ERROR: Scripts must be run from build directory.\n";
-exit;
+exit(1);
index f1f473b6460626806b5bc20feb3dee799983b2f3..13d897d029fa3b8cdd49ab93bdb77caa17021cf7 100644 (file)
@@ -1310,6 +1310,9 @@ BEGIN
 
   NEW.indexed_date = now();
 
+  IF NOT %REVERSE-ONLY% THEN
+    DELETE from search_name WHERE place_id = NEW.place_id;
+  END IF;
   result := deleteSearchName(NEW.partition, NEW.place_id);
   DELETE FROM place_addressline WHERE place_id = NEW.place_id;
   result := deleteRoad(NEW.partition, NEW.place_id);
@@ -1576,8 +1579,9 @@ BEGIN
     IF NEW.parent_place_id IS NOT NULL THEN
 
       -- Get the details of the parent road
-      select s.country_code, s.name_vector, s.nameaddress_vector from search_name s
-       where s.place_id = NEW.parent_place_id INTO location;
+      SELECT p.country_code, p.postcode FROM placex p
+       WHERE p.place_id = NEW.parent_place_id INTO location;
+
       NEW.country_code := location.country_code;
       --DEBUG: RAISE WARNING 'Got parent details from search name';
 
@@ -1586,7 +1590,7 @@ BEGIN
           IF NEW.address is not null AND NEW.address ? 'postcode' THEN
               NEW.postcode = upper(trim(NEW.address->'postcode'));
           ELSE
-             SELECT postcode FROM placex WHERE place_id = NEW.parent_place_id INTO NEW.postcode;
+             NEW.postcode := location.postcode;
           END IF;
           IF NEW.postcode is null THEN
             NEW.postcode := get_nearest_postcode(NEW.country_code, place_centroid);
@@ -1599,21 +1603,34 @@ BEGIN
         return NEW;
       END IF;
 
-      -- Merge address from parent
-      nameaddress_vector := array_merge(nameaddress_vector, location.nameaddress_vector);
-      nameaddress_vector := array_merge(nameaddress_vector, location.name_vector);
-
       -- Performance, it would be more acurate to do all the rest of the import
       -- process but it takes too long
       -- Just be happy with inheriting from parent road only
-
       IF NEW.rank_search <= 25 and NEW.rank_address > 0 THEN
         result := add_location(NEW.place_id, NEW.country_code, NEW.partition, name_vector, NEW.rank_search, NEW.rank_address, upper(trim(NEW.address->'postcode')), NEW.geometry);
         --DEBUG: RAISE WARNING 'Place added to location table';
       END IF;
 
-      result := insertSearchName(NEW.partition, NEW.place_id, NEW.country_code, name_vector, nameaddress_vector, NEW.rank_search, NEW.rank_address, NEW.importance, place_centroid, NEW.geometry);
-      --DEBUG: RAISE WARNING 'Place added to search table';
+      result := insertSearchName(NEW.partition, NEW.place_id, name_vector,
+                                 NEW.rank_search, NEW.rank_address, NEW.geometry);
+
+      IF NOT %REVERSE-ONLY% THEN
+          -- Merge address from parent
+          SELECT s.name_vector, s.nameaddress_vector FROM search_name s
+           WHERE s.place_id = NEW.parent_place_id INTO location;
+
+          nameaddress_vector := array_merge(nameaddress_vector,
+                                            location.nameaddress_vector);
+          nameaddress_vector := array_merge(nameaddress_vector, location.name_vector);
+
+          INSERT INTO search_name (place_id, search_rank, address_rank,
+                                   importance, country_code, name_vector,
+                                   nameaddress_vector, centroid)
+                 VALUES (NEW.place_id, NEW.rank_search, NEW.rank_address,
+                         NEW.importance, NEW.country_code, name_vector,
+                         nameaddress_vector, place_centroid);
+          --DEBUG: RAISE WARNING 'Place added to search table';
+        END IF;
 
       return NEW;
     END IF;
@@ -1799,9 +1816,11 @@ BEGIN
         IF address_street_word_id IS NOT NULL AND NOT(ARRAY[address_street_word_id] <@ isin_tokens) THEN
           isin_tokens := isin_tokens || address_street_word_id;
         END IF;
-        address_street_word_id := get_word_id(make_standard_name(addr_item.value));
-        IF address_street_word_id IS NOT NULL THEN
-          nameaddress_vector := array_merge(nameaddress_vector, ARRAY[address_street_word_id]);
+        IF NOT %REVERSE-ONLY% THEN
+          address_street_word_id := get_word_id(make_standard_name(addr_item.value));
+          IF address_street_word_id IS NOT NULL THEN
+            nameaddress_vector := array_merge(nameaddress_vector, ARRAY[address_street_word_id]);
+          END IF;
         END IF;
       END IF;
       IF addr_item.key = 'is_in' THEN
@@ -1815,16 +1834,20 @@ BEGIN
             END IF;
 
             -- merge word into address vector
-            address_street_word_id := get_word_id(make_standard_name(isin[i]));
-            IF address_street_word_id IS NOT NULL THEN
-              nameaddress_vector := array_merge(nameaddress_vector, ARRAY[address_street_word_id]);
+            IF NOT %REVERSE-ONLY% THEN
+              address_street_word_id := get_word_id(make_standard_name(isin[i]));
+              IF address_street_word_id IS NOT NULL THEN
+                nameaddress_vector := array_merge(nameaddress_vector, ARRAY[address_street_word_id]);
+              END IF;
             END IF;
           END LOOP;
         END IF;
       END IF;
     END LOOP;
   END IF;
-  nameaddress_vector := array_merge(nameaddress_vector, isin_tokens);
+  IF NOT %REVERSE-ONLY% THEN
+    nameaddress_vector := array_merge(nameaddress_vector, isin_tokens);
+  END IF;
 
 -- RAISE WARNING 'ISIN: %', isin_tokens;
 
@@ -1873,7 +1896,7 @@ BEGIN
 
       -- RAISE WARNING '% isaddress: %', location.place_id, location_isaddress;
       -- Add it to the list of search terms
-      IF location.rank_search > 4 THEN
+      IF NOT %REVERSE-ONLY% AND location.rank_search > 4 THEN
           nameaddress_vector := array_merge(nameaddress_vector, location.keywords::integer[]);
       END IF;
       INSERT INTO place_addressline (place_id, address_place_id, fromarea, isaddress, distance, cached_rank_address)
@@ -1927,8 +1950,18 @@ BEGIN
       --DEBUG: RAISE WARNING 'insert into road location table (full)';
     END IF;
 
-    result := insertSearchName(NEW.partition, NEW.place_id, NEW.country_code, name_vector, nameaddress_vector, NEW.rank_search, NEW.rank_address, NEW.importance, place_centroid, NEW.geometry);
-    --DEBUG: RAISE WARNING 'added to serach name (full)';
+    result := insertSearchName(NEW.partition, NEW.place_id, name_vector,
+                               NEW.rank_search, NEW.rank_address, NEW.geometry);
+    --DEBUG: RAISE WARNING 'added to search name (full)';
+
+    IF NOT %REVERSE-ONLY% THEN
+        INSERT INTO search_name (place_id, search_rank, address_rank,
+                                 importance, country_code, name_vector,
+                                 nameaddress_vector, centroid)
+               VALUES (NEW.place_id, NEW.rank_search, NEW.rank_address,
+                       NEW.importance, NEW.country_code, name_vector,
+                       nameaddress_vector, place_centroid);
+    END IF;
 
   END IF;
 
@@ -1987,6 +2020,9 @@ BEGIN
   --DEBUG: RAISE WARNING 'placex_delete:09 % %',OLD.osm_type,OLD.osm_id;
 
   IF OLD.name is not null THEN
+    IF NOT %REVERSE-ONLY% THEN
+      DELETE from search_name WHERE place_id = OLD.place_id;
+    END IF;
     b := deleteSearchName(OLD.partition, OLD.place_id);
   END IF;
 
index 32ac8a3d9b443c78c038bb125e1bcf15f7d4a1fd..dd16affb97531305492bc9ca46bfed9dd58deb8e 100644 (file)
@@ -3,10 +3,6 @@
 
 CREATE INDEX idx_word_word_id on word USING BTREE (word_id) {ts:search-index};
 
-CREATE INDEX idx_search_name_nameaddress_vector ON search_name USING GIN (nameaddress_vector) WITH (fastupdate = off) {ts:search-index};
-CREATE INDEX idx_search_name_name_vector ON search_name USING GIN (name_vector) WITH (fastupdate = off) {ts:search-index};
-CREATE INDEX idx_search_name_centroid ON search_name USING GIST (centroid) {ts:search-index};
-
 CREATE INDEX idx_place_addressline_address_place_id on place_addressline USING BTREE (address_place_id) {ts:search-index};
 
 DROP INDEX IF EXISTS idx_placex_rank_search;
diff --git a/sql/indices_search.src.sql b/sql/indices_search.src.sql
new file mode 100644 (file)
index 0000000..d1363fc
--- /dev/null
@@ -0,0 +1,6 @@
+-- Indices used for /search API.
+-- These indices are created only after the indexing process is done.
+
+CREATE INDEX idx_search_name_nameaddress_vector ON search_name USING GIN (nameaddress_vector) WITH (fastupdate = off) {ts:search-index};
+CREATE INDEX idx_search_name_name_vector ON search_name USING GIN (name_vector) WITH (fastupdate = off) {ts:search-index};
+CREATE INDEX idx_search_name_centroid ON search_name USING GIST (centroid) {ts:search-index};
index efb595426fe8d86f9f62bd14568fe0dd7f810627..3959661e7a4e567a5c983ed25723db5903b46399 100644 (file)
@@ -142,17 +142,11 @@ LANGUAGE plpgsql;
 
 
 create or replace function insertSearchName(
-  in_partition INTEGER, in_place_id BIGINT, in_country_code VARCHAR(2), 
-  in_name_vector INTEGER[], in_nameaddress_vector INTEGER[],
-  in_rank_search INTEGER, in_rank_address INTEGER, in_importance FLOAT,
-  in_centroid GEOMETRY, in_geometry GEOMETRY) RETURNS BOOLEAN AS $$
+  in_partition INTEGER, in_place_id BIGINT, in_name_vector INTEGER[],
+  in_rank_search INTEGER, in_rank_address INTEGER, in_geometry GEOMETRY)
+RETURNS BOOLEAN AS $$
 DECLARE
 BEGIN
-
-  DELETE FROM search_name WHERE place_id = in_place_id;
-  INSERT INTO search_name (place_id, search_rank, address_rank, importance, country_code, name_vector, nameaddress_vector, centroid)
-    values (in_place_id, in_rank_search, in_rank_address, in_importance, in_country_code, in_name_vector, in_nameaddress_vector, in_centroid);
-
 -- start
   IF in_partition = -partition- THEN
     DELETE FROM search_name_-partition- values WHERE place_id = in_place_id;
@@ -173,9 +167,6 @@ LANGUAGE plpgsql;
 create or replace function deleteSearchName(in_partition INTEGER, in_place_id BIGINT) RETURNS BOOLEAN AS $$
 DECLARE
 BEGIN
-
-  DELETE from search_name WHERE place_id = in_place_id;
-
 -- start
   IF in_partition = -partition- THEN
     DELETE from search_name_-partition- WHERE place_id = in_place_id;
diff --git a/test/Makefile b/test/Makefile
new file mode 100644 (file)
index 0000000..0355a17
--- /dev/null
@@ -0,0 +1,9 @@
+all: bdd php
+
+bdd:
+       cd bdd && behave -DREMOVE_TEMPLATE=1
+
+php:
+       cd php && phpunit ./
+
+.PHONY: bdd php
index 9b777412d20830d77d19800ef2fcc798f3f32699..d90b6f131418695fcc550dea2b3559d2587035bc 100644 (file)
@@ -68,7 +68,7 @@ To run the functional tests, do
 
 The tests can be configured with a set of environment variables:
 
- * `BUILD_DIR` - build directory of Nominatim installation to test
+ * `BUILDDIR` - build directory of Nominatim installation to test
  * `TEMPLATE_DB` - name of template database used as a skeleton for
                    the test databases (db tests)
  * `TEST_DB` - name of test database (db tests)
index 9f51a629f09d99856a9d4bbf6b7748dfa9a27542..ee2f5e1897d8652cf3d7bef9c2a034b1aaba6a72 100644 (file)
@@ -223,5 +223,27 @@ class ParameterParserTest extends \PHPUnit\Framework\TestCase
                            'ref' => 'ref',
                            'type' => 'type',
                           ), $oParams->getPreferredLanguages('default'));
+
+        $oParams = new ParameterParser(array('accept-language' => 'ja_rm,zh_pinyin'));
+        $this->assertSame(array(
+                           'short_name:ja_rm' => 'short_name:ja_rm',
+                           'name:ja_rm' => 'name:ja_rm',
+                           'short_name:zh_pinyin' => 'short_name:zh_pinyin',
+                           'name:zh_pinyin' => 'name:zh_pinyin',
+                           'short_name:ja' => 'short_name:ja',
+                           'name:ja' => 'name:ja',
+                           'short_name:zh' => 'short_name:zh',
+                           'name:zh' => 'name:zh',
+                           'short_name' => 'short_name',
+                           'name' => 'name',
+                           'brand' => 'brand',
+                           'official_name:ja_rm' => 'official_name:ja_rm',
+                           'official_name:zh_pinyin' => 'official_name:zh_pinyin',
+                           'official_name:ja' => 'official_name:ja',
+                           'official_name:zh' => 'official_name:zh',
+                           'official_name' => 'official_name',
+                           'ref' => 'ref',
+                           'type' => 'type',
+                          ), $oParams->getPreferredLanguages('default'));
     }
 }
index ea3e14a35765fbd8ab4db7ba70c6370a8a3c1d7e..0d2e65835f766f886817256e654dcae608b8ceb4 100755 (executable)
@@ -29,6 +29,7 @@ $aCMDOptions
    array('setup-db', '', 0, 1, 0, 0, 'bool', 'Build a blank nominatim db'),
    array('import-data', '', 0, 1, 0, 0, 'bool', 'Import a osm file'),
    array('osm2pgsql-cache', '', 0, 1, 1, 1, 'int', 'Cache size used by osm2pgsql'),
+   array('reverse-only', '', 0, 1, 0, 0, 'bool', 'Do not create search tables and indexes'),
    array('create-functions', '', 0, 1, 0, 0, 'bool', 'Create functions'),
    array('enable-diff-updates', '', 0, 1, 0, 0, 'bool', 'Turn on the code required to make diff updates work'),
    array('enable-debug-statements', '', 0, 1, 0, 0, 'bool', 'Include debug warning statements in pgsql commands'),
@@ -71,18 +72,20 @@ if ($aCMDResult['osmosis-init']) {
 
 // ******************************************************
 // instantiate Setup class
-$cSetup = new SetupFunctions($aCMDResult);
+$oSetup = new SetupFunctions($aCMDResult);
 
 // *******************************************************
 // go through complete process if 'all' is selected or start selected functions
 if ($aCMDResult['create-db'] || $aCMDResult['all']) {
     $bDidSomething = true;
-    $cSetup->createDB();
+    $oSetup->createDB();
 }
 
+$oSetup->connect();
+
 if ($aCMDResult['setup-db'] || $aCMDResult['all']) {
     $bDidSomething = true;
-    $cSetup->setupDB();
+    $oSetup->setupDB();
 }
 
 // Try accessing the C module, so we know early if something is wrong
@@ -92,68 +95,68 @@ if (!checkModulePresence()) {
 
 if ($aCMDResult['import-data'] || $aCMDResult['all']) {
     $bDidSomething = true;
-    $cSetup->importData($aCMDResult['osm-file']);
+    $oSetup->importData($aCMDResult['osm-file']);
 }
 
 if ($aCMDResult['create-functions'] || $aCMDResult['all']) {
     $bDidSomething = true;
-    $cSetup->createFunctions();
+    $oSetup->createFunctions();
 }
 
 if ($aCMDResult['create-tables'] || $aCMDResult['all']) {
     $bDidSomething = true;
-    $cSetup->createTables();
-    $cSetup->createFunctions();
+    $oSetup->createTables($aCMDResult['reverse-only']);
+    $oSetup->createFunctions();
 }
 
 if ($aCMDResult['create-partition-tables'] || $aCMDResult['all']) {
     $bDidSomething = true;
-    $cSetup->createPartitionTables();
+    $oSetup->createPartitionTables();
 }
 
 if ($aCMDResult['create-partition-functions'] || $aCMDResult['all']) {
     $bDidSomething = true;
-    $cSetup->createPartitionFunctions();
+    $oSetup->createPartitionFunctions();
 }
 
 if ($aCMDResult['import-wikipedia-articles'] || $aCMDResult['all']) {
     $bDidSomething = true;
-    $cSetup->importWikipediaArticles();
+    $oSetup->importWikipediaArticles();
 }
 
 if ($aCMDResult['load-data'] || $aCMDResult['all']) {
     $bDidSomething = true;
-    $cSetup->loadData($aCMDResult['disable-token-precalc']);
+    $oSetup->loadData($aCMDResult['disable-token-precalc']);
 }
 
 if ($aCMDResult['import-tiger-data']) {
     $bDidSomething = true;
-    $cSetup->importTigerData();
+    $oSetup->importTigerData();
 }
 
 if ($aCMDResult['calculate-postcodes'] || $aCMDResult['all']) {
     $bDidSomething = true;
-    $cSetup->calculatePostcodes($aCMDResult['all']);
+    $oSetup->calculatePostcodes($aCMDResult['all']);
 }
 
 if ($aCMDResult['index'] || $aCMDResult['all']) {
     $bDidSomething = true;
-    $cSetup->index($aCMDResult['index-noanalyse']);
+    $oSetup->index($aCMDResult['index-noanalyse']);
 }
 
 if ($aCMDResult['create-search-indices'] || $aCMDResult['all']) {
     $bDidSomething = true;
-    $cSetup->createSearchIndices();
+    $oSetup->createSearchIndices();
 }
 
 if ($aCMDResult['create-country-names'] || $aCMDResult['all']) {
     $bDidSomething = true;
-    $cSetup->createCountryNames($aCMDResult);
+    $oSetup->createCountryNames($aCMDResult);
 }
 
 if ($aCMDResult['drop']) {
     $bDidSomething = true;
-    $cSetup->drop($aCMDResult);
+    $oSetup->drop($aCMDResult);
 }
 
 // ******************************************************
index f5c208577e775c48aa81c6788dd29186941e5ef9..eebd686de62f2e47a8275923b2b0a2d86ed69daf 100755 (executable)
@@ -111,6 +111,7 @@ if ($aResult['init-updates']) {
                                       'enable-diff-updates' => true,
                                       'verbose' => $aResult['verbose']
                                      ));
+        $cSetup->connect();
         $cSetup->createFunctions();
     }