def _merge_items( item: qltracer.Source_T, parent: qltracer.SourceLike_T, *, schema: s_schema.Schema, ) -> qltracer.Source_T: item_ptrs = dict(item.get_pointers(schema).items(schema)) for pn, ptr in parent.get_pointers(schema).items(schema): if not isinstance(ptr, (qltracer.Pointer, s_sources.Source)): continue if pn not in item_ptrs: ptr_copy = qltracer.Pointer( s_name.QualName('__', pn.name), source=ptr.get_source(schema), target=ptr.get_target(schema), ) ptr_copy.pointers = dict( ptr.get_pointers(schema).items(schema)) item.pointers[pn] = ptr_copy else: item_ptr = item.getptr(schema, pn) assert isinstance(item_ptr, (qltracer.Pointer, s_sources.Source)) ptr_copy = qltracer.Pointer( s_name.QualName('__', pn.name), source=item, target=item_ptr.get_target(schema), ) ptr_copy.pointers = dict( item_ptr.get_pointers(schema).items(schema)) item.pointers[pn] = _merge_items(ptr_copy, ptr, schema=schema) return item
def _trace_item_layout(node: qlast.CreateObject, *, obj=None, fq_name=None, ctx: LayoutTraceContext): if obj is None: fq_name = ctx.get_local_name(node.name) obj = ctx.objects[fq_name] if hasattr(node, "bases"): bases = [] # construct the parents set, used later in ancestors graph parents = set() for ref in _get_bases(node, ctx=ctx): bases.append(ref) # ignore std modules dependencies if ref.module not in s_schema.STD_MODULES: parents.add(ref) if (ref.module not in ctx.local_modules and ref not in ctx.inh_graph): base = ctx.schema.get(ref) base_obj = type(obj)(name=ref) for pn, p in base.get_pointers(ctx.schema).items(ctx.schema): base_obj.pointers[pn] = qltracer.Pointer( pn, source=base, target=p.get_target(ctx.schema), ) ctx.inh_graph[ref] = { "item": base_obj, } ctx.parents[fq_name] = parents ctx.inh_graph[fq_name] = { "item": obj, "deps": bases, "merge": bases, } for decl in node.commands: if isinstance(decl, qlast.CreateConcretePointer): if isinstance(decl.target, qlast.TypeExpr): target = _resolve_type_expr(decl.target, ctx=ctx) else: target = None ptr = qltracer.Pointer(decl.name.name, source=obj, target=target) obj.pointers[decl.name.name] = ptr ptr_name = f'{fq_name}@{decl.name.name}' ctx.objects[ptr_name] = ptr ctx.defdeps[fq_name].add(ptr_name) _trace_item_layout(decl, obj=ptr, fq_name=ptr_name, ctx=ctx)
def _merge_items(item, parent): for pn, ptr in parent.pointers.items(): if pn not in item.pointers: ptr_copy = qltracer.Pointer( pn, source=ptr.source, target=ptr.target) ptr_copy.pointers = dict(ptr.pointers) item.pointers[pn] = ptr_copy else: ptr_copy = qltracer.Pointer( pn, source=item, target=item.pointers[pn].target) ptr_copy.pointers = dict(item.pointers[pn].pointers) item.pointers[pn] = _merge_items(ptr_copy, ptr) return item
def _trace_item_layout(node: qlast.CreateObject, *, obj=None, fq_name=None, ctx: LayoutTraceContext): if obj is None: fq_name = f'{ctx.module}::{node.name.name}' obj = ctx.objects[fq_name] if hasattr(node, "bases"): bases = [] for ref in _get_bases(node, ctx=ctx): bases.append(ref) if (ref.module not in ctx.local_modules and ref not in ctx.inh_graph): base = ctx.schema.get(ref) base_obj = type(obj)(name=ref) for pn, p in base.get_pointers(ctx.schema).items(ctx.schema): base_obj.pointers[pn] = qltracer.Pointer( pn, source=base, target=p.get_target(ctx.schema), ) ctx.inh_graph[ref] = { "item": base_obj, } ctx.inh_graph[fq_name] = { "item": obj, "deps": bases, "merge": bases, } for decl in node.commands: if isinstance(decl, qlast.CreateConcretePointer): if isinstance(decl.target, qlast.TypeExpr): target = _resolve_type_expr(decl.target, ctx=ctx) else: target = None ptr = qltracer.Pointer(decl.name.name, source=obj, target=target) obj.pointers[decl.name.name] = ptr ptr_name = f'{fq_name}@{decl.name.name}' ctx.objects[ptr_name] = ptr _trace_item_layout(decl, obj=ptr, fq_name=ptr_name, ctx=ctx)
def sdl_to_ddl(schema, documents): ddlgraph = {} mods = [] ctx = LayoutTraceContext( schema, local_modules=frozenset(mod for mod, schema_decl in documents.items()), ) for module_name, declarations in documents.items(): ctx.set_module(module_name) for decl_ast in declarations: if isinstance(decl_ast, qlast.CreateObject): _, fq_name = ctx.get_fq_name(decl_ast) if isinstance(decl_ast, (qlast.CreateObjectType, qlast.CreateAlias)): ctx.objects[fq_name] = qltracer.ObjectType(fq_name) elif isinstance(decl_ast, qlast.CreateScalarType): ctx.objects[fq_name] = qltracer.Type(fq_name) elif isinstance(decl_ast, (qlast.CreateLink, qlast.CreateProperty)): ctx.objects[fq_name] = qltracer.Pointer(fq_name, source=None, target=None) elif isinstance(decl_ast, qlast.CreateFunction): ctx.objects[fq_name] = qltracer.Function(fq_name) elif isinstance(decl_ast, qlast.CreateConstraint): ctx.objects[fq_name] = qltracer.Constraint(fq_name) elif isinstance(decl_ast, qlast.CreateAnnotation): ctx.objects[fq_name] = qltracer.Annotation(fq_name) else: raise AssertionError( f'unexpected SDL declaration: {decl_ast}') for module_name, declarations in documents.items(): ctx.set_module(module_name) for decl_ast in declarations: trace_layout(decl_ast, ctx=ctx) # compute the ancestors graph for fq_name in ctx.parents.keys(): ctx.ancestors[fq_name] = get_ancestors(fq_name, ctx.ancestors, ctx.parents) topological.normalize(ctx.inh_graph, _merge_items) ctx = DepTraceContext(schema, ddlgraph, ctx.objects, ctx.parents, ctx.ancestors, ctx.defdeps, ctx.constraints) for module_name, declarations in documents.items(): ctx.set_module(module_name) # module needs to be created regardless of whether its # contents are empty or not mods.append(qlast.CreateModule(name=qlast.ObjectRef(name=module_name))) for decl_ast in declarations: trace_dependencies(decl_ast, ctx=ctx) return mods + list(topological.sort(ddlgraph, allow_unresolved=False))
def sdl_to_ddl(schema, declarations): ddlgraph = {} mods = [] ctx = LayoutTraceContext( schema, local_modules=frozenset(mod for mod, schema_decl in declarations), ) for module_name, schema_ast in declarations: for decl_ast in schema_ast.declarations: if isinstance(decl_ast, qlast.CreateObject): fq_name = f'{module_name}::{decl_ast.name.name}' if isinstance(decl_ast, (qlast.CreateObjectType, qlast.CreateView)): ctx.objects[fq_name] = qltracer.ObjectType(fq_name) elif isinstance(decl_ast, qlast.CreateScalarType): ctx.objects[fq_name] = qltracer.Type(fq_name) elif isinstance(decl_ast, (qlast.CreateLink, qlast.CreateProperty)): ctx.objects[fq_name] = qltracer.Pointer(fq_name, source=None, target=None) for module_name, decl_ast in declarations: ctx.set_module(module_name) trace_layout(decl_ast, ctx=ctx) topological.normalize(ctx.inh_graph, _merge_items) ctx = DepTraceContext(schema, ddlgraph, ctx.objects) for module_name, decl_ast in declarations: ctx.set_module(module_name) trace_dependencies(decl_ast, ctx=ctx) mods.append(qlast.CreateModule(name=qlast.ObjectRef(name=module_name))) return mods + list(topological.sort(ddlgraph, allow_unresolved=False))
def _trace_item_layout( node: qlast.CreateObject, *, obj: Optional[qltracer.NamedObject] = None, fq_name: Optional[s_name.QualName] = None, ctx: LayoutTraceContext, ) -> None: if obj is None: fq_name = ctx.get_local_name(node.name) local_obj = ctx.objects[fq_name] assert isinstance(local_obj, qltracer.NamedObject) obj = local_obj assert fq_name is not None if isinstance(node, qlast.BasesMixin): bases = [] # construct the parents set, used later in ancestors graph parents = set() for ref in _get_bases(node, ctx=ctx): bases.append(ref) # ignore std modules dependencies if ref.get_module_name() not in s_schema.STD_MODULES: parents.add(ref) if ( ref.module not in ctx.local_modules and ref not in ctx.inh_graph ): base_obj = type(obj)(name=ref) ctx.inh_graph[ref] = topological.DepGraphEntry(item=base_obj) base = ctx.schema.get(ref) if isinstance(base, s_sources.Source): assert isinstance(base_obj, qltracer.Source) base_pointers = base.get_pointers(ctx.schema) for pn, p in base_pointers.items(ctx.schema): base_obj.pointers[pn] = qltracer.Pointer( s_name.QualName('__', pn.name), source=base, target=p.get_target(ctx.schema), ) ctx.parents[fq_name] = parents ctx.inh_graph[fq_name] = topological.DepGraphEntry( item=obj, deps=set(bases), merge=set(bases), ) for decl in node.commands: if isinstance(decl, qlast.CreateConcretePointer): assert isinstance(obj, qltracer.Source) target: Optional[qltracer.TypeLike] if isinstance(decl.target, qlast.TypeExpr): target = _resolve_type_expr(decl.target, ctx=ctx) else: target = None pn = s_utils.ast_ref_to_unqualname(decl.name) ptr = qltracer.Pointer( s_name.QualName('__', pn.name), source=obj, target=target, ) obj.pointers[pn] = ptr ptr_name = s_name.QualName( module=fq_name.module, name=f'{fq_name.name}@{decl.name.name}', ) ctx.objects[ptr_name] = ptr ctx.defdeps[fq_name].add(ptr_name) _trace_item_layout( decl, obj=ptr, fq_name=ptr_name, ctx=ctx) elif isinstance(decl, qlast.CreateConcreteConstraint): # Validate that the constraint exists at all. _validate_schema_ref(decl, ctx=ctx) _, con_fq_name = ctx.get_fq_name(decl) con_name = s_name.QualName( module=fq_name.module, name=f'{fq_name.name}@{con_fq_name}', ) ctx.objects[con_name] = qltracer.ConcreteConstraint(con_name) ctx.constraints[fq_name].add(con_name) elif isinstance(decl, qlast.CreateAnnotationValue): # Validate that the constraint exists at all. _validate_schema_ref(decl, ctx=ctx)
def sdl_to_ddl( schema: s_schema.Schema, documents: Mapping[str, List[qlast.DDL]], ) -> Tuple[qlast.DDLCommand, ...]: ddlgraph: DDLGraph = {} mods: List[qlast.DDLCommand] = [] ctx = LayoutTraceContext( schema, local_modules=frozenset(mod for mod in documents), ) for module_name, declarations in documents.items(): ctx.set_module(module_name) for decl_ast in declarations: if isinstance(decl_ast, qlast.CreateObject): _, fq_name = ctx.get_fq_name(decl_ast) if isinstance(decl_ast, (qlast.CreateObjectType, qlast.CreateAlias)): ctx.objects[fq_name] = qltracer.ObjectType(fq_name) elif isinstance(decl_ast, qlast.CreateScalarType): ctx.objects[fq_name] = qltracer.Type(fq_name) elif isinstance(decl_ast, (qlast.CreateLink, qlast.CreateProperty)): ctx.objects[fq_name] = qltracer.Pointer( fq_name, source=None, target=None) elif isinstance(decl_ast, qlast.CreateFunction): ctx.objects[fq_name] = qltracer.Function(fq_name) elif isinstance(decl_ast, qlast.CreateConstraint): ctx.objects[fq_name] = qltracer.Constraint(fq_name) elif isinstance(decl_ast, qlast.CreateAnnotation): ctx.objects[fq_name] = qltracer.Annotation(fq_name) else: raise AssertionError( f'unexpected SDL declaration: {decl_ast}') for module_name, declarations in documents.items(): ctx.set_module(module_name) for decl_ast in declarations: trace_layout(decl_ast, ctx=ctx) # compute the ancestors graph for obj_name in ctx.parents.keys(): ctx.ancestors[obj_name] = get_ancestors( obj_name, ctx.ancestors, ctx.parents) topological.normalize( ctx.inh_graph, merger=_graph_merge_cb, # type: ignore schema=schema, ) tracectx = DepTraceContext( schema, ddlgraph, ctx.objects, ctx.parents, ctx.ancestors, ctx.defdeps, ctx.constraints ) for module_name, declarations in documents.items(): tracectx.set_module(module_name) # module needs to be created regardless of whether its # contents are empty or not mods.append(qlast.CreateModule(name=qlast.ObjectRef(name=module_name))) for decl_ast in declarations: trace_dependencies(decl_ast, ctx=tracectx) ordered = topological.sort(ddlgraph, allow_unresolved=False) return tuple(mods) + tuple(ordered)