def ptrcls_from_ptrref( ptrref: irast.BasePointerRef, *, schema: s_schema.Schema, ) -> Tuple[s_schema.Schema, s_pointers.PointerLike]: """Return a schema pointer for a given IR PointerRef. This is the reverse of :func:`~type_to_typeref`. Args: schema: A schema instance. The result type must exist in it. ptrref: A :class:`ir.ast.BasePointerRef` instance for which to return the corresponding schema pointer. Returns: A tuple containing the possibly modifed schema and a :class:`schema.pointers.PointerLike` instance corresponding to the given *ptrref*. """ ptrcls: s_pointers.PointerLike if isinstance(ptrref, irast.TupleIndirectionPointerRef): schema, src_t = ir_typeref_to_type(schema, ptrref.out_source) schema, tgt_t = ir_typeref_to_type(schema, ptrref.out_target) ptrcls = irast.TupleIndirectionLink( source=src_t, target=tgt_t, element_name=ptrref.name.name, ) elif isinstance(ptrref, irast.TypeIntersectionPointerRef): target = schema.get_by_id(ptrref.out_target.id) assert isinstance(target, s_types.Type) ptrcls = irast.TypeIntersectionLink( source=schema.get_by_id(ptrref.out_source.id), target=target, optional=ptrref.optional, is_empty=ptrref.is_empty, is_subtype=ptrref.is_subtype, cardinality=ptrref.out_cardinality.to_schema_value()[1], ) elif isinstance(ptrref, irast.PointerRef): ptr = schema.get_by_id(ptrref.id) assert isinstance(ptr, s_pointers.Pointer) ptrcls = ptr else: raise TypeError(f'unexpected pointer ref type: {ptrref!r}') return schema, ptrcls
def float_const_to_python(ir: irast.FloatConstant, schema: s_schema.Schema) -> object: stype = schema.get_by_id(ir.typeref.id) if stype.issubclass(schema, schema.get('std::decimal')): return decimal.Decimal(ir.value) else: return float(ir.value)
def int_const_to_python(ir: irast.IntegerConstant, schema: s_schema.Schema) -> Any: stype = schema.get_by_id(ir.typeref.id) if stype.issubclass(schema, schema.get('std::bigint')): return decimal.Decimal(ir.value) else: return int(ir.value)
def float_const_to_python(ir: irast.FloatConstant, schema: s_schema.Schema) -> Any: stype = schema.get_by_id(ir.typeref.id) assert isinstance(stype, s_types.Type) if stype.issubclass(schema, schema.get('std::decimal')): return decimal.Decimal(ir.value) else: return float(ir.value)
def ptrcls_from_ptrref( ptrref: irast.BasePointerRef, *, schema: s_schema.Schema, ) -> s_pointers.PointerLike: """Return a schema pointer for a given IR PointerRef. This is the reverse of :func:`~type_to_typeref`. Args: schema: A schema instance. The result type must exist in it. ptrref: A :class:`ir.ast.BasePointerRef` instance for which to return the corresponding schema pointer. Returns: A :class:`schema.pointers.PointerLike` instance corresponding to the given *ptrref*. """ ptrcls: s_pointers.PointerLike if isinstance(ptrref, irast.TupleIndirectionPointerRef): ptrcls = irast.TupleIndirectionLink( source=ir_typeref_to_type(schema, ptrref.out_source), target=ir_typeref_to_type(schema, ptrref.out_target), element_name=ptrref.name.name, ) elif isinstance(ptrref, irast.TypeIntersectionPointerRef): ptrcls = irast.TypeIntersectionLink( source=schema.get_by_id(ptrref.out_source.id), target=schema.get_by_id(ptrref.out_target.id), optional=ptrref.optional, is_empty=ptrref.is_empty, is_subtype=ptrref.is_subtype, cardinality=ptrref.out_cardinality, ) elif isinstance(ptrref, irast.PointerRef): ptrcls = schema.get_by_id(ptrref.id) else: raise TypeError(f'unexpected pointer ref type: {ptrref!r}') return ptrcls
def float_const_to_python(ir: irast.FloatConstant, schema: s_schema.Schema) -> Any: stype = schema.get_by_id(ir.typeref.id) assert isinstance(stype, s_types.Type) bigint = schema.get('std::bigint', type=s_obj.SubclassableObject) if stype.issubclass(schema, bigint): return decimal.Decimal(ir.value) else: return float(ir.value)
def ir_typeref_to_type( schema: s_schema.Schema, typeref: irast.TypeRef, ) -> Tuple[s_schema.Schema, s_types.Type]: """Return a schema type for a given IR TypeRef. This is the reverse of :func:`~type_to_typeref`. Args: schema: A schema instance. The result type must exist in it. typeref: A :class:`ir.ast.TypeRef` instance for which to return the corresponding schema type. Returns: A tuple containing the possibly modified schema and a :class:`schema.types.Type` instance corresponding to the given *typeref*. """ if is_anytuple(typeref): return schema, s_pseudo.PseudoType.get(schema, 'anytuple') elif is_any(typeref): return schema, s_pseudo.PseudoType.get(schema, 'anytype') elif is_tuple(typeref): named = False tuple_subtypes = {} for si, st in enumerate(typeref.subtypes): if st.element_name: named = True type_name = st.element_name else: type_name = str(si) schema, st_t = ir_typeref_to_type(schema, st) tuple_subtypes[type_name] = st_t return s_types.Tuple.from_subtypes( schema, tuple_subtypes, {'named': named}) elif is_array(typeref): array_subtypes = [] for st in typeref.subtypes: schema, st_t = ir_typeref_to_type(schema, st) array_subtypes.append(st_t) return s_types.Array.from_subtypes(schema, array_subtypes) else: t = schema.get_by_id(typeref.id) assert isinstance(t, s_types.Type), 'expected a Type instance' return schema, t
def parse_into( schema: s_schema.Schema, data: Sequence[str], schema_class_layout: Dict[Type[s_obj.Object], sr_struct.SchemaTypeLayout], ) -> s_schema.Schema: """Parse JSON-encoded schema objects and populate the schema with them. Args: schema: A schema instance to use as a starting point. data: A sequence of JSON-encoded schema object data as returned by an introspection query. schema_class_layout: A mapping describing schema class layout in the reflection, as returned from :func:`schema.reflection.structure.generate_structure`. Returns: A schema instance including objects encoded in the provided JSON sequence. """ id_to_type = {} id_to_data = {} name_to_id = {} shortname_to_id = collections.defaultdict(set) globalname_to_id = {} dict_of_dicts: Callable[ [], Dict[Tuple[Type[s_obj.Object], str], Dict[uuid.UUID, None]], ] = functools.partial(collections.defaultdict, dict) # type: ignore refs_to: Dict[ uuid.UUID, Dict[Tuple[Type[s_obj.Object], str], Dict[uuid.UUID, None]] ] = collections.defaultdict(dict_of_dicts) objects: Dict[uuid.UUID, Tuple[s_obj.Object, Dict[str, Any]]] = {} for entry_json in data: entry = json.loads(entry_json) _, _, clsname = entry['_tname'].rpartition('::') mcls = s_obj.ObjectMeta.get_schema_metaclass(clsname) if mcls is None: raise ValueError( f'unexpected type in schema reflection: {clsname}') objid = uuidgen.UUID(entry['id']) objects[objid] = (mcls._create_from_id(objid), entry) refdict_updates = {} for objid, (obj, entry) in objects.items(): mcls = type(obj) name = entry['name__internal'] layout = schema_class_layout[mcls] if isinstance(obj, s_obj.QualifiedObject): name = s_name.Name(name) name_to_id[name] = objid else: globalname_to_id[mcls, name] = objid if isinstance(obj, (s_func.Function, s_oper.Operator)): shortname = mcls.get_shortname_static(name) shortname_to_id[mcls, shortname].add(objid) id_to_type[objid] = obj objdata: Dict[str, Any] = {} val: Any for k, v in entry.items(): desc = layout.get(k) if desc is None: continue fn = desc.fieldname if desc.storage is not None: if v is None: pass elif desc.storage.ptrkind == 'link': refid = uuidgen.UUID(v['id']) newobj = objects.get(refid) if newobj is not None: val = newobj[0] else: val = schema.get_by_id(refid) objdata[fn] = val refs_to[val.id][mcls, fn][objid] = None elif desc.storage.ptrkind == 'multi link': ftype = mcls.get_field(fn).type if issubclass(ftype, s_obj.ObjectDict): refids = ftype._container( uuidgen.UUID(e['value']) for e in v) val = ftype(refids, _private_init=True) val._keys = tuple(e['name'] for e in v) else: refids = ftype._container( uuidgen.UUID(e['id']) for e in v) val = ftype(refids, _private_init=True) objdata[fn] = val for refid in refids: refs_to[refid][mcls, fn][objid] = None elif desc.storage.shadow_ptrkind: val = entry[f'{k}__internal'] ftype = mcls.get_field(fn).type if val is not None and type(val) is not ftype: if issubclass(ftype, s_expr.Expression): val = _parse_expression(val) for refid in val.refs.ids(schema): refs_to[refid][mcls, fn][objid] = None elif issubclass(ftype, s_expr.ExpressionList): exprs = [] for e_dict in val: e = _parse_expression(e_dict) assert e.refs is not None for refid in e.refs.ids(schema): refs_to[refid][mcls, fn][objid] = None exprs.append(e) val = ftype(exprs) else: val = ftype(val) objdata[fn] = val else: ftype = mcls.get_field(fn).type if type(v) is not ftype: objdata[fn] = ftype(v) else: objdata[fn] = v elif desc.is_refdict: ftype = mcls.get_field(fn).type refids = ftype._container(uuidgen.UUID(e['id']) for e in v) for refid in refids: refs_to[refid][mcls, fn][objid] = None val = ftype(refids, _private_init=True) objdata[fn] = val if desc.properties: for e_dict in v: refdict_updates[uuidgen.UUID(e_dict['id'])] = { p: pv for p in desc.properties if (pv := e_dict[f'@{p}']) is not None } id_to_data[objid] = immutables.Map(objdata) for objid, updates in refdict_updates.items(): if updates: id_to_data[objid] = id_to_data[objid].update(updates) with schema._refs_to.mutate() as mm: for referred_id, refdata in refs_to.items(): try: refs = mm[referred_id] except KeyError: refs = immutables.Map(( (k, immutables.Map(r)) for k, r in refdata.items() )) else: refs_update = {} for k, referrers in refdata.items(): try: rt = refs[k] except KeyError: rt = immutables.Map(referrers) else: rt = rt.update(referrers) refs_update[k] = rt refs = refs.update(refs_update) mm[referred_id] = refs schema = schema._replace( id_to_type=schema._id_to_type.update(id_to_type), id_to_data=schema._id_to_data.update(id_to_data), name_to_id=schema._name_to_id.update(name_to_id), shortname_to_id=schema._shortname_to_id.update( (k, frozenset(v)) for k, v in shortname_to_id.items() ), globalname_to_id=schema._globalname_to_id.update(globalname_to_id), refs_to=mm.finish(), ) return schema
def type_to_typeref( schema: s_schema.Schema, t: s_types.Type, *, cache: Optional[Dict[uuid.UUID, irast.TypeRef]] = None, typename: Optional[s_name.Name] = None, _name: Optional[str] = None, ) -> irast.TypeRef: """Return an instance of :class:`ir.ast.TypeRef` for a given type. An IR TypeRef is an object that fully describes a schema type for the purposes of query compilation. Args: schema: A schema instance, in which the type *t* is defined. t: A schema type instance. cache: Optional mapping from (type UUID, typename) to cached IR TypeRefs. typename: Optional name hint to use for the type in the returned TypeRef. If ``None``, the type name is used. _name: Optional subtype element name if this type is a collection within a Tuple, Returns: A ``TypeRef`` instance corresponding to the given schema type. """ result: irast.TypeRef if cache is not None and typename is None: cached_result = cache.get(t.id) if cached_result is not None: # If the schema changed due to an ongoing compilation, the name # hint might be outdated. if cached_result.name_hint == t.get_name(schema): return cached_result if t.is_anytuple(): result = irast.AnyTupleRef( id=t.id, name_hint=typename or t.get_name(schema), ) elif t.is_any(): result = irast.AnyTypeRef( id=t.id, name_hint=typename or t.get_name(schema), ) elif not isinstance(t, s_abc.Collection): union_of = t.get_union_of(schema) if union_of: non_overlapping, union_is_concrete = ( s_utils.get_non_overlapping_union( schema, union_of.objects(schema), )) union = frozenset( type_to_typeref(schema, c, cache=cache) for c in non_overlapping) else: union_is_concrete = False union = frozenset() intersection_of = t.get_intersection_of(schema) if intersection_of: intersection = frozenset( type_to_typeref(schema, c, cache=cache) for c in intersection_of.objects(schema)) else: intersection = frozenset() material_type = t.material_type(schema) material_typeref: Optional[irast.TypeRef] if material_type is not t: material_typeref = type_to_typeref(schema, material_type, cache=cache) else: material_typeref = None if (isinstance(material_type, s_scalars.ScalarType) and not material_type.get_is_abstract(schema)): base_type = material_type.get_topmost_concrete_base(schema) if base_type is material_type: base_typeref = None else: assert isinstance(base_type, s_types.Type) base_typeref = type_to_typeref(schema, base_type, cache=cache) else: base_typeref = None if typename is not None: name = typename else: name = t.get_name(schema) module = schema.get_global(s_mod.Module, name.module) common_parent_ref: Optional[irast.TypeRef] if union_of: common_parent = s_utils.get_class_nearest_common_ancestor( schema, union_of.objects(schema)) assert isinstance(common_parent, s_types.Type) common_parent_ref = type_to_typeref(schema, common_parent, cache=cache) else: common_parent_ref = None result = irast.TypeRef( id=t.id, module_id=module.id, name_hint=name, material_type=material_typeref, base_type=base_typeref, union=union, union_is_concrete=union_is_concrete, intersection=intersection, common_parent=common_parent_ref, element_name=_name, is_scalar=t.is_scalar(), is_abstract=t.get_is_abstract(schema), is_view=t.is_view(schema), is_opaque_union=t.get_is_opaque_union(schema), ) elif isinstance(t, s_abc.Tuple) and t.named: result = irast.TypeRef( id=t.id, name_hint=typename or t.get_name(schema), element_name=_name, collection=t.schema_name, in_schema=schema.get_by_id(t.id, None) is not None, subtypes=tuple( type_to_typeref(schema, st, _name=sn) # note: no cache for sn, st in t.iter_subtypes(schema))) else: result = irast.TypeRef(id=t.id, name_hint=typename or t.get_name(schema), element_name=_name, collection=t.schema_name, in_schema=schema.get_by_id(t.id, None) is not None, subtypes=tuple( type_to_typeref(schema, st, cache=cache) for st in t.get_subtypes(schema))) if cache is not None and typename is None and _name is None: # Note: there is no cache for `_name` variants since they are only used # for Tuple subtypes and thus they will be cached on the outer level # anyway. # There's also no variant for types with custom typenames since they # proved to have a very low hit rate. # This way we save on the size of the key tuple. cache[t.id] = result return result