def declare_view_from_schema( viewcls: s_obj.Object, *, ctx: context.ContextLevel) -> irast.Set: vc = ctx.view_class_map.get(viewcls) if vc is not None: return vc with ctx.detached() as subctx: view_expr = qlparser.parse(viewcls.expr) declare_view(view_expr, alias=viewcls.name, fully_detached=True, ctx=subctx) vc = subctx.aliased_views[viewcls.name] ctx.view_class_map[viewcls] = vc ctx.source_map.update(subctx.source_map) ctx.aliased_views[viewcls.name] = subctx.aliased_views[viewcls.name] ctx.view_nodes[vc.name] = vc ctx.view_sets[vc] = subctx.view_sets[vc] return vc
def compile_to_ir(expr, schema, *, anchors=None, security_context=None, modaliases=None, implicit_id_in_shapes=False): """Compile given EdgeQL statement into EdgeDB IR.""" if debug.flags.edgeql_compile: debug.header('EdgeQL TEXT') debug.print(expr) tree = ql_parser.parse(expr, modaliases) return compile_ast_to_ir(tree, schema, anchors=anchors, security_context=security_context, modaliases=modaliases, implicit_id_in_shapes=implicit_id_in_shapes)
def _normalize_constraint_expr(cls, schema, module_aliases, expr, subject, *, inline_anchors=False): from edb.lang.edgeql import parser as edgeql_parser from edb.lang.edgeql import utils as edgeql_utils if isinstance(expr, str): tree = edgeql_parser.parse(expr, module_aliases) else: tree = expr ir, edgeql_tree, _ = edgeql_utils.normalize_tree( tree, schema, modaliases=module_aliases, anchors={qlast.Subject: subject}, inline_anchors=inline_anchors) return edgeql_tree.result, ir
def declare_view_from_schema(viewcls: s_obj.Object, *, ctx: context.ContextLevel) -> irast.Set: vc = ctx.env.schema_view_cache.get(viewcls) if vc is not None: return vc with ctx.detached() as subctx: subctx.expr_exposed = False view_expr = qlparser.parse(viewcls.get_expr(ctx.env.schema)) viewcls_name = viewcls.get_name(ctx.env.schema) declare_view(view_expr, alias=viewcls_name, fully_detached=True, ctx=subctx) vc = subctx.aliased_views[viewcls_name] ctx.env.schema_view_cache[viewcls] = vc ctx.source_map.update(subctx.source_map) ctx.aliased_views[viewcls_name] = subctx.aliased_views[viewcls_name] ctx.view_nodes[vc.get_name(ctx.env.schema)] = vc ctx.view_sets[vc] = subctx.view_sets[vc] return vc
def computable_ptr_set(rptr: irast.Pointer, *, unnest_fence: bool = False, ctx: context.ContextLevel) -> irast.Set: """Return ir.Set for a pointer defined as a computable.""" ptrcls = rptr.ptrcls # Must use an entirely separate context, as the computable # expression is totally independent from the surrounding query. subctx = stmtctx.init_context(schema=ctx.schema) self_ = rptr.source source_scls = self_.scls # 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 rptr.ptrcls.derived_from. if source_scls.is_view(): self_ = copy.copy(self_) self_.scls = source_scls.peel_view() self_.shape = [] if self_.rptr is not None: derived_from = self_.rptr.ptrcls.derived_from if (derived_from is not None and not derived_from.generic() and derived_from.derived_from is not None and ptrcls.is_link_property()): self_.rptr.ptrcls = derived_from subctx.anchors[qlast.Source] = self_ subctx.aliases = ctx.aliases subctx.stmt = ctx.stmt subctx.view_scls = ptrcls.target subctx.view_rptr = context.ViewRPtr(source_scls, ptrcls=ptrcls, rptr=rptr) subctx.toplevel_stmt = ctx.toplevel_stmt subctx.path_scope = ctx.path_scope subctx.pending_cardinality = ctx.pending_cardinality subctx.completion_work = ctx.completion_work subctx.pointer_derivation_map = ctx.pointer_derivation_map subctx.class_shapes = ctx.class_shapes subctx.all_sets = ctx.all_sets subctx.path_scope_map = ctx.path_scope_map subctx.scope_id_ctr = ctx.scope_id_ctr subctx.expr_exposed = ctx.expr_exposed if ptrcls.is_link_property(): source_path_id = rptr.source.path_id.ptr_path() else: source_path_id = rptr.target.path_id.src_path() path_id = source_path_id.extend(ptrcls, s_pointers.PointerDirection.Outbound, ptrcls.target) subctx.path_scope.contain_path(path_id) try: qlexpr, qlctx = ctx.source_map[ptrcls] except KeyError: if not ptrcls.default: raise ValueError( f'{ptrcls.shortname!r} is not a computable pointer') if isinstance(ptrcls.default, s_expr.ExpressionText): qlexpr = astutils.ensure_qlstmt(qlparser.parse(ptrcls.default)) else: qlexpr = qlast.Constant(value=ptrcls.default) qlctx = None else: subctx.modaliases = qlctx.modaliases.copy() subctx.aliased_views = qlctx.aliased_views.new_child() if source_scls.is_view(): subctx.aliased_views[self_.scls.name] = None subctx.source_map = qlctx.source_map.copy() subctx.view_nodes = qlctx.view_nodes.copy() subctx.view_sets = qlctx.view_sets.copy() subctx.view_map = qlctx.view_map.new_child() subctx.singletons = qlctx.singletons.copy() subctx.path_id_namespce = qlctx.path_id_namespace if qlctx is None: # This is a schema-level computable expression, put all # class refs into a separate namespace. subctx.path_id_namespace = (subctx.aliases.get('ns'), ) else: subctx.pending_stmt_own_path_id_namespace = \ irast.WeakNamespace(ctx.aliases.get('ns')) subns = subctx.pending_stmt_full_path_id_namespace = \ {subctx.pending_stmt_own_path_id_namespace} self_view = ctx.view_sets.get(self_.scls) if self_view: if self_view.path_id.namespace: subns.update(self_view.path_id.namespace) inner_path_id = self_view.path_id.merge_namespace( subctx.path_id_namespace + tuple(subns)) else: if self_.path_id.namespace: subns.update(self_.path_id.namespace) inner_path_id = pathctx.get_path_id( self_.scls, ctx=subctx).merge_namespace(subns) remapped_source = new_set_from_set(rptr.source, ctx=subctx) remapped_source.path_id = \ remapped_source.path_id.merge_namespace(subns) subctx.view_map[inner_path_id] = remapped_source if isinstance(qlexpr, qlast.Statement) and unnest_fence: subctx.stmt_metadata[qlexpr] = context.StatementMetadata( is_unnest_fence=True) comp_ir_set = dispatch.compile(qlexpr, ctx=subctx) if ptrcls in ctx.pending_cardinality: comp_ir_set_copy = copy.copy(comp_ir_set) stmtctx.get_pointer_cardinality_later(ptrcls=ptrcls, irexpr=comp_ir_set_copy, ctx=ctx) def _check_cardinality(ctx): if ptrcls.singular(): stmtctx.enforce_singleton_now(comp_ir_set_copy, ctx=ctx) stmtctx.at_stmt_fini(_check_cardinality, ctx=ctx) comp_ir_set.scls = ptrcls.target comp_ir_set.path_id = path_id comp_ir_set.rptr = rptr rptr.target = comp_ir_set return comp_ir_set
def get_concrete_constraint_attrs(cls, schema, subject, *, name, subjectexpr=None, sourcectx=None, args=[], modaliases=None, **kwargs): from edb.lang.edgeql import utils as edgeql_utils from edb.lang.edgeql import parser as edgeql_parser constr_base = schema.get(name, module_aliases=modaliases) module_aliases = {} orig_subject = subject base_subjectexpr = constr_base.get_field_value(schema, 'subjectexpr') if subjectexpr is None: subjectexpr = base_subjectexpr elif base_subjectexpr is not None and subjectexpr != base_subjectexpr: raise errors.InvalidConstraintDefinitionError( 'subjectexpr is already defined for ' + f'{str(name)!r}') if subjectexpr is not None: subject, _ = cls._normalize_constraint_expr( schema, {}, subjectexpr, subject) expr = constr_base.get_field_value(schema, 'expr') if not expr: raise errors.InvalidConstraintDefinitionError( f'missing constraint expression in {name!r}') expr_ql = edgeql_parser.parse(expr, module_aliases) if not args: args = constr_base.get_field_value(schema, 'args') attrs = dict(kwargs) args_map = None if args: args_ql = [ edgeql_parser.parse(arg, module_aliases) for arg in args ] args_map = edgeql_utils.index_parameters( args_ql, parameters=constr_base.get_params(schema), schema=schema) edgeql_utils.inline_parameters(expr_ql, args_map) args_map = { name: edgeql.generate_source(val, pretty=False) for name, val in args_map.items() } errmessage = attrs.get('errmessage') if not errmessage: errmessage = constr_base.get_errmessage(schema) attrs['errmessage'] = errmessage.format( __subject__='{__subject__}', **args_map) args = list(args_map.values()) attrs['args'] = args if expr == '__subject__': expr_context = sourcectx else: expr_context = None if subject is not orig_subject: # subject has been redefined subject_anchor = qlast.SubExpr( expr=subject, anchors={qlast.Subject: orig_subject}) else: subject_anchor = subject expr_text = cls.normalize_constraint_expr(schema, module_aliases, expr_ql, subject=subject_anchor, constraint_name=name, enforce_boolean=True, expr_context=expr_context) attrs['finalexpr'] = expr_text return constr_base, attrs
def computable_ptr_set(rptr: irast.Pointer, *, unnest_fence: bool = False, same_computable_scope: bool = False, ctx: context.ContextLevel) -> irast.Set: """Return ir.Set for a pointer defined as a computable.""" ptrcls = rptr.ptrcls source_set = rptr.source source_scls = source_set.stype # 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 rptr.ptrcls.derived_from. if source_scls.is_view(ctx.env.schema): source_set = new_set_from_set(source_set, preserve_scope_ns=True, ctx=ctx) source_set.stype = source_scls.peel_view(ctx.env.schema) source_set.shape = [] if source_set.rptr is not None: schema = ctx.env.schema derived_from = source_set.rptr.ptrcls.get_derived_from(schema) if (derived_from is not None and not derived_from.generic(schema) and derived_from.get_derived_from(schema) is not None and ptrcls.is_link_property(schema)): source_set.rptr.ptrcls = derived_from try: qlexpr, qlctx, inner_source_path_id, path_id_ns = \ ctx.source_map[ptrcls] except KeyError: ptrcls_default = ptrcls.get_default(ctx.env.schema) if not ptrcls_default: ptrcls_sn = ptrcls.get_shortname(ctx.env.schema) raise ValueError(f'{ptrcls_sn!r} is not a computable pointer') if isinstance(ptrcls_default, s_expr.ExpressionText): qlexpr = astutils.ensure_qlstmt(qlparser.parse(ptrcls_default)) else: qlexpr = qlast.BaseConstant.from_python(ptrcls_default) qlctx = None inner_source_path_id = None path_id_ns = None 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: source_path_id = rptr.target.path_id.src_path() path_id = source_path_id.extend(ptrcls, s_pointers.PointerDirection.Outbound, ptrcls.get_target(ctx.env.schema), ns=ctx.path_id_namespace, schema=ctx.env.schema) with newctx() as subctx: subctx.view_scls = ptrcls.get_target(ctx.env.schema) subctx.view_rptr = context.ViewRPtr(source_scls, ptrcls=ptrcls, rptr=rptr) subctx.anchors[qlast.Source] = source_set subctx.empty_result_type_hint = ptrcls.get_target(ctx.env.schema) if isinstance(qlexpr, qlast.Statement) and unnest_fence: subctx.stmt_metadata[qlexpr] = context.StatementMetadata( is_unnest_fence=True) comp_ir_set = dispatch.compile(qlexpr, ctx=subctx) if ptrcls in ctx.pending_cardinality: comp_ir_set_copy = copy.copy(comp_ir_set) specified_card, source_ctx = ctx.pending_cardinality[ptrcls] stmtctx.get_pointer_cardinality_later(ptrcls=ptrcls, irexpr=comp_ir_set_copy, specified_card=specified_card, source_ctx=source_ctx, ctx=ctx) def _check_cardinality(ctx): if ptrcls.singular(ctx.env.schema): stmtctx.enforce_singleton_now(comp_ir_set_copy, ctx=ctx) stmtctx.at_stmt_fini(_check_cardinality, ctx=ctx) comp_ir_set.stype = ptrcls.get_target(ctx.env.schema) comp_ir_set.path_id = path_id comp_ir_set.rptr = rptr rptr.target = comp_ir_set return comp_ir_set
def process_specialized_constraint(cls, schema, constraint, params=None): from edb.lang.edgeql import utils as edgeql_utils from edb.lang.edgeql import parser as edgeql_parser assert constraint.subject is not None module_aliases = {} # check to make sure that the specialized constraint doesn't redefine # an already defined subjectexpr if constraint.subjectexpr is not None: for base in constraint.bases: base_se = base.get_field_value('subjectexpr') if base_se and base_se != constraint.subjectexpr: raise s_errors.InvalidConstraintDefinitionError( 'subjectexpr is already defined for ' + f'{constraint.name!r}') subject = constraint.subject subjectexpr = constraint.get_field_value('subjectexpr') if subjectexpr: subject, _ = cls._normalize_constraint_expr( schema, {}, subjectexpr, subject) expr = constraint.get_field_value('expr') if not expr: raise s_errors.InvalidConstraintDefinitionError( f'missing constraint expression in {constraint.name!r}') expr_ql = edgeql_parser.parse(expr, module_aliases) if params: args = params else: args = constraint.get_field_value('args') args_map = None if args: if constraint.varparam is not None: varparam = constraint.varparam else: varparam = None args_ql = [ edgeql_parser.parse(arg, module_aliases) for arg in args ] args_map = edgeql_utils.index_parameters(args_ql, varparam=varparam) edgeql_utils.inline_parameters(expr_ql, args_map) args_map = { f'${name}': edgeql.generate_source(val, pretty=False) for name, val in args_map.items() } constraint.errmessage = constraint.errmessage.format( __subject__='{__subject__}', **args_map) args = list(args_map.values()) if expr == '__subject__': expr_context = \ constraint.get_attribute_source_context('subjectexpr') else: expr_context = \ constraint.get_attribute_source_context('expr') if subject is not constraint.subject: # subject has been redefined subject_anchor = qlast.SubExpr( expr=subject, anchors={qlast.Subject: constraint.subject}) else: subject_anchor = subject expr_text = cls.normalize_constraint_expr(schema, module_aliases, expr_ql, subject=subject_anchor, constraint=constraint, enforce_boolean=True, expr_context=expr_context) constraint.expr = expr_text constraint.localfinalexpr = expr_text constraint.finalexpr = expr_text constraint.args = args or None