def _float_to_path(self, token, context): from edgedb.lang.schema import pointers as s_pointers # make sure that the float is of the type 0.1 parts = token.val.split('.') if not (len(parts) == 2 and parts[0].isdigit() and parts[1].isdigit()): raise EdgeQLSyntaxError(f"Unexpected {token.val!r}", context=token.context) # context for the AST is established manually here return [ qlast.Ptr( ptr=qlast.ObjectRef( name=parts[0], context=token.context, ), direction=s_pointers.PointerDirection.Outbound, context=context, ), qlast.Ptr( ptr=qlast.ObjectRef( name=parts[1], context=token.context, ), direction=s_pointers.PointerDirection.Outbound, context=token.context, ) ]
def visit_Set(self, node): if node.expr is not None: result = self.visit(node.expr) else: links = [] while node.rptr and (not node.show_as_anchor or self.context.inline_anchors): rptr = node.rptr ptrcls = rptr.ptrcls pname = ptrcls.shortname if isinstance(rptr.target.scls, s_objtypes.ObjectType): target = rptr.target.scls.shortname target = qlast.ObjectRef(name=target.name, module=target.module) else: target = None link = qlast.Ptr(ptr=qlast.ObjectRef(name=pname.name, ), direction=rptr.direction, target=target) if isinstance(ptrcls.source, s_links.Link): link.type = 'property' links.append(link) node = node.rptr.source result = qlast.Path() if node.show_as_anchor and not self.context.inline_anchors: if issubclass(node.show_as_anchor, qlast.Expr): step = node.show_as_anchor() else: step = qlast.ObjectRef(name=node.show_as_anchor) else: step = qlast.ObjectRef(name=node.scls.shortname.name, module=node.scls.shortname.module) result.steps.append(step) result.steps.extend(reversed(links)) if node.shape: result = qlast.Shape(expr=result, elements=[]) for el in node.shape: rptr = el.rptr ptrcls = rptr.ptrcls pn = ptrcls.shortname pn = qlast.ShapeElement(expr=qlast.Path(steps=[ qlast.Ptr(ptr=qlast.ObjectRef(name=pn.name), direction=rptr.direction) ])) result.elements.append(pn) return result
def reduce_PathStepName(self, *kids): from edgedb.lang.schema import pointers as s_pointers self.val = qlast.Path(steps=[ qlast.Ptr(ptr=kids[0].val, direction=s_pointers.PointerDirection.Outbound) ])
def get_path_prefix(self, end_trim=None): # flatten the path path = [step for psteps in self._context.path for step in psteps] # find the first shadowed root for i, step in enumerate(path): base = step.type if base.shadow: break # trim the rest of the path path = path[i + 1:end_trim] prefix = [qlast.ObjectRef(module=base.module, name=base.short_name)] prefix.extend( qlast.Ptr(ptr=qlast.ObjectRef(name=step.name)) for step in path) return prefix
def _prepare_field(self, node): path = self._context.path[-1] include_base = self._context.include_base[-1] is_top = self._is_top_level_field(node) spath = self._context.path[-1] prevt, target = self._get_parent_and_current_type() # insert normal or specialized link steps = [] if include_base: base = spath[0].type steps.append( qlast.ObjectRef(module=base.module, name=base.short_name)) steps.append(qlast.Ptr(ptr=qlast.ObjectRef(name=node.name))) return is_top, path, prevt, target, steps
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 visit_Field(self, node): if self._is_duplicate_field(node): return is_top, path, prevt, target, steps = \ self._prepare_field(node) json_mode = False # determine if there needs to be extra subqueries if not prevt.dummy and target.dummy: json_mode = True # this is a special introspection type eql, shape, filterable = target.get_template() spec = qlast.ShapeElement( expr=qlast.Path(steps=[ qlast.Ptr(ptr=qlast.ObjectRef( name=node.alias or node.name)) ]), compexpr=eql, ) elif prevt.is_field_shadowed(node.name): if prevt.has_native_field(node.name) and not node.alias: spec = filterable = shape = qlast.ShapeElement( expr=qlast.Path(steps=steps), ) else: prefix = qlast.Path(steps=self.get_path_prefix(-1)) eql, shape, filterable = prevt.get_field_template( node.name, parent=prefix, has_shape=bool(node.selection_set)) spec = qlast.ShapeElement( expr=qlast.Path(steps=[ qlast.Ptr(ptr=qlast.ObjectRef( # this is already a sub-query name=node.alias or node.name)) ]), compexpr=eql) else: # if the parent is NOT a shadowed type, we need an explicit SELECT eql, shape, filterable = target.get_template() spec = qlast.ShapeElement( expr=qlast.Path(steps=[ qlast.Ptr(ptr=qlast.ObjectRef( # this is already a sub-query name=node.alias or node.name)) ]), compexpr=eql) if node.selection_set is not None: if json_mode: pass else: # a single recursion target, so we can process # selection set now self._context.fields.append({}) vals = self.visit(node.selection_set) self._context.fields.pop() if shape: shape.elements = vals if filterable: filterable.where = self._visit_path_where(node.arguments) path.pop() return spec
def reduce_AT_ShortNodeName(self, *kids): from edgedb.lang.schema import pointers as s_pointers self.val = qlast.Ptr(ptr=kids[1].val, direction=s_pointers.PointerDirection.Outbound, type='property')
def reduce_DOTBW_PathStepName(self, *kids): from edgedb.lang.schema import pointers as s_pointers self.val = qlast.Ptr(ptr=kids[1].val, direction=s_pointers.PointerDirection.Inbound)
def reduce_DOT_ICONST(self, *kids): # this is a valid link-like syntax for accessing unnamed tuples from edgedb.lang.schema import pointers as s_pointers self.val = qlast.Ptr(ptr=qlast.ObjectRef(name=kids[1].val), direction=s_pointers.PointerDirection.Outbound)
def reduce_AT_ShortNodeName(self, *kids): self.val = qlast.Path( steps=[qlast.Ptr(ptr=kids[1].val, type='property')])
def _process_view(*, scls: s_nodes.Node, path_id: irast.PathId, 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.CompilerContext) -> s_nodes.Node: view_scls = schemactx.derive_view(scls, 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 pointers = [] for shape_el in elements: with ctx.newscope(fenced=True) as scopectx: scopectx.singletons = ctx.singletons.copy() scopectx.singletons.add(path_id) pointers.append( _normalize_view_ptr_expr(shape_el, view_scls, path_id=path_id, is_insert=is_insert, is_update=is_update, view_rptr=view_rptr, ctx=scopectx)) if is_insert: explicit_ptrs = {ptrcls.shortname for ptrcls in pointers} for pn, ptrcls in scls.pointers.items(): if (not ptrcls.default or pn in explicit_ptrs or ptrcls.is_pure_computable()): continue default_ql = qlast.ShapeElement(expr=qlast.Path(steps=[ qlast.Ptr(ptr=qlast.ObjectRef(name=ptrcls.shortname.name, module=ptrcls.shortname.module)) ])) with ctx.newscope(fenced=True) as scopectx: scopectx.singletons = ctx.singletons.copy() scopectx.singletons.add(path_id) pointers.append( _normalize_view_ptr_expr(default_ql, view_scls, path_id=path_id, is_insert=is_insert, is_update=is_update, view_rptr=view_rptr, ctx=scopectx)) # Check if the view shape includes _only_ the link properties. # If so, we do not need to derive a new target view. lprops_only = True for ptrcls in pointers: if not ptrcls.is_link_property(): lprops_only = False break if lprops_only: view_scls = scls for ptrcls in pointers: if ptrcls.is_link_property(): source = view_rptr.derived_ptrcls else: source = view_scls if ptrcls.source is source and isinstance(source, s_sources.Source): # source may be an ScalarType in shapes that reference __type__, # hence the isinstance check. source.add_pointer(ptrcls, replace=True) if is_defining_shape: if source is None: # The nested shape is merely selecting the pointer, # so the link class has not been derived. But for # the purposes of shape tracking, we must derive it # still. source = 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 = source ctx.class_shapes[source].append(ptrcls) return view_scls