Exemplo n.º 1
0
def get_config_type_shape(
    schema: s_schema.Schema,
    stype: s_objtypes.ObjectType,
    path: List[qlast.Base],
) -> List[qlast.ShapeElement]:
    from . import objtypes as s_objtypes
    shape = []
    seen: Set[str] = set()

    stypes = [stype] + list(stype.descendants(schema))

    for t in stypes:
        t_name = t.get_name(schema)

        for unqual_pn, p in t.get_pointers(schema).items(schema):
            pn = str(unqual_pn)
            if pn in ('id', '__type__') or pn in seen:
                continue

            elem_path: List[qlast.Base] = []

            if t != stype:
                elem_path.append(
                    qlast.TypeIntersection(type=qlast.TypeName(
                        maintype=qlast.ObjectRef(
                            module=t_name.module,
                            name=t_name.name,
                        ), ), ), )

            elem_path.append(qlast.Ptr(ptr=qlast.ObjectRef(name=pn)))

            ptype = p.get_target(schema)
            assert ptype is not None

            if isinstance(ptype, s_objtypes.ObjectType):
                subshape = get_config_type_shape(schema, ptype,
                                                 path + elem_path)
                subshape.append(
                    qlast.ShapeElement(
                        expr=qlast.Path(steps=[
                            qlast.Ptr(ptr=qlast.ObjectRef(name='_tname'), ),
                        ], ),
                        compexpr=qlast.Path(steps=path + elem_path + [
                            qlast.Ptr(ptr=qlast.ObjectRef(name='__type__')),
                            qlast.Ptr(ptr=qlast.ObjectRef(name='name')),
                        ], ),
                    ), )
            else:
                subshape = []

            shape.append(
                qlast.ShapeElement(
                    expr=qlast.Path(steps=elem_path),
                    elements=subshape,
                ), )

            seen.add(pn)

    return shape
Exemplo n.º 2
0
def compile_inheritance_conflict_checks(
    stmt: irast.MutatingStmt,
    subject_stype: s_objtypes.ObjectType,
    *, ctx: context.ContextLevel,
) -> Optional[List[irast.OnConflictClause]]:
    if not ctx.env.dml_stmts:
        return None

    assert isinstance(subject_stype, s_objtypes.ObjectType)
    modified_ancestors = set()
    base_object = ctx.env.schema.get(
        'std::BaseObject', type=s_objtypes.ObjectType)

    subject_stype = subject_stype.get_nearest_non_derived_parent(
        ctx.env.schema)
    subject_stypes = [subject_stype]
    # For updates, we need to also consider all descendants, because
    # those could also have interesting constraints of their own.
    if isinstance(stmt, irast.UpdateStmt):
        subject_stypes.extend(
            desc for desc in subject_stype.descendants(ctx.env.schema)
            if not desc.is_view(ctx.env.schema)
        )

    for ir in ctx.env.dml_stmts:
        # N.B that for updates, the update itself will be in dml_stmts,
        # since an update can conflict with itself if there are subtypes.
        # If there aren't subtypes, though, skip it.
        if ir is stmt and len(subject_stypes) == 1:
            continue

        typ = setgen.get_set_type(ir.subject, ctx=ctx)
        assert isinstance(typ, s_objtypes.ObjectType)
        typ = typ.get_nearest_non_derived_parent(ctx.env.schema)

        typs = [typ]
        # As mentioned above, need to consider descendants of updates
        if isinstance(ir, irast.UpdateStmt):
            typs.extend(
                desc for desc in typ.descendants(ctx.env.schema)
                if not desc.is_view(ctx.env.schema)
            )

        for typ in typs:
            for subject_stype in subject_stypes:
                # If the earlier DML has a shared ancestor that isn't
                # BaseObject and isn't (if it's an insert) the same type,
                # then we need to see if we need a conflict select
                if (
                    subject_stype == typ
                    and not isinstance(ir, irast.UpdateStmt)
                    and not isinstance(stmt, irast.UpdateStmt)
                ):
                    continue
                ancs = s_utils.get_class_nearest_common_ancestors(
                    ctx.env.schema, [subject_stype, typ])
                for anc in ancs:
                    if anc != base_object:
                        modified_ancestors.add((subject_stype, anc, ir))

    conflicters = []
    for subject_stype, anc_type, ir in modified_ancestors:
        conflicters.extend(compile_inheritance_conflict_selects(
            stmt, ir, anc_type, subject_stype, ctx=ctx))

    return conflicters or None