From 14b8515473f4f6616fd1e5ae6e895e59bc95dc97 Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Tue, 20 Apr 2021 19:37:48 +0200 Subject: [PATCH] repo-sqale: finished assignment ref insertion + test This required another refactoring and another generics battle: - Ref mapping is aware of Q-type for the owner now as well. - Context in filter processors and mapping in transformers was stored all the way up in the hierarchy, this is not flexible, we will store it at the level where the type is most specific (this is started, WIP). - QAssignmentReference constructor now takes table name as it should. - Ref mapping instances for "abstract" tables are accessed via methods that fix their parametrized types to conform to the current instance. --- .../item/RefTableItemDeltaProcessor.java | 8 ++- .../RefTableItemFilterProcessor.java | 23 ++++---- .../qmodel/ObjectTemplateSqlTransformer.java | 3 + .../repo/sqale/qmodel/SqaleMappingMixin.java | 19 +++++-- .../repo/sqale/qmodel/SqaleTableMapping.java | 4 -- .../sqale/qmodel/SqaleTransformerBase.java | 10 +++- .../assignment/QAssignmentReference.java | 4 +- .../QAssignmentReferenceMapping.java | 4 +- .../qmodel/focus/FocusSqlTransformer.java | 8 ++- .../sqale/qmodel/focus/QFocusMapping.java | 17 +++++- .../object/ContainerSqlTransformer.java | 3 + .../qmodel/object/ObjectSqlTransformer.java | 27 +++++---- .../sqale/qmodel/object/QObjectMapping.java | 56 ++++++++++++++++--- .../qmodel/ref/QObjectReferenceMapping.java | 50 +++++++++-------- .../sqale/qmodel/ref/QReferenceMapping.java | 16 +++++- .../qmodel/ref/ReferenceSqlTransformer.java | 7 ++- .../qmodel/resource/QResourceMapping.java | 5 +- .../repo/sqale/SqaleRepoBaseTest.java | 3 +- .../func/SqaleRepoAddDeleteObjectTest.java | 43 +++++++++++++- .../filtering/item/ItemFilterProcessor.java | 7 ++- 20 files changed, 227 insertions(+), 90 deletions(-) diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/delta/item/RefTableItemDeltaProcessor.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/delta/item/RefTableItemDeltaProcessor.java index 2b71141a7f9..5524c6db724 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/delta/item/RefTableItemDeltaProcessor.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/delta/item/RefTableItemDeltaProcessor.java @@ -15,14 +15,16 @@ import com.evolveum.midpoint.repo.sqale.qmodel.ref.QReference; import com.evolveum.midpoint.repo.sqale.qmodel.ref.QReferenceMapping; import com.evolveum.midpoint.repo.sqale.qmodel.ref.ReferenceSqlTransformer; +import com.evolveum.midpoint.repo.sqlbase.querydsl.FlexibleRelationalPathBase; -public class RefTableItemDeltaProcessor, OR> extends ItemDeltaValueProcessor { +public class RefTableItemDeltaProcessor, OQ extends FlexibleRelationalPathBase, OR> + extends ItemDeltaValueProcessor { - private final QReferenceMapping refTableMapping; + private final QReferenceMapping refTableMapping; public RefTableItemDeltaProcessor( SqaleUpdateContext context, // TODO OR as last here as well - QReferenceMapping refTableMapping) { + QReferenceMapping refTableMapping) { super(context); this.refTableMapping = refTableMapping; } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/filtering/RefTableItemFilterProcessor.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/filtering/RefTableItemFilterProcessor.java index 757b8652bde..d3f87bb1c0e 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/filtering/RefTableItemFilterProcessor.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/filtering/RefTableItemFilterProcessor.java @@ -9,36 +9,35 @@ import com.querydsl.core.types.Predicate; import com.evolveum.midpoint.prism.query.RefFilter; -import com.evolveum.midpoint.repo.sqale.qmodel.object.QObject; import com.evolveum.midpoint.repo.sqale.qmodel.ref.MReference; -import com.evolveum.midpoint.repo.sqale.qmodel.ref.QObjectReference; -import com.evolveum.midpoint.repo.sqale.qmodel.ref.QObjectReferenceMapping; import com.evolveum.midpoint.repo.sqale.qmodel.ref.QReference; +import com.evolveum.midpoint.repo.sqale.qmodel.ref.QReferenceMapping; import com.evolveum.midpoint.repo.sqlbase.SqlQueryContext; import com.evolveum.midpoint.repo.sqlbase.filtering.item.ItemFilterProcessor; +import com.evolveum.midpoint.repo.sqlbase.querydsl.FlexibleRelationalPathBase; /** * Filter processor for reference item paths resolved via {@link QReference} tables. * This just joins the reference table and then delegates to {@link RefItemFilterProcessor}. */ -public class RefTableItemFilterProcessor +public class RefTableItemFilterProcessor, R extends MReference, + OQ extends FlexibleRelationalPathBase, OR> extends ItemFilterProcessor { - private final QObjectReferenceMapping qObjectReferenceMapping; + private final SqlQueryContext context; + private final QReferenceMapping referenceMapping; public RefTableItemFilterProcessor( - SqlQueryContext context, QObjectReferenceMapping qObjectReferenceMapping) { + SqlQueryContext context, QReferenceMapping referenceMapping) { super(context); - this.qObjectReferenceMapping = qObjectReferenceMapping; + this.context = context; + this.referenceMapping = referenceMapping; } @Override public Predicate process(RefFilter filter) { - // the cast is NOT redundant really (IDEA thinks so), it's needed for "o" in lambda - @SuppressWarnings({ "RedundantCast", "unchecked" }) - SqlQueryContext refContext = - ((SqlQueryContext, ?>) context) - .leftJoin(qObjectReferenceMapping, (o, r) -> o.oid.eq(r.ownerOid)); + SqlQueryContext refContext = + context.leftJoin(referenceMapping, referenceMapping.joinOnPredicate()); QReference ref = refContext.path(); return new RefItemFilterProcessor(context, ref.targetOid, ref.targetType, ref.relationId) diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/ObjectTemplateSqlTransformer.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/ObjectTemplateSqlTransformer.java index b4f23b44494..d63494567a3 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/ObjectTemplateSqlTransformer.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/ObjectTemplateSqlTransformer.java @@ -18,9 +18,12 @@ public class ObjectTemplateSqlTransformer extends ObjectSqlTransformer { + private final QObjectTemplateMapping mapping; + public ObjectTemplateSqlTransformer( SqlTransformerSupport transformerSupport, QObjectTemplateMapping mapping) { super(transformerSupport, mapping); + this.mapping = mapping; } @Override diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/SqaleMappingMixin.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/SqaleMappingMixin.java index ae6bf32c5a3..6c109786ad9 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/SqaleMappingMixin.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/SqaleMappingMixin.java @@ -18,7 +18,8 @@ import com.evolveum.midpoint.repo.sqale.qmodel.common.MContainer; import com.evolveum.midpoint.repo.sqale.qmodel.common.QContainer; import com.evolveum.midpoint.repo.sqale.qmodel.common.QContainerMapping; -import com.evolveum.midpoint.repo.sqale.qmodel.ref.QObjectReferenceMapping; +import com.evolveum.midpoint.repo.sqale.qmodel.ref.QReferenceMapping; +import com.evolveum.midpoint.repo.sqlbase.SqlQueryContext; import com.evolveum.midpoint.repo.sqlbase.mapping.ItemRelationResolver; import com.evolveum.midpoint.repo.sqlbase.mapping.ItemSqlMapper; import com.evolveum.midpoint.repo.sqlbase.mapping.QueryModelMapping; @@ -61,12 +62,20 @@ default SqaleNestedMapping addNestedMapping( return nestedMapping; } - /** Defines reference mapping for both query and modifications. */ + /** + * Defines reference mapping for both query and modifications. + * Reference mapping is assumed to be `QReferenceMapping` really, but because + * the instances of reference mapping are not typed flexibly enough, wildcard is used for + * parameter for client code convenience. + */ default SqaleMappingMixin addRefMapping( - @NotNull QName itemName, @NotNull QObjectReferenceMapping qReferenceMapping) { + @NotNull QName itemName, @NotNull QReferenceMapping referenceMapping) { + //noinspection unchecked addItemMapping(itemName, new SqaleItemSqlMapper( - ctx -> new RefTableItemFilterProcessor(ctx, qReferenceMapping), - ctx -> new RefTableItemDeltaProcessor(ctx, qReferenceMapping))); + ctx -> new RefTableItemFilterProcessor<>( + (SqlQueryContext) ctx, referenceMapping), + ctx -> new RefTableItemDeltaProcessor<>(ctx, referenceMapping))); + // TODO add relation mapping too for reaching to the reference target return this; } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/SqaleTableMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/SqaleTableMapping.java index 4a7265f73e4..a6e36f125f1 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/SqaleTableMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/SqaleTableMapping.java @@ -7,7 +7,6 @@ package com.evolveum.midpoint.repo.sqale.qmodel; import java.util.function.Function; -import javax.xml.namespace.QName; import com.querydsl.core.types.EntityPath; import com.querydsl.core.types.dsl.*; @@ -15,17 +14,14 @@ import com.evolveum.midpoint.repo.sqale.delta.item.*; import com.evolveum.midpoint.repo.sqale.filtering.RefItemFilterProcessor; -import com.evolveum.midpoint.repo.sqale.filtering.RefTableItemFilterProcessor; import com.evolveum.midpoint.repo.sqale.filtering.UriItemFilterProcessor; import com.evolveum.midpoint.repo.sqale.mapping.SqaleItemSqlMapper; import com.evolveum.midpoint.repo.sqale.qmodel.object.MObjectType; import com.evolveum.midpoint.repo.sqale.qmodel.object.QObject; -import com.evolveum.midpoint.repo.sqale.qmodel.ref.QObjectReferenceMapping; import com.evolveum.midpoint.repo.sqlbase.filtering.item.EnumItemFilterProcessor; import com.evolveum.midpoint.repo.sqlbase.filtering.item.PolyStringItemFilterProcessor; import com.evolveum.midpoint.repo.sqlbase.filtering.item.SimpleItemFilterProcessor; import com.evolveum.midpoint.repo.sqlbase.filtering.item.TimestampItemFilterProcessor; -import com.evolveum.midpoint.repo.sqlbase.mapping.QueryModelMapping; import com.evolveum.midpoint.repo.sqlbase.mapping.QueryTableMapping; import com.evolveum.midpoint.repo.sqlbase.querydsl.FlexibleRelationalPathBase; import com.evolveum.midpoint.repo.sqlbase.querydsl.UuidPath; diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/SqaleTransformerBase.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/SqaleTransformerBase.java index 5a4834c8bee..76f8d003f97 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/SqaleTransformerBase.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/SqaleTransformerBase.java @@ -60,6 +60,10 @@ protected SqaleTransformerBase( this.mapping = mapping; } + public QueryTableMapping mapping() { + return mapping; + } + @Override public S toSchemaObject(R row) { throw new UnsupportedOperationException("Use toSchemaObject(Tuple,...)"); @@ -191,9 +195,9 @@ protected void setReference(ObjectReferenceType ref, JdbcSession jdbcSession, } } - protected void storeRefs( + protected , OR> void storeRefs( @NotNull OR ownerRow, @NotNull List refs, - @NotNull QReferenceMapping mapping, @NotNull JdbcSession jdbcSession) { + @NotNull QReferenceMapping mapping, @NotNull JdbcSession jdbcSession) { if (!refs.isEmpty()) { ReferenceSqlTransformer transformer = mapping.createTransformer(transformerSupport); @@ -210,7 +214,7 @@ protected String[] arrayFor(List strings) { /** Convenient insert shortcut when the row is fully populated. */ protected void insert(R row, JdbcSession jdbcSession) { - jdbcSession.newInsert(mapping.defaultAlias()) + jdbcSession.newInsert(mapping().defaultAlias()) .populate(row) .execute(); } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/assignment/QAssignmentReference.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/assignment/QAssignmentReference.java index 16de0d0abfb..e42f2c6afd4 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/assignment/QAssignmentReference.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/assignment/QAssignmentReference.java @@ -29,8 +29,8 @@ public class QAssignmentReference extends QReference { public final PrimaryKey pk = createPrimaryKey(ownerOid, assignmentCid, referenceType, relationId, targetOid); - public QAssignmentReference(String variable) { - this(variable, DEFAULT_SCHEMA_NAME, TABLE_NAME); + public QAssignmentReference(String variable, String tableName) { + this(variable, DEFAULT_SCHEMA_NAME, tableName); } public QAssignmentReference(String variable, String schema, String table) { diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/assignment/QAssignmentReferenceMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/assignment/QAssignmentReferenceMapping.java index 73409b276e1..1300a5d3b52 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/assignment/QAssignmentReferenceMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/assignment/QAssignmentReferenceMapping.java @@ -15,7 +15,7 @@ * The mapping is the same for all subtypes, see different `INSTANCE_*` constants below. */ public class QAssignmentReferenceMapping - extends QReferenceMapping { + extends QReferenceMapping { public static final QAssignmentReferenceMapping INSTANCE_ASSIGNMENT_CREATE_APPROVER = new QAssignmentReferenceMapping("m_assignment_ref_create_approver", "arefca"); @@ -30,7 +30,7 @@ private QAssignmentReferenceMapping(String tableName, String defaultAliasName) { @Override protected QAssignmentReference newAliasInstance(String alias) { - return new QAssignmentReference(alias); + return new QAssignmentReference(alias, tableName()); } @Override diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/FocusSqlTransformer.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/FocusSqlTransformer.java index bef762295a0..266b2e10789 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/FocusSqlTransformer.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/FocusSqlTransformer.java @@ -9,7 +9,6 @@ import org.jetbrains.annotations.NotNull; import com.evolveum.midpoint.repo.sqale.qmodel.object.ObjectSqlTransformer; -import com.evolveum.midpoint.repo.sqale.qmodel.ref.QObjectReferenceMapping; import com.evolveum.midpoint.repo.sqlbase.JdbcSession; import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; import com.evolveum.midpoint.util.MiscUtil; @@ -18,9 +17,12 @@ public class FocusSqlTransformer, R extends MFocus> extends ObjectSqlTransformer { + private final QFocusMapping mapping; + public FocusSqlTransformer( SqlTransformerSupport transformerSupport, QFocusMapping mapping) { super(transformerSupport, mapping); + this.mapping = mapping; } @SuppressWarnings("DuplicatedCode") // activation code duplicated with assignment @@ -77,8 +79,8 @@ public void storeRelatedEntities( super.storeRelatedEntities(row, schemaObject, jdbcSession); storeRefs(row, schemaObject.getLinkRef(), - QObjectReferenceMapping.INSTANCE_PROJECTION, jdbcSession); + mapping.projectionReferenceMapping(), jdbcSession); storeRefs(row, schemaObject.getPersonaRef(), - QObjectReferenceMapping.INSTANCE_PERSONA, jdbcSession); + mapping.personaReferenceMapping(), jdbcSession); } } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/QFocusMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/QFocusMapping.java index 9b2ab5f32e1..6765f97588e 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/QFocusMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/QFocusMapping.java @@ -80,9 +80,20 @@ protected QFocusMapping( .addItemMapping(ActivationType.F_LOCKOUT_STATUS, enumMapper(path(q -> q.lockoutStatus))); - addRefMapping(F_DELEGATED_REF, QObjectReferenceMapping.INSTANCE_DELEGATED); - addRefMapping(F_PERSONA_REF, QObjectReferenceMapping.INSTANCE_PERSONA); - addRefMapping(F_LINK_REF, QObjectReferenceMapping.INSTANCE_PROJECTION); + addRefMapping(F_PERSONA_REF, personaReferenceMapping()); + addRefMapping(F_LINK_REF, projectionReferenceMapping()); + } + + /** Fixes rigid parametric types of static mapping instance to this instance. */ + public @NotNull QObjectReferenceMapping personaReferenceMapping() { + //noinspection unchecked + return (QObjectReferenceMapping) QObjectReferenceMapping.INSTANCE_PERSONA; + } + + /** Fixes rigid parametric types of static mapping instance to this instance. */ + public @NotNull QObjectReferenceMapping projectionReferenceMapping() { + //noinspection unchecked + return (QObjectReferenceMapping) QObjectReferenceMapping.INSTANCE_PROJECTION; } @Override diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/ContainerSqlTransformer.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/ContainerSqlTransformer.java index f298a0e04ea..41664099934 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/ContainerSqlTransformer.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/ContainerSqlTransformer.java @@ -19,9 +19,12 @@ public class ContainerSqlTransformer , R extends MContainer> extends SqaleTransformerBase { + private final QContainerMapping mapping; + public ContainerSqlTransformer( SqlTransformerSupport transformerSupport, QContainerMapping mapping) { super(transformerSupport, mapping); + this.mapping = mapping; } /** diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/ObjectSqlTransformer.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/ObjectSqlTransformer.java index 158c75f1c4a..2712e9d1cfc 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/ObjectSqlTransformer.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/ObjectSqlTransformer.java @@ -23,7 +23,6 @@ import com.evolveum.midpoint.repo.sqale.qmodel.assignment.AssignmentSqlTransformer; import com.evolveum.midpoint.repo.sqale.qmodel.assignment.QAssignmentMapping; import com.evolveum.midpoint.repo.sqale.qmodel.common.QUri; -import com.evolveum.midpoint.repo.sqale.qmodel.ref.QObjectReferenceMapping; import com.evolveum.midpoint.repo.sqlbase.JdbcSession; import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; import com.evolveum.midpoint.schema.GetOperationOptions; @@ -36,10 +35,13 @@ public class ObjectSqlTransformer, R extends MObject> extends SqaleTransformerBase { + private final QObjectMapping mapping; + public ObjectSqlTransformer( SqlTransformerSupport transformerSupport, QObjectMapping mapping) { super(transformerSupport, mapping); + this.mapping = mapping; } @Override @@ -64,7 +66,7 @@ public S toSchemaObject(Tuple row, Q entityPath, // This is a serious thing. We have corrupted XML in the repo. This may happen even // during system init. We want really loud and detailed error here. logger.error("Couldn't parse object {} {}: {}: {}\n{}", - mapping.schemaType().getSimpleName(), row.get(entityPath.oid), + mapping().schemaType().getSimpleName(), row.get(entityPath.oid), e.getClass().getName(), e.getMessage(), serializedForm, e); throw e; } @@ -86,7 +88,7 @@ public S toSchemaObject(Tuple row, Q entityPath, @SuppressWarnings("DuplicatedCode") // see comment for metadata lower @NotNull public R toRowObjectWithoutFullObject(S schemaObject, JdbcSession jdbcSession) { - R row = mapping.newRowObject(); + R row = mapping().newRowObject(); row.oid = oidToUUid(schemaObject.getOid()); // objectType MUST be left NULL, it's determined by PG @@ -146,9 +148,9 @@ public void storeRelatedEntities( MetadataType metadata = schemaObject.getMetadata(); if (metadata != null) { storeRefs(row, metadata.getCreateApproverRef(), - QObjectReferenceMapping.INSTANCE_OBJECT_CREATE_APPROVER, jdbcSession); + mapping.objectCreateApproverReferenceMapping(), jdbcSession); storeRefs(row, metadata.getModifyApproverRef(), - QObjectReferenceMapping.INSTANCE_OBJECT_MODIFY_APPROVER, jdbcSession); + mapping.objectModifyApproverReferenceMapping(), jdbcSession); } List triggers = schemaObject.getTrigger(); @@ -165,7 +167,8 @@ public void storeRelatedEntities( operationExecutions.forEach(oe -> transformer.insert(oe, row, jdbcSession)); } - // schemaObject.getParentOrgRef() TODO + storeRefs(row, schemaObject.getParentOrgRef(), + mapping.objectParentOrgReferenceMapping(), jdbcSession); if (schemaObject instanceof AssignmentHolderType) { storeAssignmentHolderEntities(row, (AssignmentHolderType) schemaObject, jdbcSession); @@ -179,7 +182,7 @@ public void storeRelatedEntities( } private void storeAssignmentHolderEntities( - MObject row, AssignmentHolderType schemaObject, JdbcSession jdbcSession) { + R row, AssignmentHolderType schemaObject, JdbcSession jdbcSession) { List assignments = schemaObject.getAssignment(); if (!assignments.isEmpty()) { AssignmentSqlTransformer transformer = @@ -188,12 +191,12 @@ private void storeAssignmentHolderEntities( transformer.insert(assignment, row, jdbcSession)); } - storeRefs(row, schemaObject.getRoleMembershipRef(), - QObjectReferenceMapping.INSTANCE_ROLE_MEMBERSHIP, jdbcSession); - storeRefs(row, schemaObject.getDelegatedRef(), - QObjectReferenceMapping.INSTANCE_DELEGATED, jdbcSession); storeRefs(row, schemaObject.getArchetypeRef(), - QObjectReferenceMapping.INSTANCE_ARCHETYPE, jdbcSession); + mapping.archetypeReferenceMapping(), jdbcSession); + storeRefs(row, schemaObject.getDelegatedRef(), + mapping.delegatedReferenceMapping(), jdbcSession); + storeRefs(row, schemaObject.getRoleMembershipRef(), + mapping.roleMembershipReferenceMapping(), jdbcSession); } /** diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QObjectMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QObjectMapping.java index aaf6635cf7b..89fd819cdc3 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QObjectMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QObjectMapping.java @@ -6,9 +6,7 @@ */ package com.evolveum.midpoint.repo.sqale.qmodel.object; -import static com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentHolderType.F_ARCHETYPE_REF; -import static com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentHolderType.F_ROLE_MEMBERSHIP_REF; -import static com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType.*; +import static com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentHolderType.*; import java.util.Collection; @@ -77,19 +75,59 @@ protected QObjectMapping( .addItemMapping(MetadataType.F_MODIFY_TIMESTAMP, timestampMapper(path(q -> q.modifyTimestamp))) .addRefMapping(MetadataType.F_CREATE_APPROVER_REF, - QObjectReferenceMapping.INSTANCE_OBJECT_CREATE_APPROVER) + objectCreateApproverReferenceMapping()) .addRefMapping(MetadataType.F_MODIFY_APPROVER_REF, - QObjectReferenceMapping.INSTANCE_OBJECT_MODIFY_APPROVER); + objectModifyApproverReferenceMapping()); - // AssignmentHolderType - addRefMapping(F_ARCHETYPE_REF, QObjectReferenceMapping.INSTANCE_ARCHETYPE); - addRefMapping(F_PARENT_ORG_REF, QObjectReferenceMapping.INSTANCE_OBJECT_PARENT_ORG); - addRefMapping(F_ROLE_MEMBERSHIP_REF, QObjectReferenceMapping.INSTANCE_ROLE_MEMBERSHIP); + addRefMapping(F_PARENT_ORG_REF, objectParentOrgReferenceMapping()); addContainerTableMapping(AssignmentHolderType.F_ASSIGNMENT, QAssignmentMapping.INSTANCE, joinOn((o, a) -> o.oid.eq(a.ownerOid))); addContainerTableMapping(F_TRIGGER, QTriggerMapping.INSTANCE, joinOn((o, trg) -> o.oid.eq(trg.ownerOid))); + + // AssignmentHolderType + addRefMapping(F_ARCHETYPE_REF, archetypeReferenceMapping()); + addRefMapping(F_DELEGATED_REF, delegatedReferenceMapping()); + addRefMapping(F_ROLE_MEMBERSHIP_REF, roleMembershipReferenceMapping()); + } + + /** Fixes rigid parametric types of static mapping instance to this instance. */ + public @NotNull QObjectReferenceMapping archetypeReferenceMapping() { + //noinspection unchecked + return (QObjectReferenceMapping) QObjectReferenceMapping.INSTANCE_ARCHETYPE; + } + + /** Fixes rigid parametric types of static mapping instance to this instance. */ + public @NotNull QObjectReferenceMapping delegatedReferenceMapping() { + //noinspection unchecked + return (QObjectReferenceMapping) QObjectReferenceMapping.INSTANCE_DELEGATED; + } + + /** Fixes rigid parametric types of static mapping instance to this instance. */ + public @NotNull QObjectReferenceMapping objectCreateApproverReferenceMapping() { + //noinspection unchecked + return (QObjectReferenceMapping) + QObjectReferenceMapping.INSTANCE_OBJECT_CREATE_APPROVER; + } + + /** Fixes rigid parametric types of static mapping instance to this instance. */ + public @NotNull QObjectReferenceMapping objectModifyApproverReferenceMapping() { + //noinspection unchecked + return (QObjectReferenceMapping) + QObjectReferenceMapping.INSTANCE_OBJECT_MODIFY_APPROVER; + } + + /** Fixes rigid parametric types of static mapping instance to this instance. */ + public @NotNull QObjectReferenceMapping objectParentOrgReferenceMapping() { + //noinspection unchecked + return (QObjectReferenceMapping) QObjectReferenceMapping.INSTANCE_OBJECT_PARENT_ORG; + } + + /** Fixes rigid parametric types of static mapping instance to this instance. */ + public @NotNull QObjectReferenceMapping roleMembershipReferenceMapping() { + //noinspection unchecked + return (QObjectReferenceMapping) QObjectReferenceMapping.INSTANCE_ROLE_MEMBERSHIP; } @Override diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/ref/QObjectReferenceMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/ref/QObjectReferenceMapping.java index 5de3685dec7..23b2ff2a69e 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/ref/QObjectReferenceMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/ref/QObjectReferenceMapping.java @@ -6,36 +6,42 @@ */ package com.evolveum.midpoint.repo.sqale.qmodel.ref; +import com.evolveum.midpoint.repo.sqale.qmodel.QObjectTemplate; import com.evolveum.midpoint.repo.sqale.qmodel.object.MObject; +import com.evolveum.midpoint.repo.sqale.qmodel.object.QObject; +import com.evolveum.midpoint.repo.sqale.qmodel.resource.MResource; +import com.evolveum.midpoint.repo.sqale.qmodel.resource.QResource; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType; /** * Mapping between {@link QObjectReference} and {@link ObjectReferenceType}. * The mapping is the same for all subtypes, see different `INSTANCE_*` constants below. + * These instances are not typed properly for non-leaf persistence types like object or focus, + * so mappings for these have adapter methods to change type parameters to ``. */ -public class QObjectReferenceMapping - extends QReferenceMapping { +public class QObjectReferenceMapping, R extends MObject> + extends QReferenceMapping { - public static final QObjectReferenceMapping INSTANCE_ARCHETYPE = - new QObjectReferenceMapping("m_ref_archetype", "refa"); - public static final QObjectReferenceMapping INSTANCE_DELEGATED = - new QObjectReferenceMapping("m_ref_delegated", "refd"); - public static final QObjectReferenceMapping INSTANCE_INCLUDE = - new QObjectReferenceMapping("m_ref_include", "refi"); - public static final QObjectReferenceMapping INSTANCE_PROJECTION = - new QObjectReferenceMapping("m_ref_projection", "refpj"); - public static final QObjectReferenceMapping INSTANCE_OBJECT_CREATE_APPROVER = - new QObjectReferenceMapping("m_ref_object_create_approver", "refca"); - public static final QObjectReferenceMapping INSTANCE_OBJECT_MODIFY_APPROVER = - new QObjectReferenceMapping("m_ref_object_modify_approver", "refma"); - public static final QObjectReferenceMapping INSTANCE_OBJECT_PARENT_ORG = - new QObjectReferenceMapping("m_ref_object_parent_org", "refpo"); - public static final QObjectReferenceMapping INSTANCE_PERSONA = - new QObjectReferenceMapping("m_ref_persona", "refp"); - public static final QObjectReferenceMapping INSTANCE_RESOURCE_BUSINESS_CONFIGURATION_APPROVER = - new QObjectReferenceMapping("m_ref_resource_business_configuration_approver", "refrbca"); - public static final QObjectReferenceMapping INSTANCE_ROLE_MEMBERSHIP = - new QObjectReferenceMapping("m_ref_role_membership", "refrm"); + public static final QObjectReferenceMapping INSTANCE_ARCHETYPE = + new QObjectReferenceMapping<>("m_ref_archetype", "refa"); + public static final QObjectReferenceMapping INSTANCE_DELEGATED = + new QObjectReferenceMapping<>("m_ref_delegated", "refd"); + public static final QObjectReferenceMapping INSTANCE_INCLUDE = + new QObjectReferenceMapping<>("m_ref_include", "refi"); + public static final QObjectReferenceMapping INSTANCE_PROJECTION = + new QObjectReferenceMapping<>("m_ref_projection", "refpj"); + public static final QObjectReferenceMapping INSTANCE_OBJECT_CREATE_APPROVER = + new QObjectReferenceMapping<>("m_ref_object_create_approver", "refca"); + public static final QObjectReferenceMapping INSTANCE_OBJECT_MODIFY_APPROVER = + new QObjectReferenceMapping<>("m_ref_object_modify_approver", "refma"); + public static final QObjectReferenceMapping INSTANCE_OBJECT_PARENT_ORG = + new QObjectReferenceMapping<>("m_ref_object_parent_org", "refpo"); + public static final QObjectReferenceMapping INSTANCE_PERSONA = + new QObjectReferenceMapping<>("m_ref_persona", "refp"); + public static final QObjectReferenceMapping INSTANCE_RESOURCE_BUSINESS_CONFIGURATION_APPROVER = + new QObjectReferenceMapping<>("m_ref_resource_business_configuration_approver", "refrbca"); + public static final QObjectReferenceMapping INSTANCE_ROLE_MEMBERSHIP = + new QObjectReferenceMapping<>("m_ref_role_membership", "refrm"); private QObjectReferenceMapping(String tableName, String defaultAliasName) { super(tableName, defaultAliasName, QObjectReference.class); diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/ref/QReferenceMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/ref/QReferenceMapping.java index bd6bcdfa920..24e5bcbc535 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/ref/QReferenceMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/ref/QReferenceMapping.java @@ -6,10 +6,14 @@ */ package com.evolveum.midpoint.repo.sqale.qmodel.ref; +import java.util.function.BiFunction; + +import com.querydsl.core.types.Predicate; + import com.evolveum.midpoint.prism.Referencable; import com.evolveum.midpoint.repo.sqale.qmodel.SqaleTableMapping; -import com.evolveum.midpoint.repo.sqale.qmodel.object.MObject; import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; +import com.evolveum.midpoint.repo.sqlbase.querydsl.FlexibleRelationalPathBase; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType; /** @@ -18,13 +22,15 @@ * * @param row type of the reference owner */ -public class QReferenceMapping, R extends MReference, OR> +public class QReferenceMapping, R extends MReference, OQ extends FlexibleRelationalPathBase, OR> extends SqaleTableMapping { // see also subtype specific alias names defined for instances below public static final String DEFAULT_ALIAS_NAME = "ref"; - public static final QReferenceMapping, MReference, MObject> INSTANCE = + /** Top level "abstract" reference table, not really needed for normal queries. */ + public static final QReferenceMapping< + QReference, MReference, FlexibleRelationalPathBase, Object> INSTANCE = new QReferenceMapping<>(QReference.TABLE_NAME, DEFAULT_ALIAS_NAME, QReference.CLASS); protected QReferenceMapping( @@ -59,4 +65,8 @@ public R newRowObject(OR ownerRow) { throw new UnsupportedOperationException( "Reference bean creation for owner row called on super-class level"); } + + public BiFunction joinOnPredicate() { + return null; + } } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/ref/ReferenceSqlTransformer.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/ref/ReferenceSqlTransformer.java index 0a9c2fd626b..2b21303d879 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/ref/ReferenceSqlTransformer.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/ref/ReferenceSqlTransformer.java @@ -16,9 +16,12 @@ public class ReferenceSqlTransformer, R extends MReference, OR> extends SqaleTransformerBase { + private final QReferenceMapping mapping; + public ReferenceSqlTransformer( - SqlTransformerSupport transformerSupport, QReferenceMapping mapping) { + SqlTransformerSupport transformerSupport, QReferenceMapping mapping) { super(transformerSupport, mapping); + this.mapping = mapping; } /** @@ -27,7 +30,7 @@ public ReferenceSqlTransformer( * All the other columns are based on a single schema type, so there is no variation. */ public void insert(Referencable schemaObject, OR ownerRow, JdbcSession jdbcSession) { - R row = ((QReferenceMapping) mapping).newRowObject(ownerRow); + R row = mapping.newRowObject(ownerRow); // row.referenceType is DB generated, must be kept NULL, but it will match referenceType row.relationId = processCacheableRelation(schemaObject.getRelation(), jdbcSession); row.targetOid = UUID.fromString(schemaObject.getOid()); diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/resource/QResourceMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/resource/QResourceMapping.java index e5202a65a52..03f2fecc8b3 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/resource/QResourceMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/resource/QResourceMapping.java @@ -18,8 +18,7 @@ /** * Mapping between {@link QResource} and {@link ResourceType}. */ -public class QResourceMapping - extends QObjectMapping { +public class QResourceMapping extends QObjectMapping { public static final String DEFAULT_ALIAS_NAME = "res"; @@ -33,10 +32,12 @@ private QResourceMapping() { enumMapper(path(q -> q.businessAdministrativeState))) .addRefMapping(ResourceBusinessConfigurationType.F_APPROVER_REF, QObjectReferenceMapping.INSTANCE_RESOURCE_BUSINESS_CONFIGURATION_APPROVER); + addNestedMapping(F_OPERATIONAL_STATE, OperationalStateType.class) .addItemMapping(OperationalStateType.F_LAST_AVAILABILITY_STATUS, enumMapper( path(q -> q.operationalStateLastAvailabilityStatus))); + addItemMapping(F_CONNECTOR_REF, refMapper( path(q -> q.connectorRefTargetOid), path(q -> q.connectorRefTargetType), diff --git a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/SqaleRepoBaseTest.java b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/SqaleRepoBaseTest.java index 017eafc2a5d..878f49ad37d 100644 --- a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/SqaleRepoBaseTest.java +++ b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/SqaleRepoBaseTest.java @@ -16,6 +16,7 @@ import com.querydsl.core.types.Predicate; import com.querydsl.sql.SQLQuery; +import org.jetbrains.annotations.Nullable; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.testng.annotations.BeforeClass; @@ -143,7 +144,7 @@ protected > List select( } } - protected > R selectOne( + protected > @Nullable R selectOne( Q path, Predicate... conditions) { try (JdbcSession jdbcSession = sqlRepoContext.newJdbcSession()) { return jdbcSession.newQuery() diff --git a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoAddDeleteObjectTest.java b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoAddDeleteObjectTest.java index 11847370897..1ab580633a5 100644 --- a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoAddDeleteObjectTest.java +++ b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoAddDeleteObjectTest.java @@ -24,6 +24,9 @@ import com.evolveum.midpoint.repo.sqale.SqaleRepoBaseTest; import com.evolveum.midpoint.repo.sqale.qmodel.accesscert.MAccessCertificationDefinition; import com.evolveum.midpoint.repo.sqale.qmodel.accesscert.QAccessCertificationDefinition; +import com.evolveum.midpoint.repo.sqale.qmodel.assignment.MAssignmentReference; +import com.evolveum.midpoint.repo.sqale.qmodel.assignment.QAssignmentReference; +import com.evolveum.midpoint.repo.sqale.qmodel.assignment.QAssignmentReferenceMapping; import com.evolveum.midpoint.repo.sqale.qmodel.common.MContainer; import com.evolveum.midpoint.repo.sqale.qmodel.common.MContainerType; import com.evolveum.midpoint.repo.sqale.qmodel.common.QContainer; @@ -79,6 +82,7 @@ public void test100AddNamedUserWithoutOidWorksOk() QUser u = aliasFor(QUser.class); MUser row = selectOne(u, u.nameOrig.eq(userName)); + assertThat(row).isNotNull(); assertThat(row.oid).isNotNull(); assertThat(row.nameNorm).isNotNull(); // normalized name is stored assertThat(row.version).isEqualTo(1); // initial version is set @@ -278,7 +282,7 @@ public void test205AddObjectWithMultivalueRefs() when("adding it to the repository"); repositoryService.addObject(user.asPrismObject(), null, result); - then("object and its container rows are created and container IDs are assigned"); + then("object and its reference rows are created"); assertThatOperationResult(result).isSuccess(); QUser u = aliasFor(QUser.class); @@ -303,6 +307,43 @@ public void test205AddObjectWithMultivalueRefs() .allMatch(rRow -> rRow.referenceType == MReferenceType.PROJECTION); } + @Test + public void test206AddObjectWithMultivalueRefsOnAssignment() + throws ObjectAlreadyExistsException, SchemaException { + OperationResult result = createOperationResult(); + + given("user with ref"); + String userName = "user" + getTestNumber(); + UUID approverRef1 = UUID.randomUUID(); + UUID approverRef2 = UUID.randomUUID(); + QName approverRelation = QName.valueOf("{https://random.org/ns}conn-rel"); // TODO + UserType user = new UserType(prismContext) + .name(userName) + .assignment(new AssignmentType() + .metadata(new MetadataType() + .createApproverRef(approverRef1.toString(), + UserType.COMPLEX_TYPE, approverRelation) + .createApproverRef(approverRef2.toString(), UserType.COMPLEX_TYPE))); + + when("adding it to the repository"); + String oid = repositoryService.addObject(user.asPrismObject(), null, result); + + then("object and its reference rows are created"); + assertThatOperationResult(result).isSuccess(); + + MUser userRow = selectObjectByOid(QUser.class, oid); + assertThat(userRow.oid).isNotNull(); + + QAssignmentReference ar = + QAssignmentReferenceMapping.INSTANCE_ASSIGNMENT_CREATE_APPROVER.defaultAlias(); + List projectionRefs = select(ar, ar.ownerOid.eq(userRow.oid)); + assertThat(projectionRefs).hasSize(2) + .allMatch(rRow -> rRow.referenceType == MReferenceType.ASSIGNMENT_CREATE_APPROVER) + .allMatch(rRow -> rRow.ownerOid.equals(userRow.oid)) + .allMatch(rRow -> rRow.assignmentCid.equals(1L)) // there's just one container + .anyMatch(refRowMatcher(approverRef1, approverRelation)); + } + @Test public void test290DuplicateCidInsideOneContainerIsCaughtByPrism() { expect("object construction with duplicate CID inside container fails immediately"); diff --git a/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/filtering/item/ItemFilterProcessor.java b/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/filtering/item/ItemFilterProcessor.java index 4aa949778b0..c87d239316c 100644 --- a/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/filtering/item/ItemFilterProcessor.java +++ b/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/filtering/item/ItemFilterProcessor.java @@ -112,8 +112,13 @@ protected Predicate singleValuePredicate(Path path, Ops operator, Object valu * This makes NOT truly complementary to non-NOT result. */ protected Predicate predicateWithNotTreated(Path path, Predicate predicate) { - return context.isNotFilterUsed() + return context().isNotFilterUsed() ? ExpressionUtils.and(predicate, ExpressionUtils.predicate(Ops.IS_NOT_NULL, path)) : predicate; } + + // TODO make abstract + public SqlQueryContext context() { + return context; + } }