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
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