def type_to_ql_typeref(t: s_obj.Object) -> qlast.TypeName: if not isinstance(t, s_types.Collection): result = qlast.TypeName( maintype=qlast.ObjectRef(module=t.name.module, name=t.name.name)) else: result = qlast.TypeName( maintype=qlast.ObjectRef(name=t.schema_name), subtypes=[type_to_ql_typeref(st) for st in t.get_subtypes()]) return result
def typeref_to_ast(t: so.Object) -> ql_ast.TypeName: if not isinstance(t, s_types.Collection): if isinstance(t, so.ObjectRef): name = t.classname else: name = t.name result = ql_ast.TypeName( maintype=ql_ast.ObjectRef(module=name.module, name=name.name)) else: result = ql_ast.TypeName( maintype=ql_ast.ObjectRef(name=t.schema_name), subtypes=[typeref_to_ast(st) for st in t.get_subtypes()]) return result
def visit_TypeCast(self, node): if node.type.subtypes: typ = qlast.TypeName( maintype=qlast.ObjectRef(name=node.type.maintype), subtypes=[ qlast.ObjectRef(module=stn.module, name=stn.name) for stn in node.type.subtypes ]) else: mtn = node.type.maintype mt = qlast.ObjectRef(module=mtn.module, name=mtn.name) typ = qlast.TypeName(maintype=mt) result = qlast.TypeCast(expr=self.visit(node.expr), type=typ) return result
def _apply_field_ast(self, context, node, op): if op.property == 'type': tp = op.new_value if isinstance(tp, s_types.Collection): maintype = tp.schema_name stt = tp.get_subtypes() for st in stt: eltype = qlast.ObjectRef(module=st.module, name=st.name) tnn = qlast.TypeName(maintype=maintype, subtypes=[eltype]) else: tnn = qlast.TypeName(maintype=tp) node.type = tnn else: super()._apply_field_ast(context, node, op)
def reduce_ARRAY_LANGBRACKET_NonArrayTypeName_OptDimensions_RANGBRACKET( self, *kids): subtype = kids[2].val dimensions = kids[3].val self.val = qlast.TypeName( maintype=qlast.ObjectRef(name='array'), subtypes=[subtype], dimensions=dimensions, )
def reduce_NodeName(self, *kids): maintype = kids[0].val # maintype cannot be 'map' or 'array' if maintype.module is None: if maintype.name in ('array', 'map'): raise EdgeQLSyntaxError(f"Unexpected {maintype.name!r}", context=kids[0].context) self.val = qlast.TypeName(maintype=maintype)
def reduce_ATTRIBUTE_ParenRawString_DeclarationSpecsBlob(self, *kids): eql = kids[1].parse_as_attribute_decl() attributes = [] for spec in kids[2].val: if isinstance(spec, esast.Attribute): attributes.append(spec) else: raise SchemaSyntaxError('illegal definition', context=spec.context) self.val = esast.AttributeDeclaration( name=eql.name.name, extends=[qlast.TypeName(maintype=base) for base in eql.bases], type=eql.type, attributes=attributes)
def reduce_Expr_LBRACKET_IS_NodeName_RBRACKET(self, *kids): # The path filter rule is here to resolve ambiguity with # indexes and slices, so Expr needs to be enforced as a path. # # NOTE: We specifically disallow "Foo.(bar[IS Baz])"" because # it is incorrect logical grouping. The example where the # incorrect grouping is more obvious is: "Foo.<(bar[IS Baz])" path = kids[0].val if (isinstance(path, qlast.Path) and isinstance(path.steps[-1], qlast.Ptr)): # filtering a longer path path.steps[-1].target = kids[3].val self.val = path else: # any other expression is a path with a filter self.val = qlast.TypeFilter( expr=path, type=qlast.TypeName(maintype=kids[3].val))
def _classbases_from_ast(cls, astnode, context, schema): classname = cls._classname_from_ast(astnode, context, schema) modaliases = context.modaliases bases = so.ObjectList( utils.ast_to_typeref( qlast.TypeName(maintype=b), modaliases=modaliases, schema=schema) for b in getattr(astnode, 'bases', None) or [] ) mcls = cls.get_schema_metaclass() if not bases and classname not in mcls.get_root_classes(): default_base = mcls.get_default_base_name() if default_base is not None and classname != default_base: bases = so.ObjectList([ so.ObjectRef(classname=default_base) ]) return bases
def _cmd_tree_from_ast(cls, astnode, context, schema): cmd = super()._cmd_tree_from_ast(astnode, context, schema) if isinstance(astnode, cls.referenced_astnode): objcls = cls.get_schema_metaclass() try: base = utils.ast_to_typeref( qlast.TypeName(maintype=astnode.name), modaliases=context.modaliases, schema=schema) except s_err.ItemNotFoundError: # Certain concrete items, like pointers create # abstract parents implicitly. nname = objcls.get_shortname(cmd.classname) base = so.ObjectRef( classname=sn.Name(module=nname.module, name=nname.name)) cmd.add( sd.AlterObjectProperty(property='bases', new_value=so.ObjectList([base]))) referrer_ctx = cls.get_referrer_context(context) referrer_class = referrer_ctx.op.get_schema_metaclass() referrer_name = referrer_ctx.op.classname refdict = referrer_class.get_refdict_for_class(objcls) cmd.add( sd.AlterObjectProperty( property=refdict.backref_attr, new_value=so.ObjectRef(classname=referrer_name))) if getattr(astnode, 'is_abstract', None): cmd.add( sd.AlterObjectProperty(property='is_abstract', new_value=True)) return cmd
def visit_Argument(self, node, *, get_path_prefix): op = ast.ops.EQ name_parts = node.name _, target = self._get_parent_and_current_type() name = get_path_prefix() name.append(qlast.Ptr(ptr=qlast.ObjectRef(name=name_parts))) name = qlast.Path(steps=name) value = self.visit(node.value) # potentially need to cast the 'name' side into a <str>, so as # to be compatible with the 'value' typename = target.get_field_type(name_parts).short_name if (typename != 'str' and gt.EDB_TO_GQL_SCALARS_MAP[typename] in {GraphQLString, GraphQLID}): name = qlast.TypeCast( expr=name, type=qlast.TypeName(maintype=qlast.ObjectRef(name='str')), ) return qlast.BinOp(left=name, op=op, right=value)
def _classname_from_ast(cls, astnode, context, schema): name = super()._classname_from_ast(astnode, context, schema) parent_ctx = cls.get_referrer_context(context) if parent_ctx is not None: referrer_name = parent_ctx.op.classname try: base_ref = utils.ast_to_typeref( qlast.TypeName(maintype=astnode.name), modaliases=context.modaliases, schema=schema) except s_err.ItemNotFoundError: base_name = sn.Name(name) else: base_name = base_ref.classname pcls = cls.get_schema_metaclass() pnn = pcls.get_specialized_name(base_name, referrer_name) name = sn.Name(name=pnn, module=referrer_name.module) return name
def reduce_TUPLE_LANGBRACKET_NamedTupleTypeList_RANGBRACKET(self, *kids): self.val = qlast.TypeName( maintype=qlast.ObjectRef(name='tuple'), subtypes=kids[2].val, )
def reduce_MAP_LANGBRACKET_TypeName_COMMA_TypeName_RANGBRACKET( self, *kids): self.val = qlast.TypeName( maintype=qlast.ObjectRef(name='map'), subtypes=[kids[2].val, kids[4].val], )
def _normalize_view_ptr_expr( shape_el: qlast.ShapeElement, view_scls: s_nodes.Node, *, path_id: irast.PathId, is_insert: bool = False, is_update: bool = False, view_rptr: typing.Optional[context.ViewRPtr] = None, ctx: context.CompilerContext) -> s_pointers.Pointer: steps = shape_el.expr.steps is_linkprop = False is_mutation = is_insert or is_update # Pointers may be qualified by the explicit source # class, which is equivalent to Expr[IS Type]. is_polymorphic = len(steps) == 2 scls = view_scls.peel_view() ptrsource = scls qlexpr = None if is_polymorphic: source = qlast.TypeFilter(expr=qlast.Path(steps=[qlast.Source()]), type=qlast.TypeName(maintype=steps[0])) lexpr = steps[1] ptrsource = schemactx.get_schema_type(steps[0], ctx=ctx) elif len(steps) == 1: lexpr = steps[0] is_linkprop = lexpr.type == 'property' if is_linkprop: if view_rptr is None: raise errors.EdgeQLError( 'invalid reference to link property ' 'in top level shape', context=lexpr.context) ptrsource = scls = view_rptr.ptrcls source = qlast.Source() else: raise RuntimeError( f'unexpected path length in view shape: {len(steps)}') ptrname = (lexpr.ptr.module, lexpr.ptr.name) ptrcls_is_derived = False compexpr = shape_el.compexpr if compexpr is None and is_insert and shape_el.elements: # Nested insert short form: # INSERT Foo { bar: Spam { name := 'name' }} # Expand to: # INSERT Foo { bar := (INSERT Spam { name := 'name' }) } if lexpr.target is not None: ptr_target = schemactx.get_schema_type(lexpr.target, ctx=ctx) else: ptr_target = None base_ptrcls = ptrcls = setgen.resolve_ptr( ptrsource, ptrname, s_pointers.PointerDirection.Outbound, target=ptr_target, ctx=ctx) compexpr = qlast.InsertQuery(subject=qlast.Path(steps=[ qlast.ObjectRef(name=ptrcls.target.name.name, module=ptrcls.target.name.module) ]), shape=shape_el.elements) if compexpr is None: if lexpr.target is not None: ptr_target = schemactx.get_schema_type(lexpr.target, ctx=ctx) else: ptr_target = None base_ptrcls = ptrcls = setgen.resolve_ptr( ptrsource, ptrname, s_pointers.PointerDirection.Outbound, target=ptr_target, ctx=ctx) base_ptr_is_computable = ptrcls in ctx.source_map if ptr_target is not None and ptr_target != base_ptrcls.target: # This happens when a union type target is narrowed by an # [IS Type] construct. Since the derived pointer will have # the correct target, we don't need to do anything, but # remove the [IS] qualifier to prevent recursion. lexpr.target = None else: ptr_target = ptrcls.target ptr_cardinality = ptrcls.cardinality if shape_el.elements: sub_view_rptr = context.ViewRPtr(view_scls, ptrcls, is_insert=is_insert, is_update=is_update) sub_path_id = path_id.extend(ptrcls, target=ptrcls.target) ctx.path_scope.attach_path(sub_path_id) if is_update: for subel in shape_el.elements or []: is_prop = (isinstance(subel.expr.steps[0], qlast.Ptr) and subel.expr.steps[0].type == 'property') if not is_prop: raise errors.EdgeQLError( 'only references to link properties are allowed ' 'in nested UPDATE shapes', context=subel.context) ptr_target = _process_view(scls=ptr_target, path_id=sub_path_id, view_rptr=sub_view_rptr, elements=shape_el.elements, is_update=True, ctx=ctx) else: ptr_target = _process_view(scls=ptr_target, path_id=sub_path_id, view_rptr=sub_view_rptr, elements=shape_el.elements, ctx=ctx) ptrcls = sub_view_rptr.derived_ptrcls if ptrcls is None: ptrcls_is_derived = False ptrcls = sub_view_rptr.ptrcls else: ptrcls_is_derived = True if (shape_el.where or shape_el.orderby or shape_el.offset or shape_el.limit or base_ptr_is_computable or is_polymorphic): if qlexpr is None: qlexpr = qlast.Path(steps=[source, lexpr]) qlexpr = astutils.ensure_qlstmt(qlexpr) qlexpr.where = shape_el.where qlexpr.orderby = shape_el.orderby qlexpr.offset = shape_el.offset qlexpr.limit = shape_el.limit else: try: base_ptrcls = ptrcls = setgen.resolve_ptr( ptrsource, ptrname, s_pointers.PointerDirection.Outbound, ctx=ctx) except errors.EdgeQLReferenceError: if is_mutation: raise ptr_module = (ptrname[0] or ctx.derived_target_module or scls.name.module) if is_linkprop: ptr_metacls = s_props.Property else: ptr_metacls = s_links.Link ptr_name = sn.SchemaName(module=ptr_module, name=ptrname[1]) base_ptrcls = ptrcls = ptr_metacls(name=ptr_name) qlexpr = astutils.ensure_qlstmt(compexpr) with ctx.newscope(fenced=True) as shape_expr_ctx: # Put current pointer class in context, so # that references to link properties in sub-SELECT # can be resolved. This is necessary for proper # evaluation of link properties on computable links, # most importantly, in INSERT/UPDATE context. shape_expr_ctx.view_rptr = context.ViewRPtr(view_scls, ptrcls, is_insert=is_insert, is_update=is_update) shape_expr_ctx.path_scope.unnest_fence = True if is_mutation: shape_expr_ctx.expr_exposed = True irexpr = dispatch.compile(qlexpr, ctx=shape_expr_ctx) irexpr.context = compexpr.context derived_ptrcls = shape_expr_ctx.view_rptr.derived_ptrcls if derived_ptrcls is not None: ptrcls_is_derived = True ptrcls = derived_ptrcls inferred_cardinality = pathctx.infer_cardinality(irexpr, ctx=ctx) if inferred_cardinality == irast.Cardinality.MANY: ptr_cardinality = s_pointers.PointerCardinality.ManyToMany else: ptr_cardinality = s_pointers.PointerCardinality.ManyToOne ptr_target = irutils.infer_type(irexpr, ctx.schema) if ptr_target is None: msg = 'cannot determine expression result type' raise errors.EdgeQLError(msg, context=shape_el.context) if is_mutation and not ptr_target.assignment_castable_to( base_ptrcls.target, schema=ctx.schema): # Validate that the insert/update expression is # of the correct class. lname = f'({ptrsource.name}).{ptrcls.shortname.name}' expected = [repr(str(base_ptrcls.target.name))] raise edgedb_error.InvalidPointerTargetError( f'invalid target for link {str(lname)!r}: ' f'{str(ptr_target.name)!r} (expecting ' f'{" or ".join(expected)})') if is_mutation and base_ptrcls.singular(): pathctx.enforce_singleton(irexpr, ctx=ctx) if qlexpr is not None or ptr_target is not ptrcls.target: if not ptrcls_is_derived: if ptrcls.is_link_property(): rptrcls = view_rptr.derived_ptrcls if rptrcls is None: rptrcls = schemactx.derive_view( view_rptr.ptrcls, view_rptr.source, view_scls, is_insert=view_rptr.is_insert, is_update=view_rptr.is_update, ctx=ctx) view_rptr.derived_ptrcls = rptrcls src_scls = rptrcls else: src_scls = view_scls ptrcls = schemactx.derive_view(ptrcls, src_scls, ptr_target, is_insert=is_insert, is_update=is_update, derived_name_quals=[view_scls.name], ctx=ctx) if qlexpr is not None: ctx.source_map[ptrcls] = (qlexpr, ctx) ptrcls.computable = True if not is_mutation: ptrcls.cardinality = ptr_cardinality if ptrcls.shortname == 'std::__type__' and qlexpr is not None: msg = 'cannot assign to __type__' raise errors.EdgeQLError(msg, context=shape_el.context) return ptrcls
def reduce_ATTRIBUTE_ParenRawString_NL(self, *kids): eql = kids[1].parse_as_attribute_decl() self.val = esast.AttributeDeclaration( name=eql.name.name, extends=[qlast.TypeName(maintype=base) for base in eql.bases], type=eql.type)