def compile_ast_fragment_to_ir(tree, schema, *, anchors=None, path_prefix_anchor=None, location=None, modaliases=None): """Compile given EdgeQL AST fragment into EdgeDB IR.""" ctx = stmtctx.init_context(schema=schema, anchors=anchors, modaliases=modaliases) ctx.clause = location or 'where' if path_prefix_anchor is not None: path_prefix = anchors[path_prefix_anchor] ctx.partial_path_prefix = setgen.class_set(path_prefix, ctx=ctx) ctx.partial_path_prefix.anchor = path_prefix_anchor ctx.partial_path_prefix.show_as_anchor = path_prefix_anchor ir_set = dispatch.compile(tree, ctx=ctx) try: result_type = inference.infer_type(ir_set, ctx.env) except errors.QueryError: # Not all fragments can be resolved into a concrete type, # that's OK. result_type = None return irast.Statement(expr=ir_set, schema=ctx.env.schema, stype=result_type)
def compile_ast_fragment_to_ir( tree: qlast.Base, schema: s_schema.Schema, *, options: Optional[CompilerOptions] = None, ) -> irast.Statement: """Compile given EdgeQL AST fragment into EdgeDB IR. Unlike :func:`~compile_ast_to_ir` above, this does not assume that the AST *tree* is a complete statement. The expression doesn't even have to resolve to a specific type. Args: tree: EdgeQL AST fragment. schema: Schema instance. Must contain definitions for objects referenced by the AST *tree*. options: An optional :class:`edgeql.compiler.options.CompilerOptions` instance specifying compilation options. Returns: An instance of :class:`ir.ast.Statement`. """ if options is None: options = CompilerOptions() ctx = stmtctx_mod.init_context(schema=schema, options=options) ir_set = dispatch_mod.compile(tree, ctx=ctx) result_type: Optional[s_types.Type] try: result_type = inference_mod.infer_type(ir_set, ctx.env) except errors.QueryError: # Not all fragments can be resolved into a concrete type, # that's OK. result_type = None return irast.Statement( expr=ir_set, schema=ctx.env.schema, stype=result_type, dml_exprs=ctx.env.dml_exprs, )
def fini_expression( ir: irast.Base, *, ctx: context.ContextLevel, ) -> irast.Command: if (isinstance(ir, irast.Set) and pathctx.get_set_scope(ir, ctx=ctx) is None): ir = setgen.scoped_set(ir, ctx=ctx) cardinality = qltypes.Cardinality.AT_MOST_ONE if ctx.path_scope is not None: # The inference context object will be shared between # cardinality and multiplicity inferrers. inf_ctx = inference.make_ctx(env=ctx.env) # Simple expressions have no scope. cardinality = inference.infer_cardinality( ir, scope_tree=ctx.path_scope, ctx=inf_ctx, ) multiplicity: Optional[qltypes.Multiplicity] = None if ctx.env.options.validate_multiplicity: multiplicity = inference.infer_multiplicity( ir, scope_tree=ctx.path_scope, ctx=inf_ctx, ) # Fix up weak namespaces _rewrite_weak_namespaces(ir, ctx) if ctx.path_scope is not None: ctx.path_scope.validate_unique_ids() if isinstance(ir, irast.Command): if isinstance(ir, irast.ConfigCommand): ir.scope_tree = ctx.path_scope # IR is already a Command return ir volatility = inference.infer_volatility(ir, env=ctx.env) if ctx.env.options.schema_view_mode: for view in ctx.view_nodes.values(): if view.is_collection(): continue assert isinstance(view, s_types.InheritingType) _elide_derived_ancestors(view, ctx=ctx) if not isinstance(view, s_sources.Source): continue view_own_pointers = view.get_pointers(ctx.env.schema) for vptr in view_own_pointers.objects(ctx.env.schema): _elide_derived_ancestors(vptr, ctx=ctx) ctx.env.schema = vptr.set_field_value( ctx.env.schema, 'from_alias', True, ) tgt = vptr.get_target(ctx.env.schema) assert tgt is not None if (tgt.is_union_type(ctx.env.schema) and tgt.get_is_opaque_union(ctx.env.schema)): # Opaque unions should manifest as std::BaseObject # in schema views. ctx.env.schema = vptr.set_target( ctx.env.schema, ctx.env.schema.get('std::BaseObject', type=s_types.Type), ) if not isinstance(vptr, s_sources.Source): continue vptr_own_pointers = vptr.get_pointers(ctx.env.schema) for vlprop in vptr_own_pointers.objects(ctx.env.schema): _elide_derived_ancestors(vlprop, ctx=ctx) ctx.env.schema = vlprop.set_field_value( ctx.env.schema, 'from_alias', True, ) expr_type = inference.infer_type(ir, ctx.env) in_polymorphic_func = (ctx.env.options.func_params is not None and ctx.env.options.func_params.has_polymorphic( ctx.env.schema)) if (not in_polymorphic_func and not ctx.env.options.allow_generic_type_output): anytype = expr_type.find_any(ctx.env.schema) if anytype is not None: raise errors.QueryError( 'expression returns value of indeterminate type', hint='Consider using an explicit type cast.', context=ctx.env.type_origins.get(anytype)) if ctx.must_use_views: alias, srcctx = next(iter(ctx.must_use_views.values())) raise errors.QueryError( f'unused alias definition: {str(alias)!r}', context=srcctx, ) result = irast.Statement( expr=ir, params=list(ctx.env.query_parameters.values()), views=ctx.view_nodes, source_map=ctx.source_map, scope_tree=ctx.env.path_scope, cardinality=cardinality, volatility=volatility, multiplicity=multiplicity, stype=expr_type, view_shapes={ src: [ptr for ptr, _ in ptrs] for src, ptrs in ctx.env.view_shapes.items() }, view_shapes_metadata=ctx.env.view_shapes_metadata, schema=ctx.env.schema, schema_refs=frozenset(ctx.env.schema_refs - ctx.env.created_schema_objects), schema_ref_exprs=ctx.env.schema_ref_exprs, new_coll_types=frozenset( t for t in (ctx.env.schema_refs | ctx.env.created_schema_objects) if isinstance(t, s_types.Collection) and t != expr_type), type_rewrites={s.typeref.id: s for s in ctx.type_rewrites.values()}, ) return result
def compile_ast_fragment_to_ir( tree: qlast.Base, schema: s_schema.Schema, *, options: Optional[CompilerOptions] = None, ) -> irast.Statement: """Compile given EdgeQL AST fragment into EdgeDB IR. Unlike :func:`~compile_ast_to_ir` above, this does not assume that the AST *tree* is a complete statement. The expression doesn't even have to resolve to a specific type. Args: tree: EdgeQL AST fragment. schema: Schema instance. Must contain definitions for objects referenced by the AST *tree*. options: An optional :class:`edgeql.compiler.options.CompilerOptions` instance specifying compilation options. Returns: An instance of :class:`ir.ast.Statement`. """ if options is None: options = CompilerOptions() ctx = stmtctx_mod.init_context(schema=schema, options=options) ir_set = dispatch_mod.compile(tree, ctx=ctx) result_type: s_types.Type try: result_type = inference_mod.infer_type(ir_set, ctx.env) except errors.QueryError: # Not all fragments can be resolved into a concrete type, # that's OK. # XXX: Is it really? This doesn't come up in the tests at all. result_type = cast(s_types.Type, None) return irast.Statement( expr=ir_set, schema=ctx.env.schema, stype=result_type, dml_exprs=ctx.env.dml_exprs, views={}, params=[], globals=[], # These values are nonsensical, but ideally the caller does not care cardinality=qltypes.Cardinality.UNKNOWN, multiplicity=qltypes.Multiplicity.ZERO, volatility=qltypes.Volatility.Volatile, view_shapes={}, view_shapes_metadata={}, schema_refs=frozenset(), schema_ref_exprs=None, new_coll_types=frozenset(), scope_tree=ctx.path_scope, source_map={}, type_rewrites={}, singletons=[], )
def compile_ast_fragment_to_ir( tree: qlast.Base, schema: s_schema.Schema, *, modaliases: Optional[Mapping[Optional[str], str]] = None, anchors: Optional[Mapping[str, Any]] = None, path_prefix_anchor: Optional[str] = None, ) -> irast.Statement: """Compile given EdgeQL AST fragment into EdgeDB IR. Unlike :func:`~compile_ast_to_ir` above, this does not assume that the AST *tree* is a complete statement. The expression doesn't even have to resolve to a specific type. Args: tree: EdgeQL AST fragment. schema: Schema instance. Must contain definitions for objects referenced by the AST *tree*. modaliases: Module name resolution table. Useful when this EdgeQL expression is part of some other construct, such as a DDL statement. anchors: Predefined symbol table. Maps identifiers (or ``qlast.SpecialAnchor`` instances) to specified schema objects or IR fragments. path_prefix_anchor: Symbol name used to resolve the prefix of abbreviated path expressions by default. The symbol must be present in *anchors*. Returns: An instance of :class:`ir.ast.Statement`. """ ctx = stmtctx.init_context( schema=schema, anchors=anchors, modaliases=modaliases) if path_prefix_anchor is not None: assert anchors is not None path_prefix = anchors[path_prefix_anchor] assert isinstance(path_prefix, s_types.Type) ctx.partial_path_prefix = setgen.class_set(path_prefix, ctx=ctx) ctx.partial_path_prefix.anchor = path_prefix_anchor ctx.partial_path_prefix.show_as_anchor = path_prefix_anchor ir_set = dispatch.compile(tree, ctx=ctx) result_type: Optional[s_types.Type] try: result_type = inference.infer_type(ir_set, ctx.env) except errors.QueryError: # Not all fragments can be resolved into a concrete type, # that's OK. result_type = None return irast.Statement(expr=ir_set, schema=ctx.env.schema, stype=result_type)
def fini_expression( ir: irast.Base, *, ctx: context.ContextLevel) -> irast.Command: # Run delayed work callbacks. for cb in ctx.completion_work: cb(ctx=ctx) ctx.completion_work.clear() for ir_set in ctx.env.set_types: if ir_set.path_id.namespace: ir_set.path_id = ir_set.path_id.strip_weak_namespaces() if isinstance(ir, irast.Command): if isinstance(ir, irast.ConfigCommand): ir.scope_tree = ctx.path_scope # IR is already a Command return ir if ctx.path_scope is not None: # Simple expressions have no scope. for node in ctx.path_scope.get_all_path_nodes(include_subpaths=True): if node.path_id.namespace: node.path_id = node.path_id.strip_weak_namespaces() cardinality = inference.infer_cardinality( ir, scope_tree=ctx.path_scope, env=ctx.env) else: cardinality = qltypes.Cardinality.ONE if ctx.env.schema_view_mode: for view in ctx.view_nodes.values(): if not hasattr(view, 'get_own_pointers'): # duck check continue view_own_pointers = view.get_own_pointers(ctx.env.schema) for vptr in view_own_pointers.objects(ctx.env.schema): ctx.env.schema = vptr.set_field_value( ctx.env.schema, 'target', vptr.get_target(ctx.env.schema).material_type( ctx.env.schema)) derived_from = vptr.get_derived_from(ctx.env.schema) if (derived_from is not None and derived_from.get_derived_from(ctx.env.schema) is not None): ctx.env.schema = vptr.set_field_value( ctx.env.schema, 'derived_from', derived_from.get_nearest_non_derived_parent( ctx.env.schema, ) ) if not hasattr(vptr, 'get_own_pointers'): continue vptr_own_pointers = vptr.get_own_pointers(ctx.env.schema) for vlprop in vptr_own_pointers.objects(ctx.env.schema): vlprop_target = vlprop.get_target(ctx.env.schema) ctx.env.schema = vlprop.set_field_value( ctx.env.schema, 'target', vlprop_target.material_type(ctx.env.schema)) expr_type = inference.infer_type(ir, ctx.env) if ctx.func is None: anytype = expr_type.find_any(ctx.env.schema) if anytype is not None: raise errors.QueryError( 'expression returns value of indeterminate type', hint='Consider using an explicit type cast.', context=ctx.env.type_origins.get(anytype)) result = irast.Statement( expr=ir, params=ctx.env.query_parameters, views=ctx.view_nodes, source_map=ctx.source_map, scope_tree=ctx.path_scope, cardinality=cardinality, stype=expr_type, view_shapes=ctx.env.view_shapes, view_shapes_metadata=ctx.env.view_shapes_metadata, schema=ctx.env.schema, ) return result
def fini_expression( ir: irast.Base, *, ctx: context.ContextLevel, ) -> irast.Command: # Run delayed work callbacks. for cb in ctx.completion_work: cb(ctx=ctx) ctx.completion_work.clear() for ir_set in ctx.env.set_types: if ir_set.path_id.namespace: ir_set.path_id = ir_set.path_id.strip_weak_namespaces() if isinstance(ir, irast.Command): if isinstance(ir, irast.ConfigCommand): ir.scope_tree = ctx.path_scope # IR is already a Command return ir if ctx.path_scope is not None: # Simple expressions have no scope. for node in ctx.path_scope.path_descendants: if node.path_id.namespace: node.path_id = node.path_id.strip_weak_namespaces() cardinality = inference.infer_cardinality( ir, scope_tree=ctx.path_scope, env=ctx.env) else: cardinality = qltypes.Cardinality.ONE if ctx.env.schema_view_mode: for view in ctx.view_nodes.values(): if view.is_collection(): continue _elide_derived_ancestors(view, ctx=ctx) if not isinstance(view, s_sources.Source): continue view_own_pointers = view.get_pointers(ctx.env.schema) for vptr in view_own_pointers.objects(ctx.env.schema): _elide_derived_ancestors(vptr, ctx=ctx) tgt = vptr.get_target(ctx.env.schema) if (tgt.is_union_type(ctx.env.schema) and tgt.get_is_opaque_union(ctx.env.schema)): # Opaque unions should manifest as std::Object # in schema views. ctx.env.schema = vptr.set_target( ctx.env.schema, ctx.env.schema.get('std::Object'), ) if not hasattr(vptr, 'get_pointers'): continue vptr_own_pointers = vptr.get_pointers(ctx.env.schema) for vlprop in vptr_own_pointers.objects(ctx.env.schema): _elide_derived_ancestors(vlprop, ctx=ctx) expr_type = inference.infer_type(ir, ctx.env) in_polymorphic_func = ( ctx.env.func_params is not None and ctx.env.func_params.has_polymorphic(ctx.env.schema) ) if not in_polymorphic_func and not ctx.env.allow_generic_type_output: anytype = expr_type.find_any(ctx.env.schema) if anytype is not None: raise errors.QueryError( 'expression returns value of indeterminate type', hint='Consider using an explicit type cast.', context=ctx.env.type_origins.get(anytype)) if ctx.must_use_views: alias, srcctx = next(iter(ctx.must_use_views.values())) raise errors.QueryError( f'unused alias definition: {alias!r}', context=srcctx, ) result = irast.Statement( expr=ir, params=ctx.env.query_parameters, views=ctx.view_nodes, source_map=ctx.source_map, scope_tree=ctx.path_scope, cardinality=cardinality, stype=expr_type, view_shapes=ctx.env.view_shapes, view_shapes_metadata=ctx.env.view_shapes_metadata, schema=ctx.env.schema, schema_refs=frozenset( ctx.env.schema_refs - ctx.env.created_schema_objects), new_coll_types=frozenset( t for t in ctx.env.created_schema_objects if isinstance(t, s_types.Collection) and t != expr_type ), ) return result
def fini_expression( ir: irast.Set, *, ctx: context.ContextLevel, ) -> irast.Command: ir = eta_expand.eta_expand_ir(ir, toplevel=True, ctx=ctx) if (isinstance(ir, irast.Set) and pathctx.get_set_scope(ir, ctx=ctx) is None): ir = setgen.scoped_set(ir, ctx=ctx) # exprs_to_clear collects sets where we should never need to use # their expr in pgsql compilation, so we strip it out to make this # more evident in debug output. We have to do the clearing at the # end, because multiplicity/cardinality inference needs to be able # to look through those pointers. exprs_to_clear = _fixup_materialized_sets(ir, ctx=ctx) exprs_to_clear.extend(_find_visible_binding_refs(ir, ctx=ctx)) # The inference context object will be shared between # cardinality and multiplicity inferrers. inf_ctx = inference.make_ctx(env=ctx.env) cardinality = inference.infer_cardinality( ir, scope_tree=ctx.path_scope, ctx=inf_ctx, ) multiplicity = inference.infer_multiplicity( ir, scope_tree=ctx.path_scope, ctx=inf_ctx, ) # Fix up weak namespaces _rewrite_weak_namespaces(ir, ctx) ctx.path_scope.validate_unique_ids() # Infer cardinalities of type rewrites for rw in ctx.type_rewrites.values(): inference.infer_cardinality(rw, scope_tree=ctx.path_scope, ctx=inf_ctx) # ConfigSet and ConfigReset don't like being part of a Set if isinstance(ir.expr, (irast.ConfigSet, irast.ConfigReset)): ir.expr.scope_tree = ctx.path_scope return ir.expr volatility = inference.infer_volatility(ir, env=ctx.env) if ctx.env.options.schema_view_mode: for view in ctx.view_nodes.values(): if view.is_collection(): continue assert isinstance(view, s_types.InheritingType) _elide_derived_ancestors(view, ctx=ctx) if not isinstance(view, s_sources.Source): continue view_own_pointers = view.get_pointers(ctx.env.schema) for vptr in view_own_pointers.objects(ctx.env.schema): _elide_derived_ancestors(vptr, ctx=ctx) ctx.env.schema = vptr.set_field_value( ctx.env.schema, 'from_alias', True, ) tgt = vptr.get_target(ctx.env.schema) assert tgt is not None if (tgt.is_union_type(ctx.env.schema) and tgt.get_is_opaque_union(ctx.env.schema)): # Opaque unions should manifest as std::BaseObject # in schema views. ctx.env.schema = vptr.set_target( ctx.env.schema, ctx.env.schema.get('std::BaseObject', type=s_types.Type), ) if not isinstance(vptr, s_sources.Source): continue vptr_own_pointers = vptr.get_pointers(ctx.env.schema) for vlprop in vptr_own_pointers.objects(ctx.env.schema): _elide_derived_ancestors(vlprop, ctx=ctx) ctx.env.schema = vlprop.set_field_value( ctx.env.schema, 'from_alias', True, ) expr_type = inference.infer_type(ir, ctx.env) in_polymorphic_func = (ctx.env.options.func_params is not None and ctx.env.options.func_params.has_polymorphic( ctx.env.schema)) if (not in_polymorphic_func and not ctx.env.options.allow_generic_type_output): anytype = expr_type.find_any(ctx.env.schema) if anytype is not None: raise errors.QueryError( 'expression returns value of indeterminate type', hint='Consider using an explicit type cast.', context=ctx.env.type_origins.get(anytype)) must_use_views = [val for val in ctx.must_use_views.values() if val] if must_use_views: alias, srcctx = must_use_views[0] raise errors.QueryError( f'unused alias definition: {str(alias)!r}', context=srcctx, ) for ir_set in exprs_to_clear: ir_set.expr = None group.infer_group_aggregates(ir, ctx=ctx) assert isinstance(ir, irast.Set) source_map = { k: v for k, v in ctx.source_map.items() if isinstance(k, s_pointers.Pointer) } result = irast.Statement( expr=ir, params=list(ctx.env.query_parameters.values()), globals=list(ctx.env.query_globals.values()), views=ctx.view_nodes, source_map=source_map, scope_tree=ctx.env.path_scope, cardinality=cardinality, volatility=volatility, multiplicity=multiplicity.own if multiplicity is not None else None, stype=expr_type, view_shapes={ src: [ptr for ptr, op in ptrs if op != qlast.ShapeOp.MATERIALIZE] for src, ptrs in ctx.env.view_shapes.items() if isinstance(src, s_obj.Object) }, view_shapes_metadata=ctx.env.view_shapes_metadata, schema=ctx.env.schema, schema_refs=frozenset(ctx.env.schema_refs - ctx.env.created_schema_objects), schema_ref_exprs=ctx.env.schema_ref_exprs, new_coll_types=frozenset( t for t in (ctx.env.schema_refs | ctx.env.created_schema_objects) if isinstance(t, s_types.Collection) and t != expr_type), type_rewrites={s.typeref.id: s for s in ctx.type_rewrites.values()}, dml_exprs=ctx.env.dml_exprs, singletons=ctx.env.singletons, ) return result
def fini_expression(ir: irast.Base, *, ctx: context.ContextLevel) -> irast.Command: # Run delayed work callbacks. for cb in ctx.completion_work: cb(ctx=ctx) ctx.completion_work.clear() for ir_set in ctx.env.set_types: if ir_set.path_id.namespace: ir_set.path_id = ir_set.path_id.strip_weak_namespaces() if isinstance(ir, irast.Command): if isinstance(ir, irast.ConfigCommand): ir.scope_tree = ctx.path_scope # IR is already a Command return ir if ctx.path_scope is not None: # Simple expressions have no scope. for node in ctx.path_scope.get_all_path_nodes(include_subpaths=True): if node.path_id.namespace: node.path_id = node.path_id.strip_weak_namespaces() cardinality = inference.infer_cardinality(ir, scope_tree=ctx.path_scope, env=ctx.env) else: cardinality = qltypes.Cardinality.ONE if ctx.env.schema_view_mode: for view in ctx.view_nodes.values(): if view.is_collection(): continue _elide_derived_ancestors(view, ctx=ctx) if not isinstance(view, s_sources.Source): continue view_own_pointers = view.get_pointers(ctx.env.schema) for vptr in view_own_pointers.objects(ctx.env.schema): _elide_derived_ancestors(vptr, ctx=ctx) if not hasattr(vptr, 'get_pointers'): continue vptr_own_pointers = vptr.get_pointers(ctx.env.schema) for vlprop in vptr_own_pointers.objects(ctx.env.schema): _elide_derived_ancestors(vlprop, ctx=ctx) expr_type = inference.infer_type(ir, ctx.env) in_polymorphic_func = (ctx.env.func_params is not None and ctx.env.func_params.has_polymorphic(ctx.env.schema)) if not in_polymorphic_func and not ctx.env.allow_generic_type_output: anytype = expr_type.find_any(ctx.env.schema) if anytype is not None: raise errors.QueryError( 'expression returns value of indeterminate type', hint='Consider using an explicit type cast.', context=ctx.env.type_origins.get(anytype)) result = irast.Statement( expr=ir, params=ctx.env.query_parameters, views=ctx.view_nodes, source_map=ctx.source_map, scope_tree=ctx.path_scope, cardinality=cardinality, stype=expr_type, view_shapes=ctx.env.view_shapes, view_shapes_metadata=ctx.env.view_shapes_metadata, schema=ctx.env.schema, schema_refs=frozenset(ctx.env.schema_refs), ) return result