Skip to content

Commit

Permalink
Sqale:Store assignment in m_assignment.fullObject
Browse files Browse the repository at this point in the history
Full data of assignments are not stored in m_fullObject, but rather
in m_assignment.
  • Loading branch information
tonydamage committed Oct 27, 2023
1 parent 86de31b commit 459db99
Show file tree
Hide file tree
Showing 10 changed files with 123 additions and 86 deletions.
6 changes: 6 additions & 0 deletions config/sql/native/postgres-upgrade.sql
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,12 @@ BEGIN
END;
$$;
$aa$);

-- Assignments have separate full object
call apply_change(26, $aa$
ALTER TABLE m_assignment ADD COLUMN fullObject BYTEA;
$aa$);

---
-- WRITE CHANGES ABOVE ^^
-- IMPORTANT: update apply_change number at the end of postgres-new.sql
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public class SqaleUtils {
*/
public static final String SCHEMA_AUDIT_CHANGE_NUMBER = "schemaAuditChangeNumber";

public static final int CURRENT_SCHEMA_CHANGE_NUMBER = 25;
public static final int CURRENT_SCHEMA_CHANGE_NUMBER = 26;

public static final int CURRENT_SCHEMA_AUDIT_CHANGE_NUMBER = 8;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,6 @@ public class MAssignment extends MContainer {
public Integer modifierRefRelationId;
public Integer modifyChannelId;
public Instant modifyTimestamp;

public byte[] fullObject;
}
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@ public class QAssignment<OR extends MObject> extends QContainer<MAssignment, OR>
public static final ColumnMetadata MODIFY_TIMESTAMP =
ColumnMetadata.named("modifyTimestamp").ofType(Types.TIMESTAMP_WITH_TIMEZONE);

public static final ColumnMetadata FULL_OBJECT =
ColumnMetadata.named("fullObject").ofType(Types.BINARY);

// attributes

public final EnumPath<MObjectType> ownerType =
Expand Down Expand Up @@ -193,6 +196,8 @@ public class QAssignment<OR extends MObject> extends QContainer<MAssignment, OR>
public final DateTimePath<Instant> modifyTimestamp =
createInstant("modifyTimestamp", MODIFY_TIMESTAMP);

public final ArrayPath<byte[], Byte> fullObject = createByteArray("fullObject", FULL_OBJECT);

public QAssignment(String variable) {
this(variable, DEFAULT_SCHEMA_NAME, TABLE_NAME);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@

import java.util.Objects;

import com.evolveum.midpoint.prism.PrismContainer;
import com.evolveum.midpoint.prism.PrismContainerValue;
import com.evolveum.midpoint.repo.sqale.update.SqaleUpdateContext;
import com.evolveum.midpoint.util.exception.SchemaException;

import com.evolveum.midpoint.xml.ns._public.common.common_3.*;

import org.jetbrains.annotations.NotNull;

import com.evolveum.midpoint.prism.PrismConstants;
Expand All @@ -26,10 +33,6 @@
import com.evolveum.midpoint.repo.sqlbase.JdbcSession;
import com.evolveum.midpoint.repo.sqlbase.mapping.TableRelationResolver;
import com.evolveum.midpoint.util.MiscUtil;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ActivationType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ConstructionType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.MetadataType;

/**
* Mapping between {@link QAssignment} and {@link AssignmentType}.
Expand Down Expand Up @@ -179,72 +182,11 @@ private QAssignmentMapping(
}

@Override
public AssignmentType toSchemaObject(MAssignment row) {
// TODO is there any place we can put row.ownerOid reasonably?
// repositoryContext().prismContext().itemFactory().createObject(... definition?)
// assignment.asPrismContainerValue().setParent(new ObjectType().oid(own)); abstract not possible
// For assignments we can use ownerType, but this is not general for all containers.
// Inspiration: com.evolveum.midpoint.repo.sql.helpers.CertificationCaseHelper.updateLoadedCertificationCase
// (if even possible with abstract type definition)
AssignmentType assignment = new AssignmentType()
.id(row.cid)
.lifecycleState(row.lifecycleState)
.order(row.orderValue)
.orgRef(objectReference(row.orgRefTargetOid,
row.orgRefTargetType, row.orgRefRelationId))
.targetRef(objectReference(row.targetRefTargetOid,
row.targetRefTargetType, row.targetRefRelationId))
.tenantRef(objectReference(row.tenantRefTargetOid,
row.tenantRefTargetType, row.tenantRefRelationId));

// TODO ext... wouldn't serialized fullObject part of the assignment be better after all?

if (row.policySituations != null) {
for (Integer policySituationId : row.policySituations) {
assignment.policySituation(resolveIdToUri(policySituationId));
}
}
if (row.subtypes != null) {
for (String subtype : row.subtypes) {
assignment.subtype(subtype);
}
}

if (row.resourceRefTargetOid != null) {
assignment.construction(new ConstructionType()
.resourceRef(objectReference(row.resourceRefTargetOid,
row.resourceRefTargetType, row.resourceRefRelationId)));
}

ActivationType activation = new ActivationType()
.administrativeStatus(row.administrativeStatus)
.effectiveStatus(row.effectiveStatus)
.enableTimestamp(asXMLGregorianCalendar(row.enableTimestamp))
.disableTimestamp(asXMLGregorianCalendar(row.disableTimestamp))
.disableReason(row.disableReason)
.validityStatus(row.validityStatus)
.validFrom(asXMLGregorianCalendar(row.validFrom))
.validTo(asXMLGregorianCalendar(row.validTo))
.validityChangeTimestamp(asXMLGregorianCalendar(row.validityChangeTimestamp))
.archiveTimestamp(asXMLGregorianCalendar(row.archiveTimestamp));
if (!activation.asPrismContainerValue().isEmpty()) {
assignment.activation(activation);
}

MetadataType metadata = new MetadataType()
.creatorRef(objectReference(row.creatorRefTargetOid,
row.creatorRefTargetType, row.creatorRefRelationId))
.createChannel(resolveIdToUri(row.createChannelId))
.createTimestamp(asXMLGregorianCalendar(row.createTimestamp))
.modifierRef(objectReference(row.modifierRefTargetOid,
row.modifierRefTargetType, row.modifierRefRelationId))
.modifyChannel(resolveIdToUri(row.modifyChannelId))
.modifyTimestamp(asXMLGregorianCalendar(row.modifyTimestamp));
if (!metadata.asPrismContainerValue().isEmpty()) {
assignment.metadata(metadata);
}

return assignment;
public AssignmentType toSchemaObject(MAssignment row) throws SchemaException {
return parseSchemaObject(
row.fullObject,
"assignment for " + row.ownerOid + "," + row.cid,
AssignmentType.class);
}

@Override
Expand All @@ -266,13 +208,16 @@ public MAssignment newRowObject(OR ownerRow) {
row.ownerType = ownerRow.objectType;
return row;
}

// about duplication see the comment in QObjectMapping.toRowObjectWithoutFullObject
@SuppressWarnings("DuplicatedCode")
@Override
public MAssignment insert(AssignmentType assignment, OR ownerRow, JdbcSession jdbcSession) {
public MAssignment insert(AssignmentType assignment, OR ownerRow, JdbcSession jdbcSession) throws SchemaException {
MAssignment row = initRowObject(assignment, ownerRow);

// Insert full Object here
row.fullObject = createFullObject(assignment);


row.lifecycleState = assignment.getLifecycleState();
row.orderValue = assignment.getOrder();
setReference(assignment.getOrgRef(),
Expand Down Expand Up @@ -343,4 +288,17 @@ public MAssignment insert(AssignmentType assignment, OR ownerRow, JdbcSession jd

return row;
}

@Override
public void afterModify(SqaleUpdateContext<AssignmentType, QAssignment<OR>, MAssignment> updateContext) throws SchemaException {
// insert fullObject here
PrismContainer<AssignmentType> identityContainer =
updateContext.findValueOrItem(FocusType.F_ASSIGNMENT);
// row in context already knows its CID
PrismContainerValue<AssignmentType> pcv = identityContainer.findValue(updateContext.row().cid);
byte[] fullObject = createFullObject(pcv.asContainerable());
updateContext.set(updateContext.entityPath().fullObject, fullObject);


}
}
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ public R newRowObject() {

@Override
protected PathSet fullObjectItemsToSkip() {
return PathSet.of(F_JPEG_PHOTO, PATH_FOCUS_IDENTITY, PATH_FOCUS_NORMALIZED_DATA);
return PathSet.of(F_JPEG_PHOTO, PATH_FOCUS_IDENTITY, PATH_FOCUS_NORMALIZED_DATA, F_ASSIGNMENT);
}

@SuppressWarnings("DuplicatedCode") // activation code duplicated with assignment
Expand Down Expand Up @@ -229,7 +229,6 @@ public S toSchemaObject(@NotNull Tuple row, @NotNull Q entityPath, @NotNull Jdbc
} else if (SelectorOptions.hasToFetchPathNotRetrievedByDefault(F_JPEG_PHOTO, options)) {
PrismUtil.setPropertyNullAndComplete(focus.asPrismObject(), F_JPEG_PHOTO);
}

if (SelectorOptions.hasToFetchPathNotRetrievedByDefault(PATH_FOCUS_NORMALIZED_DATA, options)) {
loadFocusIdentitiesNormalizedData(row.get(entityPath.normalizedData), focus);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,30 @@
*/
package com.evolveum.midpoint.repo.sqale.qmodel.object;

import static com.evolveum.midpoint.schema.constants.SchemaConstants.PATH_FOCUS_IDENTITY;
import static com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentHolderType.*;

import java.util.Collection;
import java.util.List;
import java.util.Objects;

import com.evolveum.midpoint.prism.Containerable;
import com.evolveum.midpoint.prism.PrismContainer;
import com.evolveum.midpoint.prism.path.PathSet;

import com.evolveum.midpoint.repo.sqale.SqaleUtils;
import com.evolveum.midpoint.repo.sqale.qmodel.assignment.MAssignment;
import com.evolveum.midpoint.repo.sqale.qmodel.assignment.QAssignment;
import com.evolveum.midpoint.repo.sqale.qmodel.focus.MFocus;
import com.evolveum.midpoint.repo.sqale.qmodel.focus.MFocusIdentity;
import com.evolveum.midpoint.repo.sqale.qmodel.focus.QFocusIdentity;
import com.evolveum.midpoint.repo.sqale.qmodel.focus.QFocusIdentityMapping;
import com.evolveum.midpoint.schema.GetOperationOptions;
import com.evolveum.midpoint.schema.SelectorOptions;

import com.evolveum.midpoint.xml.ns._public.common.common_3.FocusIdentityType;

import com.querydsl.core.Tuple;
import org.jetbrains.annotations.NotNull;

import com.evolveum.midpoint.repo.sqale.SqaleRepoContext;
Expand Down Expand Up @@ -75,17 +94,60 @@ protected Q newAliasInstance(String alias) {
return (Q) new QAssignmentHolder<>(MObject.class, alias);
}

@Override
protected PathSet fullObjectItemsToSkip() {
return PathSet.of(F_ASSIGNMENT);
}

public S toSchemaObject(@NotNull Tuple row, @NotNull Q entityPath, @NotNull JdbcSession jdbcSession,
Collection<SelectorOptions<GetOperationOptions>> options) throws SchemaException {
S holder = super.toSchemaObject(row, entityPath, jdbcSession, options);

var includeAssignments = true;
if (includeAssignments) {
loadAssignments(holder, jdbcSession);
}
return holder;
}

private void loadAssignments(S focus, JdbcSession jdbcSession) throws SchemaException {
// Currently we don't consider container ids and load all identities/identity values.
// FIXME: This should be probably API concept now - something like fetchNestedFullObjects
QAssignmentMapping<R> mapping = QAssignmentMapping.getAssignmentMapping();
QAssignment<R> q = mapping.defaultAlias();
var query = jdbcSession.newQuery()
.from(q)
.select(q) // no complications here, we load it whole
.where(q.ownerOid.eq(SqaleUtils.oidToUuid(focus.getOid())));
for (MAssignment row : query.fetch()) {
// Logic here should be probably if assignment have fullObject and assignments

// FIXME: Migration needs testing for such scenarios
// We have fullObject with assignments inlined
// Object is readed, one assignment is modified
// All assignments should have full object present / legacy assignments should be kept
if (row.fullObject != null) {
focus.assignment(mapping.toSchemaObject(row));
}
}
// Setting "complete" for multi-value containers is quite verbose.
PrismContainer<Containerable> identityContainer = focus.asPrismObject().findContainer(F_ASSIGNMENT);
if (identityContainer != null) {
identityContainer.setIncomplete(false);
}
}

@Override
public void storeRelatedEntities(
@NotNull R row, @NotNull S schemaObject, @NotNull JdbcSession jdbcSession) throws SchemaException {
super.storeRelatedEntities(row, schemaObject, jdbcSession);

List<AssignmentType> assignments = schemaObject.getAssignment();
if (!assignments.isEmpty()) {
assignments.forEach(assignment ->
QAssignmentMapping.getAssignmentMapping().insert(assignment, row, jdbcSession));
for (var assignment : assignments) {
QAssignmentMapping.getAssignmentMapping().insert(assignment, row, jdbcSession);
}
}

storeRefs(row, schemaObject.getArchetypeRef(),
QObjectReferenceMapping.getForArchetype(), jdbcSession);
storeRefs(row, schemaObject.getDelegatedRef(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,11 @@ public void storeRelatedEntities(
@NotNull R row, @NotNull S schemaObject, @NotNull JdbcSession jdbcSession) throws SchemaException {
super.storeRelatedEntities(row, schemaObject, jdbcSession);

List<AssignmentType> inducement = schemaObject.getInducement();
if (!inducement.isEmpty()) {
inducement.forEach(assignment ->
QAssignmentMapping.getInducementMapping().insert(assignment, row, jdbcSession));
List<AssignmentType> inducements = schemaObject.getInducement();
if (!inducements.isEmpty()) {
for (var inducement : inducements) {
QAssignmentMapping.getInducementMapping().insert(inducement, row, jdbcSession);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3652,7 +3652,8 @@ private void assertPolicySituationFound(String situation, int count, OperationRe
assertEquals(found.size(), count, "Found situation count does not match.");
}

@Test
// This test assumes assignments are in full object and separate table at same time
@Test(enabled = false)
public void test952ReindexFixingColumnsOutOfSync()
throws SchemaException, ObjectNotFoundException, ObjectAlreadyExistsException {
OperationResult result = createOperationResult();
Expand Down Expand Up @@ -3745,7 +3746,8 @@ public void test952ReindexFixingColumnsOutOfSync()
assertThat(assRows.get(0).orderValue).isEqualTo(1);
}

@Test
// This test assumes assignments are in full object and separete table at same time
@Test(enabled = false)
public void test955ReindexOfShadowWithAttributes() throws Exception {
OperationResult result = createOperationResult();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,8 @@ public void test400SqlLogger() throws Exception {
assertThat(users).isNotEmpty();

queryBuffer = queryRecorder.getQueryBuffer();
assertThat(queryBuffer).hasSize(1);
// 1 query if assignments are inlined, 11 if assignments are separate table
assertThat(queryBuffer).hasSize(11);
entry = queryBuffer.remove();
assertThat(entry.sql).startsWith("select u.oid, u.fullObject");
}
Expand Down Expand Up @@ -528,7 +529,8 @@ public void test500ExecuteQueryDiagnostics() throws Exception {
+ " where u.nameNorm = ? and u.nameOrig = ? and u.administrativeStatus = ?"
+ " limit ?");

assertThat(queryRecorder.getQueryBuffer()).hasSize(1);
// assignments are in separate table, so 2 queries
assertThat(queryRecorder.getQueryBuffer()).hasSize(2);
}

@Test
Expand Down

0 comments on commit 459db99

Please sign in to comment.