New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement compilation of triggers #5088
Conversation
@@ -98,10 +98,15 @@ def inherit_fields( | |||
inherited_fields_update = {} | |||
deferred_complex_ops = [] | |||
|
|||
for field_name in field_names: | |||
# Iterate over mcls.get_schema_fields() instead of field_names for |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this important?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Compilation of the trigger expression depends on the values of kind
and scope
, and so we need to arrange to inherit those before we try to inherit expr
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good!
I still have to re-read the pg compiler side with a fresh set of brain, but I can do that even after the merge.
@@ -1025,7 +1026,22 @@ class GroupStmt(FilteredStmt): | |||
] = ast.field(factory=dict) | |||
|
|||
|
|||
class MutatingStmt(Stmt): | |||
# XXX: DOC | |||
class MutatingLikeStmt(Expr): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
class MutatingLikeStmt(Expr): | |
class MutatingBaseStmt(Expr): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Eh, I like MutatingLike
more, since it's not really a MutatingBase
kinds = set(trigger.get_kinds(schema)) | ||
source = trigger.get_subject(schema) | ||
|
||
with ctx.detached() as _, ctx.newscope(fenced=True) as sctx: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How is using ctx.detached()
different then wrapping the expr into a qlast.DetactedExpr
? Because this is what we do for defaults (and now rewrites):
https://github.com/edgedb/edgedb/blob/master/edb/edgeql/compiler/viewgen.py#L711-L746
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DetachedExpr will trigger a new detached
context when processing. I think the main difference is I'm just doing it directly
# Process all the types, starting with the base type | ||
for subtype in sorted(stypes, key=lambda t: t != stype): | ||
for trigger in subtype.get_relevant_triggers(kind, schema): | ||
mro = (trigger, *trigger.get_ancestors(schema).objects(schema)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does the get_ancestors
guarantee mro order? This should probably be documented somewhere.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it does, though we don't actually care here.
Though also: #5118 😢
for stmt in dml_stmts: | ||
kind = TRIGGER_KINDS[type(stmt)] | ||
|
||
stype = schemactx.concretify( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does this do? I cannot gather from the function documentation. What are "views" in this context?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"view" is what "shape" used to be called, and which it is still sometimes called internally. But more generally, all of the transient subtypes we generate during compilation.
concretify
finds non-view concrete base types, but it does it recursively through unions and intersections.
Arguably this should be the normal thing? Idk.
edb/edgeql/compiler/triggers.py
Outdated
# N.B: If the *base type* of the DML appears, that suffices, | ||
# because it covers everything, and we don't need to duplicate. | ||
if (stype, stmt) not in tmap: | ||
tmap.add((subtype, stmt)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this could be "If any of the previously handled triggers of ancestors appears, ...
This would have effect when you have a trigger on Child1
in hierarchy Updated > Child1 > Child2
.
You would first add the trigger on Child1, but when processing Child2, the trigger would be added again, even though it's effects have already been covered.
(I may not be understanding this whole chunk correctly)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it is a specific interaction with how the pgsql/dml.py side is implemented. I'm elaborating a bit more in the comment.
Closes #4936. The interesting part of the compilation runs at the end of IR->SQL and injects the triggers into CTEs. The overlay system is (somewhat hackily) used to drive what objects are triggered on and (slightly less hackily) to provide a view of the new database state. I have one known bug at the moment, which is approximately that we apply access policies *too much* in one case. I have a plan for fixing it that doesn't change that much of the big picture, and I wanted to get something up for review.
Track deletes also, and track all types of dml in the same way, at the in fini_stmt. This requires more logic where we consume the data when compiling conflicts, but the data structure logic is more consistent. This also lets us drop the search for DML for triggers.
Closes #4936. The interesting part of the compilation runs at the end of IR->SQL and injects the triggers into CTEs. The overlay system is (somewhat hackily) used to drive what objects are triggered on and (slightly less hackily) to provide a view of the new database state. I have one known bug at the moment, which is approximately that we apply access policies *too much* in one case. I have a plan for fixing it that doesn't change that much of the big picture, and I wanted to get something up for review.
Closes #4936.
The interesting part of the compilation runs at the end of IR->SQL and
injects the triggers into CTEs. The overlay system is (somewhat hackily)
used to drive what objects are triggered on and (slightly less hackily) to
provide a view of the new database state.
I have one known bug at the moment, which is approximately that we apply
access policies too much in one case. I have a plan for fixing it that
doesn't change that much of the big picture, and I wanted to get something
up for review.