Repository navigation
grass.temporal: support fully qualified names on read - #7670
Conversation
|
Heads-up on the red macOS build check here: it's a pre-existing, unrelated test-isolation flake, not something in this PR. |
ninsbl
left a comment
There was a problem hiding this comment.
Hi @gkneighb and thanks for your PR. Extending the SQLDatabaseConnection class with an add_mapset method is one option to solve the issue that I have not thought about before. Could be a good approach.
However, injecting a call to that function to every attempt to fetch data from the TGIS DB is probably not what I would do, as the sql functions may get executed quite frequently, and there we would not want to have avoidable overhead.
I can imagine two slightly alternative approaches.
- One approach may be to modify the open_stds functions to identify is the mapset in the ident is in the SQLDatabaseConnection and use your add_mapset function if not.
I have not checked if all tools use that function set or rather e.g. dataset factory, or even the space_time_datasets We would have to research a bit. The advantade with such a solution on the library level is that we do not have to adjust all the tools. - Another approach would be that all tools that take STDS as input, do the following:
a) get (all) required mapsets from user input (fully qualified dataset names)
b) initialize SQLDatabaseConnection with those mapsets (in addition to the current mapset if write access is required).
|
On the overhead: agreed the connection's SQL methods ( To scope the alternatives I checked which read paths actually reach an off-search-path mapset, and it's broader than
So Alt 1 (open_stds only) would leave Proposed middle ground that avoids the per-statement overhead: call Does that direction work for you? If so I'll rework it that way. For reference, I verified coverage on a two-mapset setup (STRDS in an off-search-path mapset, queried from PERMANENT): |
|
OK, I think it would be reasonable to limit the scope of this PR to:
Tools like t.info create the database interface separately, so they would have to make sure that the interface contains connections for the required mapsets. t.info then call database related function with that interface, e.g. For such cases like it may make sense to create a helper function to check if an ID references a dataset on the search path mapset. But I am not sure where or how such a function should be implemented in detail. Would have to think about it a bit more... |
1122073 to
4079381
Compare
|
Reworked to that scope: |
Add an idempotent method to include a mapset's temporal database in an existing connection, enabling access to space time datasets outside the current search path. Part of OSGeo#7641. Authored with the help of an AI assistant.
open_old_stds() now ensures the dataset's own mapset is part of the database connection (using SQLDatabaseInterfaceConnection.add_mapset), so a fully qualified name (name@mapset) can be opened even when the mapset is not on the current search path. Scope is limited to tools that go through open_old_stds. Tools that build their own database interface (e.g. t.info) need to add the required mapset to that interface themselves and are left as follow-up. Part of OSGeo#7641. Authored with the help of an AI assistant.
4079381 to
8c8761e
Compare
Changes should make sure that the temporal modules respect the search path in a way tools for other data do. Not sure if it should warn if the name can be found in several mapsets (currently it stops as soon as a dataset is found)... The behavior would need a proper test...
ninsbl
left a comment
There was a problem hiding this comment.
Now, that I edited the PR, I hope others can review.
Sorry, for messing around with your work @gkneighb . We have other stuff coming up depending on this, so I took the liberty to modify. Still, it is a very valuable contibution from you side.
|
I start wondering if it would be good to deprecate the open_stds functions and replace them with one generic Probably with support for a context manager... That would give a more familiar API.... But is a different PR of course... |
|
The tgis_dbmi_paramstyle change is a relatively trivial regression fix. It is not entirely related to the other changes in this PR, but I noticed weird SQL error messages and since the fix is tiny, I just added it here. I can easily separate it out though if you prefer... |
|
No worries at all, @ninsbl — please don't apologize. It's great to see it land in a form that unblocks the work you have depending on it, and your refactor was instructive to read (the |
Co-authored-by: Anna Petrasova <kratochanna@gmail.com>
|
@petrasovaa could you have another look at this when your time allows? Would be great to get it merged, as based on this one, more temporal improvements can be added... |
|
It looks like this PR triggers a problem in t.(un)register because they use a hack. Here are the details from the AI analysis: Detailst.register/t.unregister crash instead of reporting a missing datasetFrom a mapset with another temporal mapset on the search path (PERMANENT always is), a dataset name not found in the current mapset crashes: $ t.register input=does_not_exist maps=m1 main branch gives for an unqualified name: ERROR: Space time raster dataset does_not_exist@user1 not found Cause. SQLDatabaseInterfaceConnection keeps tgis_mapsets (permitted) and connections (open handles) in sync. register.py:126-130 and t.unregister.py:80-83 deliberately break that — they prune connections to the current mapset ("ugly hack !") and leave tgis_mapsets alone. All six accessors (core.py:1215, 1242, 1260, 1275, 1287, 1306) then validate against tgis_mapsets and dereference connections, so a pruned mapset passes the guard and fails the lookup. Relation to #7670. Pre-existing, not introduced there: main already raises the same KeyError for t.register input=ds@OTHERMAPSET. It stayed rare because open_old_stds never left the current mapset for an unqualified name. #7670's search-path loop iterates dbif.tgis_mapsets, so every failed unqualified lookup now hits it — a rare crash becomes an everyday one. Repro. Project with PERMANENT + user1, both with a temporal database, raster m1 in user1: Fix. In core.py, guard on the dict actually dereferenced (if mapset not in self.connections: at all six sites, and list self.connections.keys() in _create_mapset_error_message). No-op in normal use, since the dicts only diverge inside the hack. Better still, drop the hack: register_map already refuses cross-mapset writes on its own (verified — nothing gets written), so the connection surgery is redundant protection that exists only to break the invariant. Here is a patch that (hopefully) solves this issue: |
I see. I would probably rather change t.register and t.unregister and replace the "ugly hack" with creating a SQLDatabaseInterfaceConnection only for the current mapset:
and raise an error when the requested STDS is not in the current mapset. Not sure if that is sufficient for t.unregister. Will look into it... |
Solved in #7901 |
|
@petrasovaa — friendly nudge when you have a minute. Stefan addressed your Sep 4 notes in |
petrasovaa
left a comment
There was a problem hiding this comment.
Sorry, I've been busy...
Summary
Temporal read tools could only access a space time dataset from mapsets on the current search path, even when given a fully qualified name (
name@mapset). This makesopen_old_stds()resolve a fully qualified name from any accessible mapset, matching how other GRASS data types behave.Part of #7641.
Approach
SQLDatabaseInterfaceConnection.add_mapset()that pulls an accessible mapset's temporal database into an existing connection (building on themapsetssupport from t.list: Add mapset options for t.list #7631 / temporal: add parameter to skip creation of TGIS DB in current mapset on tgis.init #7645; a no-op when the mapset is already present).open_old_stds(), after the connection is initialized, so the dataset's own mapset is part of the connection before the dataset is queried.Scope
Limited to tools that open datasets through
open_old_stds. Tools that build their own database interface and bypassopen_old_stds(e.g.t.info, which callsis_in_db(dbif)on an interface it created itself) must ensure the required mapset is added to that interface; that is left as follow-up. The write/create path is unchanged (datasets are still created only in the current mapset).Test plan
New pytest suite
python/grass/temporal/tests/grass_temporal_fully_qualified_names_test.py:add_mapsetunit: adds an off-search-path mapset to a connection; idempotent.open_old_stdsresolvesname@mapsetfor a mapset off the search path.AI assistance
Implemented with the assistance of an AI coding assistant (Claude), reviewed and verified by the author.