-
Notifications
You must be signed in to change notification settings - Fork 409
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
Refactor converting type expressions to typerefs and ranges #7274
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,7 +28,6 @@ | |
Dict, | ||
Set, | ||
FrozenSet, | ||
cast, | ||
overload, | ||
TYPE_CHECKING, | ||
) | ||
|
@@ -173,10 +172,6 @@ def contains_predicate( | |
if pred(typeref): | ||
return True | ||
|
||
elif typeref.intersection: | ||
return any( | ||
contains_predicate(sub, pred) for sub in typeref.intersection | ||
) | ||
elif typeref.union: | ||
return any( | ||
contains_predicate(sub, pred) for sub in typeref.union | ||
|
@@ -305,39 +300,34 @@ def _typeref( | |
) | ||
elif not isinstance(t, s_types.Collection): | ||
assert isinstance(t, s_types.InheritingType) | ||
union_of = t.get_union_of(schema) | ||
union: Optional[FrozenSet[irast.TypeRef]] | ||
if union_of: | ||
assert isinstance(t, s_objtypes.ObjectType) | ||
union_types = { | ||
cast(s_objtypes.ObjectType, c).get_nearest_non_derived_parent( | ||
schema) | ||
for c in union_of.objects(schema) | ||
} | ||
non_overlapping, union_is_exhaustive = ( | ||
s_utils.get_non_overlapping_union(schema, union_types) | ||
|
||
union: Optional[FrozenSet[irast.TypeRef]] = None | ||
union_is_exhaustive: bool = False | ||
expr_intersection: Optional[FrozenSet[irast.TypeRef]] = None | ||
expr_union: Optional[FrozenSet[irast.TypeRef]] = None | ||
if t.is_union_type(schema) or t.is_intersection_type(schema): | ||
union_types, union_is_exhaustive = ( | ||
s_utils.get_type_expr_non_overlapping_union(t, schema) | ||
) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does normalizing help here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All normalization stuff removed. |
||
if union_is_exhaustive: | ||
non_overlapping = frozenset({ | ||
t for t in non_overlapping | ||
if t.is_material_object_type(schema) | ||
}) | ||
|
||
union = frozenset( | ||
_typeref(c) for c in non_overlapping | ||
) | ||
else: | ||
union_is_exhaustive = False | ||
union = None | ||
|
||
intersection_of = t.get_intersection_of(schema) | ||
intersection: Optional[FrozenSet[irast.TypeRef]] | ||
if intersection_of: | ||
intersection = frozenset( | ||
_typeref(c) for c in intersection_of.objects(schema) | ||
_typeref(c) for c in union_types | ||
) | ||
else: | ||
intersection = None | ||
|
||
# Keep track of type expression structure. | ||
# This is necessary to determine the correct rvar when doing | ||
# type intersections or polymorphic queries. | ||
if expr_intersection_types := t.get_intersection_of(schema): | ||
expr_intersection = frozenset( | ||
_typeref(c) | ||
for c in expr_intersection_types.objects(schema) | ||
) | ||
|
||
if expr_union_types := t.get_union_of(schema): | ||
expr_union = frozenset( | ||
_typeref(c) | ||
for c in expr_union_types.objects(schema) | ||
) | ||
|
||
schema, material_type = t.material_type(schema) | ||
|
||
|
@@ -358,26 +348,29 @@ def _typeref( | |
else: | ||
base_typeref = None | ||
|
||
children: Optional[FrozenSet[irast.TypeRef]] | ||
|
||
if material_typeref is None and include_children: | ||
children: Optional[FrozenSet[irast.TypeRef]] = None | ||
if ( | ||
material_typeref is None | ||
and include_children | ||
and children is None | ||
): | ||
children = frozenset( | ||
_typeref(child, include_children=True) | ||
for child in t.children(schema) | ||
if not child.get_is_derived(schema) | ||
and not child.is_compound_type(schema) | ||
) | ||
else: | ||
children = None | ||
|
||
ancestors: Optional[FrozenSet[irast.TypeRef]] | ||
if material_typeref is None and include_ancestors: | ||
ancestors: Optional[FrozenSet[irast.TypeRef]] = None | ||
if ( | ||
material_typeref is None | ||
and include_ancestors | ||
and ancestors is None | ||
): | ||
ancestors = frozenset( | ||
_typeref(ancestor, include_ancestors=False) | ||
for ancestor in t.get_ancestors(schema).objects(schema) | ||
) | ||
else: | ||
ancestors = None | ||
|
||
sql_type = None | ||
needs_custom_json_cast = False | ||
|
@@ -401,7 +394,8 @@ def _typeref( | |
ancestors=ancestors, | ||
union=union, | ||
union_is_exhaustive=union_is_exhaustive, | ||
intersection=intersection, | ||
expr_intersection=expr_intersection, | ||
expr_union=expr_union, | ||
element_name=_name, | ||
is_scalar=t.is_scalar(), | ||
is_abstract=t.get_abstract(schema), | ||
|
@@ -912,36 +906,36 @@ def type_contains( | |
if typeref == parent: | ||
return True | ||
|
||
elif typeref.union: | ||
elif typeref.expr_union: | ||
# A union is considered a subtype of a type, if | ||
# ALL its components are subtypes of that type. | ||
return all( | ||
type_contains(parent, component) | ||
for component in typeref.union | ||
for component in typeref.expr_union | ||
) | ||
|
||
elif typeref.intersection: | ||
elif typeref.expr_intersection: | ||
# An intersection is considered a subtype of a type, if | ||
# ANY of its components are subtypes of that type. | ||
return any( | ||
type_contains(parent, component) | ||
for component in typeref.intersection | ||
for component in typeref.expr_intersection | ||
) | ||
|
||
elif parent.union: | ||
elif parent.expr_union: | ||
# A type is considered a subtype of a union type, | ||
# if it is a subtype of ANY of the union components. | ||
return any( | ||
type_contains(component, typeref) | ||
for component in parent.union | ||
for component in parent.expr_union | ||
) | ||
|
||
elif parent.intersection: | ||
elif parent.expr_intersection: | ||
# A type is considered a subtype of an intersection type, | ||
# if it is a subtype of ALL of the intersection components. | ||
return any( | ||
type_contains(component, typeref) | ||
for component in parent.intersection | ||
for component in parent.expr_intersection | ||
) | ||
|
||
else: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1171,6 +1171,84 @@ def get_non_overlapping_union( | |
return frozenset(all_objects), True | ||
|
||
|
||
def get_type_expr_non_overlapping_union( | ||
type: s_types.Type, | ||
schema: s_schema.Schema, | ||
) -> Tuple[FrozenSet[s_types.Type], bool]: | ||
"""Get a non-overlapping set of the type's descendants""" | ||
|
||
from edb.schema import types as s_types | ||
|
||
expanded_types = expand_type_expr_descendants(type, schema) | ||
|
||
# filter out subclasses | ||
expanded_types = { | ||
type | ||
for type in expanded_types | ||
if not any( | ||
type is not other and type.issubclass(schema, other) | ||
for other in expanded_types | ||
) | ||
} | ||
Comment on lines
+1184
to
+1192
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this needed? I guess it helps us not always require There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's right. It also makes reading the sql easier too :) |
||
|
||
non_overlapping, union_is_exhaustive = get_non_overlapping_union( | ||
schema, cast(set[so.InheritingObject], expanded_types) | ||
) | ||
|
||
return cast(FrozenSet[s_types.Type], non_overlapping), union_is_exhaustive | ||
|
||
|
||
def expand_type_expr_descendants( | ||
type: s_types.Type, | ||
schema: s_schema.Schema, | ||
*, | ||
expand_opaque_union: bool = True, | ||
) -> set[s_types.Type]: | ||
"""Expand types and type expressions to get descendants""" | ||
|
||
from edb.schema import types as s_types | ||
|
||
if sub_union := type.get_union_of(schema): | ||
# Expanding a union | ||
# Get the union of the component descendants | ||
return set.union(*( | ||
expand_type_expr_descendants( | ||
component, schema, | ||
) | ||
for component in sub_union.objects(schema) | ||
)) | ||
|
||
elif sub_intersection := type.get_intersection_of(schema): | ||
# Expanding an intersection | ||
# Get the intersection of component descendants | ||
return set.intersection(*( | ||
expand_type_expr_descendants( | ||
component, schema | ||
) | ||
for component in sub_intersection.objects(schema) | ||
)) | ||
|
||
elif type.is_view(schema): | ||
# When expanding a view, simply unpeel the view. | ||
return expand_type_expr_descendants( | ||
type.peel_view(schema), schema | ||
) | ||
|
||
# Return simple type and all its descendants. | ||
# Some types (eg. BaseObject) have non-simple descendants, filter them out. | ||
return {type} | { | ||
c for c in cast( | ||
set[s_types.Type], | ||
set(cast(so.InheritingObject, type).descendants(schema)) | ||
) | ||
if ( | ||
not c.is_union_type(schema) | ||
and not c.is_intersection_type(schema) | ||
and not c.is_view(schema) | ||
) | ||
} | ||
|
||
|
||
def _union_error( | ||
schema: s_schema.Schema, components: Iterable[s_types.Type] | ||
) -> errors.SchemaError: | ||
|
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.
It weirds me out a little if we aren't going to track that a type is an intersection in the TypeRef, even when it is one in the schema.
This might not actually matter though
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.
It's a bit odd, but the typeref doesn't exactly map onto types anyways