PostGIS – Troubleshooting PostGIS Database Connection with GRASS GIS

grass-gispostgis

I am trying to connect to a PostGIS database (hosted in a docker container) on a remote server with GRASS. I am able to connect with psql and confirm that the table is there (IP redacted):

psql -h x.x.x.x -U store_sa -d lenistore -p 5432
re -p 5432
psql (12.9 (Ubuntu 12.9-0ubuntu0.20.04.1), server 13.4)
WARNING: psql major version 12, server major version 13.
         Some psql features might not work.
Type "help" for help.
lenistore=> select count(*) from store_sa.all_roads;
 count
-------
  5000
(1 row)

I then make a connection to a table in GRASS:

GRASS 7.8.2 (newLocation):~ > v.external -o input="PG:dbname=lenistore user=store_sa host=x.x.x.x port=5432" layer="store_sa.all_roads" out=network_to_clean --overwrite
Over-riding projection check
WARNING: Vector map <network_to_clean> already exists and will be
         overwritten
v.external complete. Link to vector map <network_to_clean> created.

But when I try to run v.clean, it fails, complaining that the database does not exist.

GRASS 7.8.2 (newLocation):~ > v.clean -c input=network_to_clean output="routing_network" tool=break,chdangl threshold=0.00025,0.0005 --overwrite
Note: In latitude-longitude coordinate system specify threshold in degree
unit
WARNING: Threshold for tool 1 may not be > 0, set to 0
--------------------------------------------------
Tool: Threshold
Break: 0
Change type of boundary dangles: 0.0005
--------------------------------------------------
WARNING: Vector map <routing_network> already exists and will be
         overwritten
WARNING: Table <routing_network> linked to vector map <routing_network>
         does not exist
DBMI-PostgreSQL driver error:
Connection failed.
FATAL:  database "lenistore" does not exist


DBMI-PostgreSQL driver error:
Connection failed.
FATAL:  database "lenistore" does not exist


WARNING: Unable to open database <lenistore> by driver <pg>
no database is open
no database is open
WARNING: Unable to copy table <routing_network>
WARNING: Unable to copy table <store_sa.all_roads> for layer 1 from
         <network_to_clean@PERMANENT> to <routing_network>
WARNING: Failed to copy attribute table to output vector map
Rebuilding parts of topology...
Building topology for vector map <routing_network@PERMANENT>...
Registering primitives...
--------------------------------------------------
Tool: Split lines
Tool: Break lines at intersections
 100%
Tool: Remove duplicates
 100%
Tool: Merge lines
 100%
--------------------------------------------------
Building topology for vector map <routing_network@PERMANENT>...
--------------------------------------------------
Tool: Change type of boundary dangles
 100%
--------------------------------------------------
Rebuilding topology for output vector map...
Building topology for vector map <routing_network@PERMANENT>...
Registering primitives...

I can also confirm that the tables are there by executing v.external -l:

GRASS 7.8.2 (newLocation):~ > v.external -l input="PG:dbname=lenistore user=store_sa host=x.x.x.x port=5432"
PostGIS database <lenistore> contains 7 feature tables:
store_sa.all_roads
...

I see that I have a psql mismatch, but I don't know if GRASS uses psql or not, and I didn't want to go mucking around too much with that because this server is used by other people. I can try to change it if all else fails but I was hoping that someone could see something simple that I am just completely missing. I tried again with a PostgreSQL 12 image and it still fails for the same reason.

What I find very strange is that when v.clean executes, it actually seems to go through the process, with the progress bars progressing rather slowly, as if it is processing a large table.

I have also tried altering the user (store_sa) to be SUPERUSER, and even that doesn't work. I am at my wit's end here. It works fine with a local database, only when I connect to this remote one does it fail.

Best Answer

After a very frustrating afternoon with a colleague, we figured it out.

The reason it had always worked before this is because all my code was previously configured to work on a local Postgres instance. (i.e. on localhost).

My explanation here is based on what appears to have happened, I am not an expert on the inner workings on GRASS and my brain feels like a pretzel after this afternoon, so don't take this explanation as the absolute truth.

It seems that v.external -o input="PG:dbname=lenistore user=store_sa host=x.x.x.x port=5432" layer="store_sa.all_roads" out=network_to_clean --overwrite does not store the postgres login credentials as part of the created layer, nor does it store the data locally. It is a one-time read of the data, and seems to store it as a layer in your GRASS session which serves as a kind of pointer to the data source.

That means that the second command, v.clean -c input=network_to_clean output="routing_network" tool=break,chdangl threshold=0.00025,0.0005 --overwrite, has no concept of how to access the database, because it only sees the name of the layer in grass (network_to_clean), and not the credentials to go actually get the data.

We have to run another couple of commands before running v.clean:

db.connect driver=pg database=lenistore
db.login user=store_sa pass=<password> host=x.x.x.x port=5432 --overwrite

At first I was confused because I thought that db.login would perform the action of logging into the database. No - it saves the login credentials to a config file used by GRASS to make future connections. So by running this command first, we seem to save the signature of the login credentials for future uses, similar to a .pgpass file.

So, in short, the full process is:

db.connect driver=pg database=lenistore
db.login user=store_sa pass=<password> host=x.x.x.x port=5432 --overwrite
v.external -o input="PG:dbname=lenistore user=store_sa host=x.x.x.x port=5432" layer="togo.all_roads" out=network_to_clean --overwrite
v.clean -c input=network_to_clean output="routing_network" tool=break,chdangl threshold=0.00025,0.0005 --overwrite
... do whetver else at this point

You probably don't need to run the first two commands every time if this is being executed as a script in the same GRASS context, but it seems to not hurt my process (which is internal and not sensitive, so I don't care), so I'm just leaving it like that.

Related Question