def get_field_template(self, name, *, parent=None, has_shape=False): eql = shape = filterable = None if self.dummy: return eql, shape, filterable if name == '__typename' and not self.is_field_shadowed(name): is_view = self.edb_base.is_view(self.edb_schema) if is_view: eql = parse_fragment(f'{self.gql_typename!r}') else: eql = parse_fragment( f'''stdgraphql::short_name( {codegen.generate_source(parent)}.__type__.name)''') elif has_shape: eql = filterable = parse_fragment( f'''SELECT {codegen.generate_source(parent)}. {codegen.generate_source(qlast.ObjectRef(name=name))} {{ xxx }} ''') filterable = eql shape = filterable.result elif self.get_field_type(name).is_json: eql = filterable = parse_fragment( f'''SELECT to_str({codegen.generate_source(parent)}. {codegen.generate_source(qlast.ObjectRef(name=name))}) ''') else: eql = filterable = parse_fragment( f'''SELECT {codegen.generate_source(parent)}. {codegen.generate_source(qlast.ObjectRef(name=name))} ''') return eql, shape, filterable
def rewrite_refs(expr, callback): """Rewrite class references in EdgeQL expression.""" tree = qlparser.parse_fragment(expr) def _cb(node): if isinstance(node, qlast.ObjectRef): name = sn.Name(name=node.name, module=node.module) upd = callback(name) if name != upd: node.name = upd.name node.module = upd.module ast.find_children(tree, _cb) return qlcodegen.generate_source(tree, pretty=False)
def renormalize_compat( norm_qltree: qlast.Base, orig_text: str, *, schema: s_schema.Schema, localnames: AbstractSet[str] = frozenset(), ) -> qlast.Base: """Renormalize an expression normalized with imprint_expr_context(). This helper takes the original, unmangled expression, an EdgeQL AST tree of the same expression mangled with `imprint_expr_context()` (which injects extra WITH MODULE clauses), and produces a normalized expression with explicitly qualified identifiers instead. Old dumps are the main user of this facility. """ orig_qltree = qlparser.parse_fragment(orig_text) norm_aliases: Dict[Optional[str], str] = {} assert isinstance(norm_qltree, qlast.Command) for alias in norm_qltree.aliases: if isinstance(alias, qlast.ModuleAliasDecl): norm_aliases[alias.alias] = alias.module if isinstance(orig_qltree, qlast.Command): orig_aliases: Dict[Optional[str], str] = {} for alias in orig_qltree.aliases: if isinstance(alias, qlast.ModuleAliasDecl): orig_aliases[alias.alias] = alias.module modaliases = { k: v for k, v in norm_aliases.items() if k not in orig_aliases } else: modaliases = norm_aliases normalize( orig_qltree, schema=schema, modaliases=modaliases, localnames=localnames, ) return orig_qltree
def computable_ptr_set( rptr: irast.Pointer, *, unnest_fence: bool = False, same_computable_scope: bool = False, srcctx: Optional[parsing.ParserContext] = None, ctx: context.ContextLevel, ) -> irast.Set: """Return ir.Set for a pointer defined as a computable.""" ptrcls = typegen.ptrcls_from_ptrref(rptr.ptrref, ctx=ctx) source_set = rptr.source source_scls = get_set_type(source_set, ctx=ctx) # process_view() may generate computable pointer expressions # in the form "self.linkname". To prevent infinite recursion, # self must resolve to the parent type of the view NOT the view # type itself. Similarly, when resolving computable link properties # make sure that we use the parent of derived ptrcls. if source_scls.is_view(ctx.env.schema): source_set_stype = source_scls.peel_view(ctx.env.schema) source_set = new_set_from_set(source_set, stype=source_set_stype, preserve_scope_ns=True, ctx=ctx) source_set.shape = [] if source_set.rptr is not None: source_rptrref = source_set.rptr.ptrref if source_rptrref.base_ptr is not None: source_rptrref = source_rptrref.base_ptr source_set.rptr = irast.Pointer( source=source_set.rptr.source, target=source_set, ptrref=source_rptrref, direction=source_set.rptr.direction, ) qlctx: Optional[context.ContextLevel] inner_source_path_id: Optional[irast.PathId] try: comp_info = ctx.source_map[ptrcls] qlexpr = comp_info.qlexpr assert isinstance(comp_info.context, context.ContextLevel) qlctx = comp_info.context inner_source_path_id = comp_info.path_id path_id_ns = comp_info.path_id_ns except KeyError: comp_expr = ptrcls.get_expr(ctx.env.schema) schema_qlexpr: Optional[qlast.Expr] = None if comp_expr is None and ctx.env.options.apply_query_rewrites: schema_deflt = ptrcls.get_schema_reflection_default(ctx.env.schema) if schema_deflt is not None: assert isinstance(ptrcls, s_pointers.Pointer) ptrcls_n = ptrcls.get_shortname(ctx.env.schema).name schema_qlexpr = qlast.BinOp( left=qlast.Path(steps=[ qlast.Source(), qlast.Ptr( ptr=qlast.ObjectRef(name=ptrcls_n), direction=s_pointers.PointerDirection.Outbound, type=('property' if ptrcls.is_link_property( ctx.env.schema) else None)) ], ), right=qlparser.parse_fragment(schema_deflt), op='??', ) if schema_qlexpr is None: if comp_expr is None: ptrcls_sn = ptrcls.get_shortname(ctx.env.schema) raise errors.InternalServerError( f'{ptrcls_sn!r} is not a computable pointer') comp_qlexpr = qlparser.parse(comp_expr.text) assert isinstance(comp_qlexpr, qlast.Expr), 'expected qlast.Expr' schema_qlexpr = comp_qlexpr # NOTE: Validation of the expression type is not the concern # of this function. For any non-object pointer target type, # the default expression must be assignment-cast into that # type. target_scls = ptrcls.get_target(ctx.env.schema) assert target_scls is not None if not target_scls.is_object_type(): schema_qlexpr = qlast.TypeCast( type=typegen.type_to_ql_typeref(target_scls, ctx=ctx), expr=schema_qlexpr, ) qlexpr = astutils.ensure_qlstmt(schema_qlexpr) qlctx = None inner_source_path_id = None path_id_ns = None newctx: Callable[[], ContextManager[context.ContextLevel]] if qlctx is None: # Schema-level computable, completely detached context newctx = ctx.detached else: newctx = _get_computable_ctx(rptr=rptr, source=source_set, source_scls=source_scls, inner_source_path_id=inner_source_path_id, path_id_ns=path_id_ns, same_scope=same_computable_scope, qlctx=qlctx, ctx=ctx) if ptrcls.is_link_property(ctx.env.schema): source_path_id = rptr.source.path_id.ptr_path() else: src_path = rptr.target.path_id.src_path() assert src_path is not None source_path_id = src_path result_path_id = pathctx.extend_path_id( source_path_id, ptrcls=ptrcls, ns=ctx.path_id_namespace, ctx=ctx, ) result_stype = ptrcls.get_target(ctx.env.schema) base_object = ctx.env.schema.get('std::BaseObject', type=s_types.Type) with newctx() as subctx: subctx.disable_shadowing.add(ptrcls) if result_stype != base_object: subctx.view_scls = result_stype subctx.view_rptr = context.ViewRPtr(source_scls, ptrcls=ptrcls, rptr=rptr) # type: ignore subctx.anchors[qlast.Source().name] = source_set subctx.empty_result_type_hint = ptrcls.get_target(ctx.env.schema) subctx.partial_path_prefix = source_set # On a mutation, make the expr_exposed. This corresponds with # a similar check on is_mutation in _normalize_view_ptr_expr. if (source_scls.get_expr_type(ctx.env.schema) != s_types.ExprType.Select): subctx.expr_exposed = True if isinstance(qlexpr, qlast.Statement): subctx.stmt_metadata[qlexpr] = context.StatementMetadata( is_unnest_fence=unnest_fence, iterator_target=True, ) comp_ir_set = ensure_set(dispatch.compile(qlexpr, ctx=subctx), ctx=subctx) comp_ir_set = new_set_from_set(comp_ir_set, path_id=result_path_id, rptr=rptr, context=srcctx, ctx=ctx) rptr.target = comp_ir_set return comp_ir_set
def evaluate_to_python_val(expr, schema, *, modaliases=None) -> object: tree = ql_parser.parse_fragment(expr) return evaluate_ast_to_python_val(tree, schema, modaliases=modaliases)
def qlast(self) -> qlast_.Base: if self._qlast is None: self._qlast = qlparser.parse_fragment(self.text) return self._qlast
def _process_view( *, stype: s_objtypes.ObjectType, 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.QualName] = None, is_insert: bool = False, is_update: bool = False, is_delete: bool = False, parser_context: pctx.ParserContext, ctx: context.ContextLevel, ) -> s_objtypes.ObjectType: if (view_name is None and ctx.env.options.schema_view_mode and view_rptr is not None): # Make sure persistent schema expression aliases have properly formed # names as opposed to the usual mangled form of the ephemeral # aliases. 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 ALIAS V := (SELECT Foo { bar: { baz: { ... } }) # The name of the innermost alias 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.QualName( module=ctx.derived_target_module or '__derived__', name=name, ) view_scls = schemactx.derive_view( stype, is_insert=is_insert, is_update=is_update, is_delete=is_delete, derived_name=view_name, ctx=ctx, ) assert isinstance(view_scls, s_objtypes.ObjectType), view_scls 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: pointer = _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 pointer in pointers: schema = ctx.env.schema vnp = pointer.get_verbosename(schema, with_parent=True) raise errors.QueryError( f'duplicate definition of {vnp}', context=shape_el.context) pointers.append(pointer) if is_insert: explicit_ptrs = { ptrcls.get_local_name(ctx.env.schema) 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 default_expr = ptrcls.get_default(ctx.env.schema) if not default_expr: if ( ptrcls.get_required(ctx.env.schema) and pn != sn.UnqualName('__type__') ): if ptrcls.is_property(ctx.env.schema): # If the target is a sequence, there's no need # for an explicit value. ptrcls_target = ptrcls.get_target(ctx.env.schema) assert ptrcls_target is not None if ptrcls_target.issubclass( ctx.env.schema, ctx.env.schema.get( 'std::sequence', type=s_objects.SubclassableObject)): continue vn = ptrcls.get_verbosename( ctx.env.schema, with_parent=True) raise errors.MissingRequiredError( f'missing value for required {vn}') 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, ), ), ], ), compexpr=qlast.DetachedExpr( expr=default_expr.qlast, ), ) 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, from_default=True, view_rptr=view_rptr, ctx=scopectx, ), ) elif ( stype.get_name(ctx.env.schema).module == 'schema' and ctx.env.options.apply_query_rewrites ): explicit_ptrs = { ptrcls.get_local_name(ctx.env.schema) 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 schema_deflt = ptrcls.get_schema_reflection_default(ctx.env.schema) if schema_deflt is None: continue with ctx.newscope(fenced=True) as scopectx: ptr_ref = s_utils.name_to_ast_ref(pn) implicit_ql = qlast.ShapeElement( expr=qlast.Path(steps=[qlast.Ptr(ptr=ptr_ref)]), compexpr=qlast.BinOp( left=qlast.Path( partial=True, steps=[ qlast.Ptr( ptr=ptr_ref, direction=( s_pointers.PointerDirection.Outbound ), ) ], ), right=qlparser.parse_fragment(schema_deflt), op='??', ), ) # Note: we only need to record the schema default # as a computable, but not include it in the type # shape, so we ignore the return value. _normalize_view_ptr_expr( implicit_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: source: Union[s_types.Type, s_pointers.PointerLike] if ptrcls.is_link_property(ctx.env.schema): assert view_rptr is not None and view_rptr.ptrcls is not None source = view_rptr.ptrcls else: source = view_scls if is_defining_shape: cinfo = ctx.source_map.get(ptrcls) if cinfo is not None: shape_op = cinfo.shape_op else: shape_op = qlast.ShapeOp.ASSIGN ctx.env.view_shapes[source].append((ptrcls, shape_op)) if (view_rptr is not None and view_rptr.ptrcls is not None and view_scls != stype): ctx.env.schema = view_scls.set_field_value( ctx.env.schema, 'rptr', view_rptr.ptrcls) return view_scls
def compile_FunctionCall(expr: qlast.FunctionCall, *, ctx: context.ContextLevel) -> irast.Base: env = ctx.env if isinstance(expr.func, str): if (ctx.env.func_params is not None and ctx.env.func_params.get_by_name(env.schema, expr.func)): raise errors.QueryError(f'parameter `{expr.func}` is not callable', context=expr.context) funcname = expr.func else: funcname = sn.Name(expr.func[1], expr.func[0]) funcs = env.schema.get_functions(funcname, module_aliases=ctx.modaliases) if funcs is None: raise errors.QueryError(f'could not resolve function name {funcname}', context=expr.context) in_polymorphic_func = (ctx.env.func_params is not None and ctx.env.func_params.has_polymorphic(env.schema)) in_abstract_constraint = ( in_polymorphic_func and ctx.env.parent_object_type is s_constr.Constraint) args, kwargs = compile_call_args(expr, funcname, ctx=ctx) matched = polyres.find_callable(funcs, args=args, kwargs=kwargs, ctx=ctx) if not matched: raise errors.QueryError( f'could not find a function variant {funcname}', context=expr.context) elif len(matched) > 1: if in_abstract_constraint: matched_call = matched[0] else: raise errors.QueryError(f'function {funcname} is not unique', context=expr.context) else: matched_call = matched[0] func = matched_call.func assert isinstance(func, s_func.Function) func_name = func.get_shortname(env.schema) if not ctx.env.session_mode and func.get_session_only(env.schema): raise errors.QueryError( f'{func_name}() cannot be called in a non-session context', context=expr.context) final_args, params_typemods = finalize_args(matched_call, ctx=ctx) matched_func_params = func.get_params(env.schema) variadic_param = matched_func_params.find_variadic(env.schema) variadic_param_type = None if variadic_param is not None: variadic_param_type = irtyputils.type_to_typeref( env.schema, variadic_param.get_type(env.schema)) matched_func_ret_type = func.get_return_type(env.schema) is_polymorphic = (any( p.get_type(env.schema).is_polymorphic(env.schema) for p in matched_func_params.objects(env.schema)) and matched_func_ret_type.is_polymorphic(env.schema)) matched_func_initial_value = func.get_initial_value(env.schema) if not in_abstract_constraint: # We cannot add strong references to functions from # abstract constraints, since we cannot know which # form of the function is actually used. env.schema_refs.add(func) func_initial_value: typing.Optional[irast.Set] if matched_func_initial_value is not None: iv_ql = qlast.TypeCast( expr=qlparser.parse_fragment(matched_func_initial_value.text), type=typegen.type_to_ql_typeref(matched_call.return_type, ctx=ctx), ) func_initial_value = setgen.ensure_set( dispatch.compile(iv_ql, ctx=ctx), ctx=ctx, ) else: func_initial_value = None rtype = matched_call.return_type path_id = pathctx.get_expression_path_id(rtype, ctx=ctx) if rtype.is_tuple(): rtype = typing.cast(s_types.Tuple, rtype) tuple_path_ids = [] nested_path_ids = [] for n, st in rtype.iter_subtypes(ctx.env.schema): elem_path_id = pathctx.get_tuple_indirection_path_id( path_id, n, st, ctx=ctx).strip_weak_namespaces() if st.is_tuple(): nested_path_ids.append([ pathctx.get_tuple_indirection_path_id( elem_path_id, nn, sst, ctx=ctx).strip_weak_namespaces() for nn, sst in st.iter_subtypes(ctx.env.schema) ]) tuple_path_ids.append(elem_path_id) for nested in nested_path_ids: tuple_path_ids.extend(nested) else: tuple_path_ids = [] fcall = irast.FunctionCall( args=final_args, func_module_id=env.schema.get_global(s_mod.Module, func_name.module).id, func_shortname=func_name, func_polymorphic=is_polymorphic, func_sql_function=func.get_from_function(env.schema), force_return_cast=func.get_force_return_cast(env.schema), session_only=func.get_session_only(env.schema), volatility=func.get_volatility(env.schema), sql_func_has_out_params=func.get_sql_func_has_out_params(env.schema), error_on_null_result=func.get_error_on_null_result(env.schema), params_typemods=params_typemods, context=expr.context, typeref=irtyputils.type_to_typeref(env.schema, rtype), typemod=matched_call.func.get_return_typemod(env.schema), has_empty_variadic=matched_call.has_empty_variadic, variadic_param_type=variadic_param_type, func_initial_value=func_initial_value, tuple_path_ids=tuple_path_ids, ) return setgen.ensure_set(fcall, typehint=rtype, path_id=path_id, ctx=ctx)
def get_ql_default(self, schema): from edb.edgeql import parser as ql_parser return ql_parser.parse_fragment(self.get_default(schema))
is_polymorphic=is_polymorphic, arg_ctxs=arg_ctxs, ctx=ctx, ) if not in_abstract_constraint: # We cannot add strong references to functions from # abstract constraints, since we cannot know which # form of the function is actually used. env.add_schema_ref(func, expr) func_initial_value: Optional[irast.Set] if matched_func_initial_value is not None: iv_ql = qlast.TypeCast( expr=qlparser.parse_fragment(matched_func_initial_value.text), type=typegen.type_to_ql_typeref(matched_call.return_type, ctx=ctx), ) func_initial_value = setgen.ensure_set( dispatch.compile(iv_ql, ctx=ctx), ctx=ctx, ) else: func_initial_value = None rtype = matched_call.return_type path_id = pathctx.get_expression_path_id(rtype, ctx=ctx) if rtype.is_tuple(env.schema): rtype = cast(s_types.Tuple, rtype) tuple_path_ids = []
def compile_FunctionCall( expr: qlast.Base, *, ctx: context.ContextLevel) -> irast.Base: env = ctx.env if isinstance(expr.func, str): if ctx.func is not None: ctx_func_params = ctx.func.get_params(env.schema) if ctx_func_params.get_by_name(env.schema, expr.func): raise errors.QueryError( f'parameter `{expr.func}` is not callable', context=expr.context) funcname = expr.func else: funcname = sn.Name(expr.func[1], expr.func[0]) funcs = env.schema.get_functions(funcname, module_aliases=ctx.modaliases) if funcs is None: raise errors.QueryError( f'could not resolve function name {funcname}', context=expr.context) args, kwargs = compile_call_args(expr, funcname, ctx=ctx) matched = polyres.find_callable(funcs, args=args, kwargs=kwargs, ctx=ctx) if not matched: raise errors.QueryError( f'could not find a function variant {funcname}', context=expr.context) elif len(matched) > 1: raise errors.QueryError( f'function {funcname} is not unique', context=expr.context) else: matched_call = matched[0] args, params_typemods = finalize_args(matched_call, ctx=ctx) matched_func_params = matched_call.func.get_params(env.schema) variadic_param = matched_func_params.find_variadic(env.schema) variadic_param_type = None if variadic_param is not None: variadic_param_type = irtyputils.type_to_typeref( env.schema, variadic_param.get_type(env.schema)) matched_func_ret_type = matched_call.func.get_return_type(env.schema) is_polymorphic = ( any(p.get_type(env.schema).is_polymorphic(env.schema) for p in matched_func_params.objects(env.schema)) and matched_func_ret_type.is_polymorphic(env.schema) ) matched_func_initial_value = matched_call.func.get_initial_value( env.schema) func = matched_call.func func_name = func.get_shortname(env.schema) if matched_func_initial_value is not None: iv_ql = qlast.TypeCast( expr=qlparser.parse_fragment(matched_func_initial_value.text), type=typegen.type_to_ql_typeref(matched_call.return_type, ctx=ctx), ) func_initial_value = dispatch.compile(iv_ql, ctx=ctx) else: func_initial_value = None rtype = matched_call.return_type path_id = pathctx.get_expression_path_id(rtype, ctx=ctx) if rtype.is_tuple(): tuple_path_ids = [] nested_path_ids = [] for n, st in rtype.iter_subtypes(ctx.env.schema): elem_path_id = pathctx.get_tuple_indirection_path_id( path_id, n, st, ctx=ctx).strip_weak_namespaces() if st.is_tuple(): nested_path_ids.append([ pathctx.get_tuple_indirection_path_id( elem_path_id, nn, sst, ctx=ctx).strip_weak_namespaces() for nn, sst in st.iter_subtypes(ctx.env.schema) ]) tuple_path_ids.append(elem_path_id) for nested in nested_path_ids: tuple_path_ids.extend(nested) else: tuple_path_ids = None fcall = irast.FunctionCall( args=args, func_module_id=env.schema.get_global( s_mod.Module, func_name.module).id, func_shortname=func_name, func_polymorphic=is_polymorphic, func_sql_function=func.get_from_function(env.schema), force_return_cast=func.get_force_return_cast(env.schema), sql_func_has_out_params=func.get_sql_func_has_out_params(env.schema), error_on_null_result=func.get_error_on_null_result(env.schema), params_typemods=params_typemods, context=expr.context, typeref=irtyputils.type_to_typeref(env.schema, rtype), typemod=matched_call.func.get_return_typemod(env.schema), has_empty_variadic=matched_call.has_empty_variadic, variadic_param_type=variadic_param_type, func_initial_value=func_initial_value, tuple_path_ids=tuple_path_ids, ) return setgen.ensure_set(fcall, typehint=rtype, path_id=path_id, ctx=ctx)