def object_type_to_python_type( objtype: s_types.Type, schema: s_schema.Schema, *, base_class: typing.Optional[type] = None, _memo: typing.Optional[typing.Mapping[s_types.Type, type]] = None) -> type: if _memo is None: _memo = {} fields = [] subclasses = [] for pn, p in objtype.get_pointers(schema).items(schema): if pn in ('id', '__type__'): continue ptype = p.get_target(schema) if ptype.is_object_type(): pytype = _memo.get(ptype) if pytype is None: pytype = object_type_to_python_type(ptype, schema, base_class=base_class, _memo=_memo) _memo[ptype] = pytype for subtype in ptype.children(schema): subclasses.append( object_type_to_python_type(subtype, schema, base_class=pytype, _memo=_memo)) else: pytype = scalar_type_to_python_type(ptype, schema) is_multi = p.get_cardinality(schema) is qltypes.Cardinality.MANY if is_multi: pytype = typing.FrozenSet[pytype] default = p.get_default(schema) if default is None: if p.get_required(schema): default = dataclasses.MISSING else: default = ql_compiler.evaluate_to_python_val(default.text, schema=schema) if is_multi and not isinstance(default, frozenset): default = frozenset((default, )) constraints = p.get_constraints(schema).objects(schema) exclusive = schema.get('std::exclusive') unique = (not ptype.is_object_type() and any( c.issubclass(schema, exclusive) for c in constraints)) field = dataclasses.field( compare=unique, hash=unique, repr=True, default=default, ) fields.append((pn, pytype, field)) return dataclasses.make_dataclass( objtype.get_name(schema).name, fields=fields, bases=(base_class, ) if base_class is not None else (), frozen=True, namespace={'_subclasses': subclasses}, )
def derive_view(stype: s_types.Type, *, derived_name: typing.Optional[sn.SchemaName] = None, derived_name_quals: typing.Optional[typing.Sequence[str]] = (), derived_name_base: typing.Optional[str] = None, preserve_shape: bool = False, preserve_path_id: bool = False, is_insert: bool = False, is_update: bool = False, inheritance_merge: bool = True, attrs: typing.Optional[dict] = None, ctx: context.ContextLevel) -> s_types.Type: if derived_name is None: derived_name = derive_view_name(stype=stype, derived_name_quals=derived_name_quals, derived_name_base=derived_name_base, ctx=ctx) if is_insert: vtype = s_types.ViewType.Insert elif is_update: vtype = s_types.ViewType.Update else: vtype = s_types.ViewType.Select if attrs is None: attrs = {} else: attrs = dict(attrs) attrs['view_type'] = vtype derived: s_types.Type if isinstance(stype, s_abc.Collection): ctx.env.schema, derived = stype.derive_subtype(ctx.env.schema, name=derived_name) elif isinstance(stype, s_inh.InheritingObject): ctx.env.schema, derived = stype.derive_subtype( ctx.env.schema, name=derived_name, inheritance_merge=inheritance_merge, refdict_whitelist={'pointers'}, mark_derived=True, preserve_path_id=preserve_path_id, attrs=attrs, ) if (not stype.generic(ctx.env.schema) and isinstance(derived, s_sources.Source)): scls_pointers = stype.get_pointers(ctx.env.schema) derived_own_pointers = derived.get_pointers(ctx.env.schema) for pn, ptr in derived_own_pointers.items(ctx.env.schema): # This is a view of a view. Make sure query-level # computable expressions for pointers are carried over. src_ptr = scls_pointers.get(ctx.env.schema, pn) computable_data = ctx.source_map.get(src_ptr) if computable_data is not None: ctx.source_map[ptr] = computable_data if src_ptr in ctx.pending_cardinality: ctx.pointer_derivation_map[src_ptr].append(ptr) stmtctx.pend_pointer_cardinality_inference(ptrcls=ptr, ctx=ctx) ctx.view_nodes[derived.get_name(ctx.env.schema)] = derived if preserve_shape and stype in ctx.env.view_shapes: ctx.env.view_shapes[derived] = ctx.env.view_shapes[stype] return derived
def derive_view( stype: s_types.Type, *, derived_name: Optional[sn.QualName] = None, derived_name_quals: Optional[Sequence[str]] = (), preserve_shape: bool = False, preserve_path_id: bool = False, exprtype: s_types.ExprType = s_types.ExprType.Select, inheritance_merge: bool = True, attrs: Optional[Dict[str, Any]] = None, ctx: context.ContextLevel, ) -> s_types.Type: if derived_name is None: assert isinstance(stype, s_obj.DerivableObject) derived_name = derive_view_name(stype=stype, derived_name_quals=derived_name_quals, ctx=ctx) if attrs is None: attrs = {} else: attrs = dict(attrs) attrs['expr_type'] = exprtype derived: s_types.Type if isinstance(stype, s_types.Collection): ctx.env.schema, derived = stype.derive_subtype( ctx.env.schema, name=derived_name, attrs=attrs, ) elif isinstance(stype, (s_objtypes.ObjectType, s_scalars.ScalarType)): existing = ctx.env.schema.get(derived_name, default=None, type=type(stype)) if existing is not None: if ctx.recompiling_schema_alias: # When recompiling schema alias, we, essentially # re-derive the already-existing objects exactly. derived = existing else: raise AssertionError( f'{type(stype).get_schema_class_displayname()}' f' {derived_name!r} already exists', ) else: ctx.env.schema, derived = stype.derive_subtype( ctx.env.schema, name=derived_name, inheritance_merge=inheritance_merge, inheritance_refdicts={'pointers'}, mark_derived=True, transient=True, preserve_path_id=preserve_path_id, attrs=attrs, ) if (stype.is_view(ctx.env.schema) # XXX: Previously, the main check here was just for # (not stype.generic(...)). generic isn't really the # right way to figure out if something is a view, since # some aliases will be generic. On changing it to is_view # instead, though, two GROUP BY tests that grouped # on the result of a group broke # (test_edgeql_group_by_group_by_03{a,b}). # # It's probably a bug that this matters in that case, and # it is an accident that group bindings are named in such # a way that they count as being generic, but for now # preserve that behavior. and not (stype.generic(ctx.env.schema) and (view_ir := ctx.view_sets.get(stype)) and (scope_info := ctx.path_scope_map.get(view_ir)) and scope_info.binding_kind) and isinstance( derived, s_objtypes.ObjectType)): assert isinstance(stype, s_objtypes.ObjectType) scls_pointers = stype.get_pointers(ctx.env.schema) derived_own_pointers = derived.get_pointers(ctx.env.schema) for pn, ptr in derived_own_pointers.items(ctx.env.schema): # This is a view of a view. Make sure query-level # computable expressions for pointers are carried over. src_ptr = scls_pointers.get(ctx.env.schema, pn) computable_data = (ctx.source_map.get(src_ptr) if src_ptr else None) if computable_data is not None: ctx.source_map[ptr] = computable_data if src_ptr in ctx.env.pointer_specified_info: ctx.env.pointer_derivation_map[src_ptr].append(ptr)
def derive_view( stype: s_types.Type, *, derived_name: Optional[sn.QualName] = None, derived_name_quals: Optional[Sequence[str]] = (), derived_name_base: Optional[str] = None, preserve_shape: bool = False, preserve_path_id: bool = False, is_insert: bool = False, is_update: bool = False, is_delete: bool = False, inheritance_merge: bool = True, attrs: Optional[Dict[str, Any]] = None, ctx: context.ContextLevel, ) -> s_types.Type: if derived_name is None: assert isinstance(stype, s_obj.DerivableObject) derived_name = derive_view_name(stype=stype, derived_name_quals=derived_name_quals, derived_name_base=derived_name_base, ctx=ctx) if is_insert: exprtype = s_types.ExprType.Insert elif is_update: exprtype = s_types.ExprType.Update elif is_delete: exprtype = s_types.ExprType.Delete else: exprtype = s_types.ExprType.Select if attrs is None: attrs = {} else: attrs = dict(attrs) attrs['expr_type'] = exprtype derived: s_types.Type if isinstance(stype, s_abc.Collection): ctx.env.schema, derived = stype.derive_subtype( ctx.env.schema, name=derived_name, attrs=attrs, ) elif isinstance(stype, s_obj.DerivableInheritingObject): existing = ctx.env.schema.get(derived_name, default=None, type=type(stype)) if existing is not None: if ctx.recompiling_schema_alias: # When recompiling schema alias, we, essentially # re-derive the already-existing objects exactly. derived = existing else: raise AssertionError( f'{type(stype).get_schema_class_displayname()}' f' {derived_name!r} already exists', ) else: ctx.env.schema, derived = stype.derive_subtype( ctx.env.schema, name=derived_name, inheritance_merge=inheritance_merge, inheritance_refdicts={'pointers'}, mark_derived=True, transient=True, preserve_path_id=preserve_path_id, attrs=attrs, ) if (not stype.generic(ctx.env.schema) and isinstance(derived, s_sources.Source)): scls_pointers = stype.get_pointers(ctx.env.schema) derived_own_pointers = derived.get_pointers(ctx.env.schema) for pn, ptr in derived_own_pointers.items(ctx.env.schema): # This is a view of a view. Make sure query-level # computable expressions for pointers are carried over. src_ptr = scls_pointers.get(ctx.env.schema, pn) computable_data = ctx.source_map.get(src_ptr) if computable_data is not None: ctx.source_map[ptr] = computable_data if src_ptr in ctx.env.pointer_specified_info: ctx.env.pointer_derivation_map[src_ptr].append(ptr) else: raise TypeError("unsupported type in derive_view") ctx.view_nodes[derived.get_name(ctx.env.schema)] = derived if preserve_shape and stype in ctx.env.view_shapes: ctx.env.view_shapes[derived] = ctx.env.view_shapes[stype] ctx.env.created_schema_objects.add(derived) return derived
def _process_view(*, stype: s_types.Type, path_id: irast.PathId, path_id_namespace: Optional[irast.WeakNamespace] = None, elements: List[qlast.ShapeElement], view_rptr: Optional[context.ViewRPtr] = None, view_name: Optional[sn.SchemaName] = None, is_insert: bool = False, is_update: bool = False, ctx: context.ContextLevel) -> s_types.Type: if (view_name is None and ctx.env.schema_view_mode and view_rptr is not None): # Make sure persistent schema views have properly formed # names as opposed to the usual mangled form of the ephemeral # views. This is needed for introspection readability, as well # as helps in maintaining proper type names for schema # representations that require alphanumeric names, such as # GraphQL. # # We use the name of the source together with the name # of the inbound link to form the name, so in e.g. # CREATE VIEW V := (SELECT Foo { bar: { baz: { ... } }) # The name of the innermost view would be "__V__bar__baz". source_name = view_rptr.source.get_name(ctx.env.schema).name if not source_name.startswith('__'): source_name = f'__{source_name}' if view_rptr.ptrcls_name is not None: ptr_name = view_rptr.ptrcls_name.name elif view_rptr.ptrcls is not None: ptr_name = view_rptr.ptrcls.get_shortname(ctx.env.schema).name else: raise errors.InternalServerError( '_process_view in schema mode received view_rptr with ' 'neither ptrcls_name, not ptrcls') name = f'{source_name}__{ptr_name}' view_name = sn.Name( module=ctx.derived_target_module or '__derived__', name=name, ) view_scls = schemactx.derive_view(stype, is_insert=is_insert, is_update=is_update, derived_name=view_name, ctx=ctx) is_mutation = is_insert or is_update is_defining_shape = ctx.expr_exposed or is_mutation if view_rptr is not None and view_rptr.ptrcls is None: derive_ptrcls(view_rptr, target_scls=view_scls, transparent=True, ctx=ctx) pointers = [] for shape_el in elements: with ctx.newscope(fenced=True) as scopectx: pointers.append( _normalize_view_ptr_expr(shape_el, view_scls, path_id=path_id, path_id_namespace=path_id_namespace, is_insert=is_insert, is_update=is_update, view_rptr=view_rptr, ctx=scopectx)) if is_insert: assert isinstance(stype, s_objtypes.ObjectType) explicit_ptrs = { ptrcls.get_shortname(ctx.env.schema).name for ptrcls in pointers } scls_pointers = stype.get_pointers(ctx.env.schema) for pn, ptrcls in scls_pointers.items(ctx.env.schema): if (pn in explicit_ptrs or ptrcls.is_pure_computable(ctx.env.schema)): continue if not ptrcls.get_default(ctx.env.schema): if ptrcls.get_required(ctx.env.schema): if ptrcls.is_property(ctx.env.schema): # If the target is a sequence, there's no need # for an explicit value. if ptrcls.get_target(ctx.env.schema).issubclass( ctx.env.schema, ctx.env.schema.get('std::sequence')): continue what = 'property' else: what = 'link' raise errors.MissingRequiredError( f'missing value for required {what} ' f'{stype.get_displayname(ctx.env.schema)}.' f'{ptrcls.get_displayname(ctx.env.schema)}') else: continue ptrcls_sn = ptrcls.get_shortname(ctx.env.schema) default_ql = qlast.ShapeElement(expr=qlast.Path(steps=[ qlast.Ptr(ptr=qlast.ObjectRef(name=ptrcls_sn.name, module=ptrcls_sn.module)) ])) with ctx.newscope(fenced=True) as scopectx: pointers.append( _normalize_view_ptr_expr( default_ql, view_scls, path_id=path_id, path_id_namespace=path_id_namespace, is_insert=is_insert, is_update=is_update, view_rptr=view_rptr, ctx=scopectx)) for ptrcls in pointers: if ptrcls.is_link_property(ctx.env.schema): assert view_rptr is not None source = view_rptr.ptrcls else: source = view_scls if is_defining_shape: ctx.env.view_shapes[source].append(ptrcls) if (view_rptr is not None and view_rptr.ptrcls is not None and view_scls is not stype): ctx.env.schema = view_scls.set_field_value(ctx.env.schema, 'rptr', view_rptr.ptrcls) return view_scls
def _process_view(*, stype: s_types.Type, path_id: irast.PathId, path_id_namespace: typing.Optional[ irast.WeakNamespace] = None, elements: typing.List[qlast.ShapeElement], view_rptr: typing.Optional[context.ViewRPtr] = None, view_name: typing.Optional[sn.SchemaName] = None, is_insert: bool = False, is_update: bool = False, ctx: context.ContextLevel) -> s_types.Type: view_scls = schemactx.derive_view(stype, is_insert=is_insert, is_update=is_update, derived_name=view_name, ctx=ctx) is_mutation = is_insert or is_update is_defining_shape = ctx.expr_exposed or is_mutation if view_rptr is not None and view_rptr.ptrcls is None: derive_ptrcls(view_rptr, target_scls=view_scls, transparent=True, ctx=ctx) pointers = [] for shape_el in elements: with ctx.newscope(fenced=True) as scopectx: pointers.append( _normalize_view_ptr_expr(shape_el, view_scls, path_id=path_id, path_id_namespace=path_id_namespace, is_insert=is_insert, is_update=is_update, view_rptr=view_rptr, ctx=scopectx)) if is_insert: assert isinstance(stype, s_objtypes.ObjectType) explicit_ptrs = { ptrcls.get_shortname(ctx.env.schema).name for ptrcls in pointers } scls_pointers = stype.get_pointers(ctx.env.schema) for pn, ptrcls in scls_pointers.items(ctx.env.schema): if (pn in explicit_ptrs or ptrcls.is_pure_computable(ctx.env.schema)): continue if not ptrcls.get_default(ctx.env.schema): if ptrcls.get_required(ctx.env.schema): if ptrcls.is_property(ctx.env.schema): what = 'property' else: what = 'link' raise errors.MissingRequiredError( f'missing value for required {what} ' f'{stype.get_displayname(ctx.env.schema)}.' f'{ptrcls.get_displayname(ctx.env.schema)}') else: continue ptrcls_sn = ptrcls.get_shortname(ctx.env.schema) default_ql = qlast.ShapeElement(expr=qlast.Path(steps=[ qlast.Ptr(ptr=qlast.ObjectRef(name=ptrcls_sn.name, module=ptrcls_sn.module)) ])) with ctx.newscope(fenced=True) as scopectx: pointers.append( _normalize_view_ptr_expr( default_ql, view_scls, path_id=path_id, path_id_namespace=path_id_namespace, is_insert=is_insert, is_update=is_update, view_rptr=view_rptr, ctx=scopectx)) for ptrcls in pointers: if ptrcls.is_link_property(ctx.env.schema): assert view_rptr is not None source = view_rptr.ptrcls else: source = view_scls if is_defining_shape: ctx.env.view_shapes[source].append(ptrcls) if (view_rptr is not None and view_rptr.ptrcls is not None and view_scls is not stype): ctx.env.schema = view_scls.set_field_value(ctx.env.schema, 'rptr', view_rptr.ptrcls) return view_scls
def derive_view( stype: s_types.Type, *, derived_name: Optional[sn.SchemaName] = None, derived_name_quals: Optional[Sequence[str]] = (), derived_name_base: Optional[str] = None, preserve_shape: bool = False, preserve_path_id: bool = False, is_insert: bool = False, is_update: bool = False, is_delete: bool = False, inheritance_merge: bool = True, attrs: Optional[Dict[str, Any]] = None, ctx: context.ContextLevel, ) -> s_types.Type: if derived_name is None: assert isinstance(stype, s_obj.DerivableObject) derived_name = derive_view_name(stype=stype, derived_name_quals=derived_name_quals, derived_name_base=derived_name_base, ctx=ctx) if is_insert: exprtype = s_types.ExprType.Insert elif is_update: exprtype = s_types.ExprType.Update elif is_delete: exprtype = s_types.ExprType.Delete else: exprtype = s_types.ExprType.Select if attrs is None: attrs = {} else: attrs = dict(attrs) attrs['expr_type'] = exprtype derived: s_types.Type if isinstance(stype, s_abc.Collection): ctx.env.schema, derived = stype.derive_subtype(ctx.env.schema, name=derived_name) elif isinstance(stype, s_obj.DerivableInheritingObject): ctx.env.schema, derived = stype.derive_subtype( ctx.env.schema, name=derived_name, inheritance_merge=inheritance_merge, inheritance_refdicts={'pointers'}, mark_derived=True, transient=True, preserve_path_id=preserve_path_id, attrs=attrs, ) if (not stype.generic(ctx.env.schema) and isinstance(derived, s_sources.Source)): scls_pointers = stype.get_pointers(ctx.env.schema) derived_own_pointers = derived.get_pointers(ctx.env.schema) for pn, ptr in derived_own_pointers.items(ctx.env.schema): # This is a view of a view. Make sure query-level # computable expressions for pointers are carried over. src_ptr = scls_pointers.get(ctx.env.schema, pn) computable_data = ctx.source_map.get(src_ptr) if computable_data is not None: ctx.source_map[ptr] = computable_data if src_ptr in ctx.pending_cardinality: ctx.pointer_derivation_map[src_ptr].append(ptr) stmtctx.pend_pointer_cardinality_inference(ptrcls=ptr, ctx=ctx) else: raise TypeError("unsupported type in derive_view") ctx.view_nodes[derived.get_name(ctx.env.schema)] = derived if preserve_shape and stype in ctx.env.view_shapes: ctx.env.view_shapes[derived] = ctx.env.view_shapes[stype] ctx.env.created_schema_objects.add(derived) return derived