def _normalize_objtype_expressions(self, objtype, typedecl): """Interpret and validate EdgeQL expressions in type declaration.""" for ptrdecl in itertools.chain(typedecl.links, typedecl.properties): link_name = self._get_ref_name(ptrdecl.name) generic_link = self._schema.get(link_name, module_aliases=self._mod_aliases) spec_link = objtype.getptr( self._schema, generic_link.get_name(self._schema).name) if ptrdecl.expr is not None: # Computable self._normalize_ptr_default(ptrdecl.expr, objtype, spec_link, ptrdecl) for attr in ptrdecl.fields: name = attr.name.name if name == 'default': if isinstance(attr.value, edgeql.ast.SelectQuery): self._normalize_ptr_default(attr.value, objtype, spec_link, ptrdecl) else: expr = attr.value _, _, default = qlutils.normalize_tree( expr, self._schema) self._schema = spec_link.set_field_value( self._schema, 'default', default)
def _parse_computable(self, expr, schema, context) -> so.ObjectRef: from edb.lang.edgeql import utils as ql_utils from . import sources as s_sources # "source" attribute is set automatically as a refdict back-attr parent_ctx = context.get(s_sources.SourceCommandContext) source_name = parent_ctx.op.classname source = schema.get(source_name, default=None) if source is None: raise errors.SchemaDefinitionError( f'cannot define link/property computables in CREATE TYPE', hint='Perform a CREATE TYPE without the link ' 'followed by ALTER TYPE defining the computable', context=expr.context) ir, _, target_expr = ql_utils.normalize_tree( expr, schema, anchors={qlast.Source: source}, singletons=[source]) target = utils.reduce_to_typeref(schema, ir.stype) self.add( sd.AlterObjectProperty(property='default', new_value=target_expr)) self.add(sd.AlterObjectProperty(property='computable', new_value=True)) self.add( sd.AlterObjectProperty(property='cardinality', new_value=ir.cardinality)) return target
def _normalize_ptr_default(self, expr, source, ptr, ptrdecl): module_aliases = {None: source.get_name(self._schema).module} ir, _, expr_text = qlutils.normalize_tree( expr, self._schema, modaliases=module_aliases, anchors={qlast.Source: source}, singletons=[source]) expr_type = ir.stype self._schema = ptr.set_field_value(self._schema, 'default', expr_text) if ptr.is_pure_computable(self._schema): # Pure computable without explicit target. # Fixup pointer target and target property. self._schema = ptr.set_field_value(self._schema, 'target', expr_type) if isinstance(ptr, s_links.Link): if not isinstance(expr_type, s_objtypes.ObjectType): raise errors.InvalidLinkTargetError( f'invalid link target, expected object type, got ' f'{expr_type.__class__.__name__}', context=ptrdecl.expr.context) else: if not isinstance(expr_type, (s_scalars.ScalarType, s_types.Collection)): raise errors.InvalidPropertyTargetError( f'invalid property target, expected primitive type, ' f'got {expr_type.__class__.__name__}', context=ptrdecl.expr.context) if isinstance(ptr, s_links.Link): tgt_prop = ptr.getptr(self._schema, 'target') self._schema = tgt_prop.set_field_value( self._schema, 'target', expr_type) self._schema = ptr.set_field_value(self._schema, 'cardinality', ir.cardinality) if ptrdecl.cardinality is not ptr.get_cardinality(self._schema): if ptrdecl.cardinality is qlast.Cardinality.ONE: raise errors.SchemaError( f'computable expression possibly returns more than ' f'one value, but the {ptr.schema_class_displayname!r} ' f'is declared as "single"', context=expr.context) if (not isinstance(expr_type, s_abc.Type) or (ptr.get_target(self._schema) is not None and not expr_type.issubclass( self._schema, ptr.get_target(self._schema)))): raise errors.SchemaError( 'default value query must yield a single result of ' 'type {!r}'.format( ptr.get_target(self._schema).get_name(self._schema)), context=expr.context)
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 _parse_subject_indexes(self, subject, subjdecl): module_aliases = {None: subject.get_name(self._schema).module} for indexdecl in subjdecl.indexes: index_name = self._get_ref_name(indexdecl.name) index_name = subject.get_name(self._schema) + '.' + index_name local_name = s_name.get_specialized_name( index_name, subject.get_name(self._schema)) der_name = s_name.Name(name=local_name, module=subject.get_name( self._schema).module) _, _, index_expr = qlutils.normalize_tree( indexdecl.expression, self._schema, modaliases=module_aliases, anchors={qlast.Subject: subject}, inline_anchors=True) self._schema, index = s_indexes.SourceIndex.create_in_schema( self._schema, name=der_name, expr=index_expr, subject=subject) self._schema = subject.add_index(self._schema, index)
def _cmd_tree_from_ast(cls, astnode, context, schema): from edb.lang.edgeql import utils as ql_utils from edb.lang.ir import ast as irast from edb.lang.ir import inference as ir_inference from edb.lang.ir import utils as ir_utils from . import objtypes as s_objtypes cmd = super()._cmd_tree_from_ast(astnode, context, schema) if isinstance(astnode, qlast.CreateConcreteLink): cmd.add( sd.AlterObjectProperty(property='required', new_value=astnode.is_required)) # "source" attribute is set automatically as a refdict back-attr parent_ctx = context.get(LinkSourceCommandContext) source_name = parent_ctx.op.classname target_type = None if len(astnode.targets) > 1: cmd.add( sd.AlterObjectProperty( property='spectargets', new_value=so.ObjectList([ utils.ast_to_typeref(t, modaliases=context.modaliases, schema=schema) for t in astnode.targets ]))) target_name = sources.Source.gen_virt_parent_name( (sn.Name(module=t.maintype.module, name=t.maintype.name) for t in astnode.targets), module=source_name.module) target = so.ObjectRef(classname=target_name) create_virt_parent = s_objtypes.CreateObjectType( classname=target_name, metaclass=s_objtypes.ObjectType) create_virt_parent.update( (sd.AlterObjectProperty( property='bases', new_value=so.ObjectList([ so.ObjectRef( classname=sn.Name(module='std', name='Object')) ])), sd.AlterObjectProperty(property='name', new_value=target_name), sd.AlterObjectProperty(property='is_virtual', new_value=True))) alter_db_ctx = context.get(s_db.DatabaseCommandContext) for cc in alter_db_ctx.op.get_subcommands( type=s_objtypes.CreateObjectType): if cc.classname == create_virt_parent.classname: break else: alter_db_ctx.op.add(create_virt_parent) else: target_expr = astnode.targets[0] if isinstance(target_expr, qlast.TypeName): target = utils.ast_to_typeref( target_expr, modaliases=context.modaliases, schema=schema) else: # computable source = schema.get(source_name, default=None) if source is None: raise s_err.SchemaDefinitionError( f'cannot define link computables in CREATE TYPE', hint='Perform a CREATE TYPE without the link ' 'followed by ALTER TYPE defining the ' 'computable', context=target_expr.context) ir, _, target_expr = ql_utils.normalize_tree( target_expr, schema, anchors={qlast.Source: source}) try: target_type = ir_utils.infer_type(ir, schema) except edgeql.EdgeQLError as e: raise s_err.SchemaDefinitionError( 'could not determine the result type of ' 'computable expression', context=target_expr.context) from e target = utils.reduce_to_typeref(target_type) cmd.add( sd.AlterObjectProperty(property='default', new_value=target_expr)) cmd.add( sd.AlterObjectProperty(property='computable', new_value=True)) singletons = {irast.PathId(source)} cardinality = ir_inference.infer_cardinality( ir, singletons, schema) if cardinality == qlast.Cardinality.ONE: link_card = pointers.PointerCardinality.ManyToOne else: link_card = pointers.PointerCardinality.ManyToMany cmd.add( sd.AlterObjectProperty(property='cardinality', new_value=link_card)) if (isinstance(target, so.ObjectRef) and target.classname == source_name): # Special case for loop links. Since the target # is the same as the source, we know it's a proper # type. pass else: if target_type is None: target_type = utils.resolve_typeref(target, schema=schema) if not isinstance(target_type, s_objtypes.ObjectType): raise s_err.SchemaDefinitionError( f'invalid link target, expected object type, got ' f'{target_type.__class__.__name__}', context=astnode.targets[0].context) cmd.add(sd.AlterObjectProperty(property='target', new_value=target)) base_prop_name = sn.Name('std::source') s_name = lproperties.Property.get_specialized_name( base_prop_name, cmd.classname) src_prop_name = sn.Name(name=s_name, module=cmd.classname.module) src_prop = lproperties.CreateProperty( classname=src_prop_name, metaclass=lproperties.Property) src_prop.update(( sd.AlterObjectProperty(property='name', new_value=src_prop_name), sd.AlterObjectProperty( property='bases', new_value=[so.ObjectRef(classname=base_prop_name)]), sd.AlterObjectProperty( property='source', new_value=so.ObjectRef(classname=cmd.classname)), sd.AlterObjectProperty( property='target', new_value=so.ObjectRef(classname=source_name)), sd.AlterObjectProperty(property='required', new_value=True), sd.AlterObjectProperty(property='readonly', new_value=True), )) cmd.add(src_prop) base_prop_name = sn.Name('std::target') s_name = lproperties.Property.get_specialized_name( base_prop_name, cmd.classname) tgt_prop_name = sn.Name(name=s_name, module=cmd.classname.module) tgt_prop = lproperties.CreateProperty( classname=tgt_prop_name, metaclass=lproperties.Property) tgt_prop.update(( sd.AlterObjectProperty(property='name', new_value=tgt_prop_name), sd.AlterObjectProperty( property='bases', new_value=[so.ObjectRef(classname=base_prop_name)]), sd.AlterObjectProperty( property='source', new_value=so.ObjectRef(classname=cmd.classname)), sd.AlterObjectProperty(property='target', new_value=target), sd.AlterObjectProperty(property='required', new_value=False), sd.AlterObjectProperty(property='readonly', new_value=True), )) cmd.add(tgt_prop) cls._parse_default(cmd) return cmd