def _get_hard_deps( expr: qlast.TypeExpr, *, ctx: DepTraceContext ) -> MutableSet[s_name.QualName]: deps: MutableSet[s_name.QualName] = set() # If we have any type ops, get a flat list of their operands. targets = qlast.get_targets(expr) for target in targets: # We care about subtypes dependencies, because # they can either be custom scalars or illegal # ObjectTypes (then error message will depend on # dependency tracing) if target.subtypes: for subtype in target.subtypes: # Recurse! deps |= _get_hard_deps(subtype, ctx=ctx) else: # Base case. name = ctx.get_ref_name(target.maintype) if name.get_module_name() not in s_schema.STD_MODULES: deps.add(name) return deps
def _process_create_or_alter_ast(self, schema, astnode, context): """Handle the CREATE {PROPERTY|LINK} AST node. This may be called in the context of either Create or Alter. """ if astnode.is_required is not None: self.set_attribute_value('required', astnode.is_required) if astnode.cardinality is not None: self.set_attribute_value('cardinality', astnode.cardinality) parent_ctx = self.get_referrer_context(context) source_name = parent_ctx.op.classname self.set_attribute_value('source', so.ObjectRef(name=source_name)) # FIXME: this is an approximate solution targets = qlast.get_targets(astnode.target) if len(targets) > 1: new_targets = [ utils.ast_to_typeref(t, modaliases=context.modaliases, schema=schema, metaclass=s_types.Type) for t in targets ] target_ref = s_types.UnionTypeRef(new_targets, module=source_name.module) elif targets: target_expr = targets[0] if isinstance(target_expr, qlast.TypeName): target_ref = utils.ast_to_typeref( target_expr, modaliases=context.modaliases, schema=schema, metaclass=s_types.Type) else: # computable target_ref = ComputableRef( s_expr.imprint_expr_context( target_expr, context.modaliases, )) else: # Target is inherited. target_ref = None if isinstance(self, sd.CreateObject): self.set_attribute_value('target', target_ref, source_context=astnode.target.context) if self.get_attribute_value('cardinality') is None: self.set_attribute_value('cardinality', qltypes.Cardinality.ONE) if self.get_attribute_value('required') is None: self.set_attribute_value('required', False) elif target_ref is not None: self._set_pointer_type(schema, astnode, context, target_ref)
def _cmd_tree_from_ast(cls, schema, astnode, context): cmd = super()._cmd_tree_from_ast(schema, astnode, context) targets = qlast.get_targets(astnode.type) alter_ptr_ctx = context.get(PointerCommandContext) alter_ptr_op = alter_ptr_ctx.op if len(targets) > 1: new_targets = [ utils.ast_to_typeref( t, modaliases=context.modaliases, schema=schema) for t in targets ] target = alter_ptr_op._create_union_target( schema, context, new_targets, module=cmd.classname.module) target_ref = utils.reduce_to_typeref(schema, target) else: target = targets[0] target_ref = utils.ast_to_typeref( target, modaliases=context.modaliases, schema=schema) target_obj = utils.resolve_typeref(target_ref, schema=schema) if target_obj.is_collection(): sd.ensure_schema_collection( schema, target_obj, alter_ptr_ctx.op, src_context=astnode.type.context, context=context, ) cmd.set_attribute_value('target', target_ref) return cmd
def _cmd_tree_from_ast(cls, schema, astnode, context): cmd = super()._cmd_tree_from_ast(schema, astnode, context) targets = qlast.get_targets(astnode.type) if len(targets) > 1: new_targets = [ utils.ast_to_type_shell( t, modaliases=context.modaliases, schema=schema, ) for t in targets ] target_ref = s_types.UnionTypeShell( new_targets, module=cls.classname.module, ) else: target = targets[0] target_ref = utils.ast_to_type_shell( target, modaliases=context.modaliases, schema=schema, ) cmd.set_attribute_value('target', target_ref) return cmd
def _cmd_tree_from_ast( cls, schema: s_schema.Schema, astnode: qlast.DDLOperation, context: sd.CommandContext, ) -> sd.Command: assert isinstance(astnode, qlast.SetPointerType) cmd = super()._cmd_tree_from_ast(schema, astnode, context) targets = qlast.get_targets(astnode.type) target_ref: s_types.TypeShell if len(targets) > 1: new_targets = [ utils.ast_to_type_shell( t, modaliases=context.modaliases, schema=schema, ) for t in targets ] target_ref = s_types.UnionTypeShell( new_targets, module=cls.classname.module, ) else: target = targets[0] target_ref = utils.ast_to_type_shell( target, modaliases=context.modaliases, schema=schema, ) if isinstance(target_ref, s_types.CollectionTypeShell): s_types.ensure_schema_collection( schema, target_ref, parent_cmd=cmd, src_context=astnode.type.context, context=context, ) ctx = context.current() assert isinstance(ctx, sd.ObjectCommandContext) ptr = ctx.op.get_object(schema, context, default=None) # type: ignore if ptr is not None: orig_type = ptr.get_target(schema) if orig_type.is_collection(): s_types.cleanup_schema_collection(schema, orig_type, ptr, cmd, context=context, src_context=astnode.context) cmd.set_attribute_value('target', target_ref) return cmd
def reduce_CreateRegularLink(self, *kids): """%reduce LINK ShortNodeName OptExtendingSimple ARROW FullTypeExpr CreateConcreteLinkSDLCommandsBlock """ self.val = qlast.Link(name=kids[1].val.name, extends=kids[2].val, target=qlast.get_targets(kids[4].val), **_process_commands(kids[5].val))
def reduce_CreateRegularProperty(self, *kids): """%reduce PROPERTY ShortNodeName OptExtendingSimple ARROW FullTypeExpr """ self.val = qlast.Property( name=kids[1].val.name, extends=kids[2].val, target=qlast.get_targets(kids[4].val), )
def reduce_CreateRegularLink(self, *kids): """%reduce LINK ShortNodeName OptExtendingSimple ARROW FullTypeExpr """ self.val = qlast.Link( name=kids[1].val.name, extends=kids[2].val, target=qlast.get_targets(kids[4].val), )
def reduce_CreateQualifiedRegularLink(self, *kids): """%reduce PtrQuals LINK ShortNodeName OptExtendingSimple ARROW FullTypeExpr CreateConcreteLinkSDLCommandsBlock """ self.val = qlast.Link(inherited=kids[0].val.inherited, required=kids[0].val.required, cardinality=kids[0].val.cardinality, name=kids[2].val.name, extends=kids[3].val, target=qlast.get_targets(kids[5].val), **_process_commands(kids[6].val))
def reduce_CreateQualifiedRegularProperty(self, *kids): """%reduce PtrQuals PROPERTY ShortNodeName OptExtendingSimple ARROW FullTypeExpr """ self.val = qlast.Property( name=kids[2].val.name, extends=kids[3].val, inherited=kids[0].val.inherited, required=kids[0].val.required, cardinality=kids[0].val.cardinality, target=qlast.get_targets(kids[5].val), )
def _cmd_tree_from_ast(cls, schema, astnode, context): cmd = super()._cmd_tree_from_ast(schema, astnode, context) targets = qlast.get_targets(astnode.target) if len(targets) > 1: alter_ptr_ctx = context.get(pointers.PointerCommandContext) new_targets = [ utils.ast_to_typeref(t, modaliases=context.modaliases, schema=schema) for t in targets ] alter_ptr_ctx.op.add( sd.AlterObjectProperty( property='spectargets', new_value=so.ObjectList.create(schema, new_targets), )) target = cls._create_union_target(schema, context, new_targets, module=cmd.classname.module) else: target = targets[0] target_ref = utils.ast_to_typeref(target, modaliases=context.modaliases, schema=schema) target_obj = utils.resolve_typeref(target_ref, schema=schema) if target_obj.is_collection(): sd.ensure_schema_collection( schema, target_obj, alter_ptr_ctx.op, src_context=astnode.target.context, context=context, ) cmd.new_value = target_ref return cmd
def _cmd_tree_from_ast( cls, schema: s_schema.Schema, astnode: qlast.DDLOperation, context: sd.CommandContext, ) -> sd.Command: assert isinstance(astnode, qlast.SetPointerType) cmd = super()._cmd_tree_from_ast(schema, astnode, context) targets = qlast.get_targets(astnode.type) target_ref: s_types.TypeShell if len(targets) > 1: new_targets = [ utils.ast_to_type_shell( t, modaliases=context.modaliases, schema=schema, ) for t in targets ] target_ref = s_types.UnionTypeShell( new_targets, module=cls.classname.module, ) else: target = targets[0] target_ref = utils.ast_to_type_shell( target, modaliases=context.modaliases, schema=schema, ) cmd.set_attribute_value('target', target_ref) return cmd
def _process_create_or_alter_ast( self, schema: s_schema.Schema, astnode: qlast.CreateConcretePointer, context: sd.CommandContext, ) -> None: """Handle the CREATE {PROPERTY|LINK} AST node. This may be called in the context of either Create or Alter. """ if astnode.is_required is not None: self.set_attribute_value('required', astnode.is_required) if astnode.cardinality is not None: self.set_attribute_value('cardinality', astnode.cardinality) parent_ctx = self.get_referrer_context_or_die(context) source_name = parent_ctx.op.classname self.set_attribute_value('source', so.ObjectShell(name=source_name)) # FIXME: this is an approximate solution targets = qlast.get_targets(astnode.target) target_ref: Union[None, s_types.TypeShell, ComputableRef] if len(targets) > 1: assert isinstance(source_name, sn.Name) new_targets = [ utils.ast_to_type_shell( t, modaliases=context.modaliases, schema=schema, ) for t in targets ] target_ref = s_types.UnionTypeShell( new_targets, module=source_name.module, ) elif targets: target_expr = targets[0] if isinstance(target_expr, qlast.TypeName): target_ref = utils.ast_to_type_shell( target_expr, modaliases=context.modaliases, schema=schema, ) else: # computable target_ref = ComputableRef( s_expr.imprint_expr_context( target_expr, context.modaliases, )) else: # Target is inherited. target_ref = None if isinstance(target_ref, s_types.CollectionTypeShell): assert astnode.target is not None s_types.ensure_schema_collection( schema, target_ref, parent_cmd=self, src_context=astnode.target.context, context=context, ) if isinstance(self, sd.CreateObject): assert astnode.target is not None self.set_attribute_value( 'target', target_ref, source_context=astnode.target.context, ) # If target is a computable ref defer cardinality # enforcement until the expression is parsed. if not isinstance(target_ref, ComputableRef): if self.get_attribute_value('cardinality') is None: self.set_attribute_value('cardinality', qltypes.SchemaCardinality.ONE) if self.get_attribute_value('required') is None: self.set_attribute_value('required', False) elif target_ref is not None: self._set_pointer_type(schema, astnode, context, target_ref)
def _process_link_create_or_alter(cls, schema, astnode, context, cmd): from . import objtypes as s_objtypes parent_ctx = context.get(LinkSourceCommandContext) if isinstance(astnode, qlast.CreateConcreteLink): # "source" attribute is set automatically as a refdict back-attr source_name = parent_ctx.op.classname 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) # FIXME: this is an approximate solution targets = qlast.get_targets(astnode.target) if len(targets) > 1: new_targets = [ utils.ast_to_typeref( t, modaliases=context.modaliases, schema=schema) for t in targets ] target = cls._create_union_target( schema, context, new_targets, module=source_name.module) else: target_expr = targets[0] if isinstance(target_expr, qlast.TypeName): target = utils.ast_to_typeref( target_expr, modaliases=context.modaliases, schema=schema) else: # computable target, base = cmd._parse_computable( target_expr, 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 ) if (isinstance(target, so.ObjectRef) and target.name == 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: target_type = utils.resolve_typeref(target, schema=schema) if not isinstance(target_type, s_objtypes.ObjectType): raise errors.InvalidLinkTargetError( f'invalid link target, expected object type, got ' f'{target_type.__class__.__name__}', context=astnode.target.context ) if isinstance(cmd, sd.CreateObject): cmd.set_attribute_value('target', target) 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 = SetLinkType(classname=cmd.classname, type=target) slt.set_attribute_value('target', target) cmd.add(slt) cls._parse_default(cmd) if parent_ctx is None: # this is an abstract link then if cmd.get_attribute_value('default') is not None: raise errors.SchemaDefinitionError( f"'default' is not a valid field for an abstact link", context=astnode.context)
def _cmd_tree_from_ast(cls, schema, astnode, context): from . import objtypes as s_objtypes cmd = super()._cmd_tree_from_ast(schema, astnode, context) parent_ctx = context.get(LinkSourceCommandContext) source_name = parent_ctx.op.classname alter_ptr_ctx = context.get(pointers.PointerCommandContext) targets = qlast.get_targets(astnode.target) if len(targets) > 1: alter_ptr_ctx.op.add( sd.AlterObjectProperty( property='spectargets', new_value=so.ObjectList([ so.ObjectRef( name=sn.Name(module=t.module, name=t.name)) for t in targets ]))) target_name = sources.Source.gen_virt_parent_name( (sn.Name(module=t.module, name=t.name) for t in targets), module=source_name.module) target_ref = so.ObjectRef(name=target_name) create_virt_parent = s_objtypes.CreateObjectType( classname=target_name, metaclass=s_objtypes.ObjectType) create_virt_parent.update( (sd.AlterObjectProperty(property='name', new_value=target_name), sd.AlterObjectProperty(property='is_virtual', new_value=True), sd.AlterObjectProperty(property='is_derived', new_value=True))) delta_ctx = context.get(sd.DeltaRootContext) for cc in delta_ctx.op(s_objtypes.CreateObjectType): if cc.classname == create_virt_parent.classname: break else: delta_ctx.op.add(create_virt_parent) else: target = targets[0] target_ref = utils.ast_to_typeref(target, modaliases=context.modaliases, schema=schema) target_obj = utils.resolve_typeref(target_ref, schema=schema) if target_obj.is_collection(): sd.ensure_schema_collection( schema, target_obj, alter_ptr_ctx.op, src_context=astnode.target.context, context=context, ) cmd.new_value = target_ref return cmd
def _register_item(decl, *, deps=None, hard_dep_exprs=None, loop_control=None, source=None, subject=None, extra_name=None, ctx): if deps: deps = set(deps) else: deps = set() op = orig_op = copy.copy(decl) if isinstance(decl, qlast.CreateConcretePointer): name = decl.name.name else: name = ctx.get_local_name(decl.name) if ctx.depstack: op.alter_if_exists = True top_parent = parent = copy.copy(ctx.depstack[0][0]) parent.commands = [] for entry, _entry_name in ctx.depstack[1:]: entry_op = copy.copy(entry) entry_op.commands = [] parent.commands.append(entry_op) parent = entry_op parent.commands.append(op) op = top_parent fq_name = ctx.depstack[-1][1] + "@" + name else: op.aliases = [qlast.ModuleAliasDecl(alias=None, module=ctx.module)] fq_name = name if extra_name is not None: fq_name = f'{fq_name}:{extra_name}' node = { "item": op, "deps": {n for _, n in ctx.depstack if n != loop_control}, } ctx.ddlgraph[fq_name] = node if hasattr(decl, "bases"): bases = set() for ref in _get_bases(decl, ctx=ctx): if ref.module == ctx.module: bases.add(ref) deps.update(bases) ctx.inhgraph[fq_name] = bases if ctx.depstack: parent_bases = ctx.inhgraph.get(ctx.depstack[-1][1]) if parent_bases: for parent_base in parent_bases: base_item = f'{parent_base}@{name}' if base_item in ctx.objects: deps.add(base_item) ast_subcommands = getattr(decl, 'commands', []) commands = [] if ast_subcommands: subcmds = [] for cmd in ast_subcommands: if isinstance(cmd, qlast.ObjectDDL): subcmds.append(cmd) elif (isinstance(cmd, qlast.SetField) and not isinstance(cmd.value, qlast.BaseConstant) and not isinstance(op, qlast.CreateView)): subcmds.append(cmd) else: commands.append(cmd) if subcmds: alter_name = f"Alter{decl.__class__.__name__[len('Create'):]}" alter_cls = getattr(qlast, alter_name) alter_cmd = alter_cls(name=decl.name) # indexes need to preserve their "on" expression if alter_name == 'AlterIndex': # find the original expr, which will be in non-normalized form for sub in op.commands: if isinstance(sub, qlast.CreateIndex): alter_cmd.expr = sub.expr break # constraints need to preserve their "on" expression elif alter_name == 'AlterConcreteConstraint': # find the original expr, which will be in non-normalized form for sub in op.commands: if isinstance(sub, qlast.CreateConcreteConstraint): alter_cmd.subjectexpr = sub.subjectexpr break if not ctx.depstack: alter_cmd.aliases = [ qlast.ModuleAliasDecl(alias=None, module=ctx.module) ] ctx.depstack.append((alter_cmd, fq_name)) for cmd in subcmds: trace_dependencies(cmd, ctx=ctx) ctx.depstack.pop() if hard_dep_exprs: for expr in hard_dep_exprs: if isinstance(expr, qlast.TypeExpr): targets = qlast.get_targets(expr) for target in targets: if not target.subtypes: name = ctx.get_ref_name(target.maintype) if name.module == ctx.module: deps.add(name) else: for dep in qltracer.trace_refs( expr, schema=ctx.schema, module=ctx.module, source=source, path_prefix=source, subject=subject or fq_name, objects=ctx.objects, ): if dep.startswith(f"{ctx.module}::"): deps.add(dep) orig_op.commands = commands if loop_control: parent_node = ctx.ddlgraph[loop_control] if 'loop-control' not in parent_node: parent_node['loop-control'] = {fq_name} else: parent_node['loop-control'].add(fq_name) node["deps"].update(deps)
def _cmd_tree_from_ast(cls, schema, astnode, context): from . import objtypes as s_objtypes cmd = super()._cmd_tree_from_ast(schema, astnode, context) if isinstance(astnode, qlast.CreateConcreteLink): cmd.add( sd.AlterObjectProperty(property='required', new_value=astnode.is_required)) cmd.add( sd.AlterObjectProperty(property='cardinality', new_value=astnode.cardinality)) # "source" attribute is set automatically as a refdict back-attr parent_ctx = context.get(LinkSourceCommandContext) source_name = parent_ctx.op.classname target_type = None # FIXME: this is an approximate solution targets = qlast.get_targets(astnode.target) if len(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 targets ]))) target_name = sources.Source.gen_virt_parent_name( (sn.Name(module=t.maintype.module, name=t.maintype.name) for t in targets), module=source_name.module) target = so.ObjectRef(name=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( name=sn.Name(module='std', name='Object')) ])), sd.AlterObjectProperty(property='name', new_value=target_name), sd.AlterObjectProperty(property='is_virtual', new_value=True))) delta_ctx = context.get(sd.DeltaRootContext) for cc in delta_ctx.op.get_subcommands( type=s_objtypes.CreateObjectType): if cc.classname == create_virt_parent.classname: break else: delta_ctx.op.add(create_virt_parent) else: target_expr = targets[0] if isinstance(target_expr, qlast.TypeName): target = utils.ast_to_typeref( target_expr, modaliases=context.modaliases, schema=schema) else: # computable target = cmd._parse_computable(target_expr, schema, context) if (isinstance(target, so.ObjectRef) and target.name == 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 errors.InvalidLinkTargetError( f'invalid link target, expected object type, got ' f'{target_type.__class__.__name__}', context=astnode.target.context) cmd.add(sd.AlterObjectProperty(property='target', new_value=target)) base_prop_name = sn.Name('std::source') s_name = sn.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(name=base_prop_name)]), sd.AlterObjectProperty( property='source', new_value=so.ObjectRef(name=cmd.classname)), sd.AlterObjectProperty( property='target', new_value=so.ObjectRef(name=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 = sn.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(name=base_prop_name)]), sd.AlterObjectProperty( property='source', new_value=so.ObjectRef(name=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) else: # this is an abstract link then if cmd.get_attribute_value('default') is not None: raise errors.SchemaDefinitionError( f"'default' is not a valid field for an abstact link", context=astnode.context) return cmd