edgedb / edgedb

A graph-relational database with declarative schema, built-in migration system, and a next-generation query language
https://edgedb.com
Apache License 2.0
13.12k stars 402 forks source link

Adding a new pointer doesn't trigger reevaluation of schema expressions that could reference it #4452

Open msullivan opened 2 years ago

msullivan commented 2 years ago

If we do something like

create type T;                                                            
create type P { create multi link l -> T; };                              
insert T;                                                                 
insert P { l := T }; insert P { l := T };                                 

create function cnt_l() -> int64 using (select count(T.<l));              
select cnt_l();                                                           

create type Q { create multi link l -> T; };                              
insert Q { l := T };                                                      
select cnt_l();

we get back 2, as expected, from the first call, and then 2 again from the second call.

This is because cnt_l doesn't get updated when a new link named l gets created. (Note that banning use of backlinks without a type intersection does not help us here, unless we are willing to go as far as banning things like T.l[is Object] (or any other type that doesn't directly have the link)).

This example uses functions but I think there are other potential snags---maybe we could fail to trigger a cycle error in an access policy?

--

As far as backportable ways to fix this, we might be stuck finding all existing links with the same name and doing a _propagate_expr_refs on the expr references to all of them. Ew! For future versions we can probably reduce the pain a bit and track specifically which what backreferences are used by expression, by name. (We can't do that in 2.0 because it would require adding more fields to the schema)

msullivan commented 1 year ago

There is another issue related to this, which is that if a new required pointer is created, or a pointer is made required, that could invalidate existing rewrites/triggers/defaults that use it