def _validate_pointer_def( self, schema: s_schema.Schema, context: sd.CommandContext, ) -> None: """Check that property definition is sound.""" super()._validate_pointer_def(schema, context) scls = self.scls if not scls.get_owned(schema): return if scls.is_special_pointer(schema): return if ( scls.is_link_property(schema) and not scls.is_pure_computable(schema) ): # link properties cannot be required or multi if self.get_attribute_value('required'): raise errors.InvalidPropertyDefinitionError( 'link properties cannot be required', context=self.source_context, ) if (self.get_attribute_value('cardinality') is qltypes.SchemaCardinality.Many): raise errors.InvalidPropertyDefinitionError( "multi properties aren't supported for links", context=self.source_context, ) target_type = scls.get_target(schema) if target_type is None: raise TypeError(f'missing target type in scls {scls}') if target_type.is_polymorphic(schema): srcctx = self.get_attribute_source_context('target') raise errors.InvalidPropertyTargetError( f'invalid property type: ' f'{target_type.get_verbosename(schema)} ' f'is a generic type', context=srcctx, ) if (target_type.is_object_type() or (isinstance(target_type, s_types.Collection) and target_type.contains_object(schema))): srcctx = self.get_attribute_source_context('target') raise errors.InvalidPropertyTargetError( f'invalid property type: expected a scalar type, ' f'or a scalar collection, got ' f'{target_type.get_verbosename(schema)}', context=srcctx, )
def _validate_pointer_def(self, schema, context): """Check that property definition is sound.""" super()._validate_pointer_def(schema, context) scls = self.scls if not scls.get_is_local(schema): return if scls.is_special_pointer(schema): return if scls.is_link_property(schema): # link properties cannot be required or multi if self.get_attribute_value('required'): raise errors.InvalidPropertyDefinitionError( 'link properties cannot be required', context=self.source_context, ) if (self.get_attribute_value('cardinality') is qltypes.Cardinality.MANY): raise errors.InvalidPropertyDefinitionError( "multi properties aren't supported for links", context=self.source_context, ) target_type = scls.get_target(schema) if target_type.is_polymorphic(schema): srcctx = self.get_attribute_source_context('target') raise errors.InvalidPropertyTargetError( f'invalid property type: ' f'{target_type.get_verbosename(schema)} ' f'is a generic type', context=srcctx, ) if (target_type.is_object_type() or (target_type.is_collection() and target_type.contains_object(schema))): srcctx = self.get_attribute_source_context('target') raise errors.InvalidPropertyTargetError( f'invalid property type: expected a scalar type, ' f'or a scalar collection, got ' f'{target_type.get_verbosename(schema)}', context=srcctx, ) if target_type.is_collection(): srcctx = self.get_attribute_source_context('target') sd.ensure_schema_collection( schema, target_type, self, src_context=srcctx, context=context, )
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 _cmd_tree_from_ast(cls, schema, astnode, context): cmd = super()._cmd_tree_from_ast(schema, astnode, context) modaliases = context.modaliases if isinstance(astnode, qlast.CreateConcreteProperty): target = getattr(astnode, 'target', None) cmd.add( sd.AlterObjectProperty(property='required', new_value=astnode.is_required)) cmd.add( sd.AlterObjectProperty(property='cardinality', new_value=astnode.cardinality)) parent_ctx = context.get(PropertySourceContext) source_name = parent_ctx.op.classname cmd.add( sd.AlterObjectProperty( property='source', new_value=so.ObjectRef(name=source_name))) if isinstance(target, qlast.TypeName): target_ref = utils.ast_to_typeref(target, modaliases=modaliases, schema=schema) else: # computable target_ref = cmd._parse_computable(target, schema, context) target_type = utils.resolve_typeref(target_ref, schema=schema) if (not isinstance(target_type, (scalars.ScalarType, types.Collection)) or target_type.is_polymorphic(schema)): raise errors.InvalidPropertyTargetError( f'invalid property target, expected primitive type, ' f'got {target_type.get_displayname(schema)!r}', context=target.context) cmd.add( sd.AlterObjectProperty( property='target', new_value=target_ref, )) cls._parse_default(cmd) return cmd
def _cmd_tree_from_ast(cls, schema, astnode, context): cmd = super()._cmd_tree_from_ast(schema, astnode, context) modaliases = context.modaliases if isinstance(astnode, qlast.CreateConcreteProperty): target = getattr(astnode, 'target', None) cmd.add( sd.AlterObjectProperty(property='required', new_value=astnode.is_required or False)) cmd.add( sd.AlterObjectProperty(property='cardinality', new_value=astnode.cardinality or qltypes.Cardinality.ONE)) parent_ctx = context.get(PropertySourceContext) source_name = parent_ctx.op.classname cmd.add( sd.AlterObjectProperty( property='source', new_value=so.ObjectRef(name=source_name))) if isinstance(target, qlast.TypeName): target_ref = utils.ast_to_typeref(target, modaliases=modaliases, schema=schema, metaclass=s_types.Type) else: # computable target_ref, base = cmd._parse_computable( target, schema, context) if base is not None: cmd.set_attribute_value( 'bases', so.ObjectList.create(schema, [base]), ) cmd.set_attribute_value('derived_from', base) cmd.set_attribute_value('is_derived', True) if context.declarative: cmd.set_attribute_value('declared_inherited', True) target_type = utils.resolve_typeref(target_ref, schema=schema) if target_type.is_polymorphic(schema): raise errors.InvalidPropertyTargetError( f'invalid property type: ' f'{target_type.get_displayname(schema)!r} ' f'is a generic type', context=target.context) if (target_type.is_object_type() or (target_type.is_collection() and target_type.contains_object(schema))): raise errors.InvalidPropertyTargetError( f'invalid property type: expected a scalar type, ' f'or a scalar collection, got ' f'{target_type.get_displayname(schema)!r}', context=target.context) if target_type.is_collection(): sd.ensure_schema_collection( schema, target_type, cmd, src_context=target.context, context=context, ) cmd.add( sd.AlterObjectProperty( property='target', new_value=target_ref, )) cls._parse_default(cmd) else: # this is an abstract property then if cmd.get_attribute_value('default') is not None: raise errors.SchemaDefinitionError( f"'default' is not a valid field for an abstact property", context=astnode.context) return cmd
def _parse_source_props(self, source, sourcedecl): for propdecl in sourcedecl.properties: prop_name = propdecl.name if len(prop_name) > s_pointers.MAX_NAME_LENGTH: raise errors.SchemaDefinitionError( f'link or property name length exceeds the maximum of ' f'{s_pointers.MAX_NAME_LENGTH} characters', context=propdecl.context) if propdecl.extends: prop_bases = [self._get_ref_type(b) for b in propdecl.extends] else: prop_bases = [ self._schema.get(s_props.Property.get_default_base_name()) ] prop_target = None if propdecl.target is not None: prop_target = self._get_ref_type(propdecl.target[0]) if not isinstance(prop_target, (s_scalars.ScalarType, s_types.Collection)): raise errors.InvalidPropertyTargetError( f'invalid property type: expected primitive type, ' f'got {prop_target.__class__.__name__}', context=propdecl.target[0].context ) new_props = { 'sourcectx': propdecl.context, } name = self._get_derived_ptr_name(prop_name, source) self._schema, prop = prop_bases[0].derive( self._schema, source, prop_target, merge_bases=prop_bases, attrs=new_props, name=name) if propdecl.cardinality is None: if propdecl.expr is None: cardinality = qltypes.Cardinality.ONE else: cardinality = None else: cardinality = propdecl.cardinality self._schema = prop.update(self._schema, { 'declared_inherited': propdecl.inherited, 'required': bool(propdecl.required), 'cardinality': cardinality, }) if propdecl.expr is not None: self._schema = prop.set_field_value( self._schema, 'computable', True) self._schema = source.add_pointer(self._schema, prop) if propdecl.annotations: self._parse_attr_setters(prop, propdecl.annotations) if propdecl.fields: self._parse_field_setters(prop, propdecl.fields) if propdecl.constraints: self._parse_subject_constraints(prop, propdecl)
def _parse_source_props(self, source, sourcedecl): for propdecl in sourcedecl.properties: prop_name = self._get_ref_name(propdecl.name) if len(prop_name) > s_pointers.MAX_NAME_LENGTH: raise errors.SchemaDefinitionError( f'link or property name length exceeds the maximum of ' f'{s_pointers.MAX_NAME_LENGTH} characters', context=propdecl.context) prop_base = self._schema.get(prop_name, type=s_props.Property, default=None, module_aliases=self._mod_aliases) prop_target = None if prop_base is None: if not source.generic(self._schema): # Only generic links can implicitly define properties raise errors.SchemaDefinitionError( f'reference to an undefined property {prop_name!r}') # The link property has not been defined globally. if not s_name.Name.is_qualified(prop_name): # If the name is not fully qualified, assume inline link # property definition. The only attribute that is used for # global definition is the name. prop_qname = s_name.Name(name=prop_name, module=source.get_name( self._schema).module) std_lprop = self._schema.get( s_props.Property.get_default_base_name(), module_aliases=self._mod_aliases) self._schema, prop_base = \ s_props.Property.create_in_schema( self._schema, name=prop_qname, bases=[std_lprop]) else: prop_qname = s_name.Name(prop_name) else: prop_qname = prop_base.get_name(self._schema) if propdecl.target is not None: prop_target = self._get_ref_type(propdecl.target[0]) if not isinstance(prop_target, (s_scalars.ScalarType, s_types.Collection)): raise errors.InvalidPropertyTargetError( f'invalid property target, expected primitive type, ' f'got {prop_target.__class__.__name__}', context=propdecl.target[0].context) elif not source.generic(self._schema): link_base = source.get_bases(self._schema).first(self._schema) propdef = link_base.getptr(self._schema, prop_qname.name) if not propdef: raise errors.SchemaError( 'link {!r} does not define property ' '{!r}'.format(source.get_name(self._schema), prop_qname.name)) prop_qname = propdef.get_shortname(self._schema) new_props = { 'sourcectx': propdecl.context, } self._schema, prop = prop_base.derive(self._schema, source, prop_target, attrs=new_props) self._schema = prop.update( self._schema, { 'declared_inherited': propdecl.inherited, 'required': bool(propdecl.required), 'cardinality': propdecl.cardinality, }) if propdecl.expr is not None: self._schema = prop.set_field_value(self._schema, 'computable', True) self._schema = source.add_pointer(self._schema, prop) if propdecl.attributes: self._parse_attr_setters(prop, propdecl.attributes) if propdecl.fields: self._parse_field_setters(prop, propdecl.fields) if propdecl.constraints: self._parse_subject_constraints(prop, propdecl)
def _process_create_ast(cls, schema, astnode, context, cmd): """Handle the CREATE PROPERTY ast node. This may be called in the context of either Create or Alter. """ if astnode.is_required is not None: cmd.set_attribute_value('required', astnode.is_required) if astnode.cardinality is not None: cmd.set_attribute_value('cardinality', astnode.cardinality) parent_ctx = context.get(PropertySourceContext) source_name = parent_ctx.op.classname cmd.set_attribute_value('source', so.ObjectRef(name=source_name)) target = getattr(astnode, 'target', None) if isinstance(target, qlast.TypeName): target_ref = utils.ast_to_typeref( target, modaliases=context.modaliases, schema=schema, metaclass=s_types.Type) else: # computable target_ref, base = cmd._parse_computable( target, schema, context) if base is not None: cmd.set_attribute_value( 'bases', so.ObjectList.create(schema, [base]), ) cmd.set_attribute_value( 'is_derived', True ) if context.declarative: cmd.set_attribute_value( 'declared_inherited', True ) target_type = utils.resolve_typeref(target_ref, schema=schema) if target_type.is_polymorphic(schema): raise errors.InvalidPropertyTargetError( f'invalid property type: ' f'{target_type.get_displayname(schema)!r} ' f'is a generic type', context=target.context ) if (target_type.is_object_type() or (target_type.is_collection() and target_type.contains_object(schema))): raise errors.InvalidPropertyTargetError( f'invalid property type: expected a scalar type, ' f'or a scalar collection, got ' f'{target_type.get_displayname(schema)!r}', context=target.context ) if target_type.is_collection(): sd.ensure_schema_collection( schema, target_type, cmd, src_context=target.context, context=context, ) if isinstance(cmd, sd.CreateObject): cmd.set_attribute_value('target', target_ref) if cmd.get_attribute_value('cardinality') is None: cmd.set_attribute_value( 'cardinality', qltypes.Cardinality.ONE) if cmd.get_attribute_value('required') is None: cmd.set_attribute_value( 'required', False) else: slt = SetPropertyType(classname=cmd.classname, type=target_ref) slt.set_attribute_value('target', target_ref) cmd.add(slt) cls._parse_default(cmd)