Skip to content

Commit

Permalink
repo-sqale: added modify support for array columns
Browse files Browse the repository at this point in the history
Added ArrayItemDeltaProcessor to set array column.
SqaleUpdateContext#findItem(path) returns item from root prism object;
used to obtain new value (modification is applied before processor).
Added missing mapping for assignment/policySituations.
  • Loading branch information
virgo47 committed May 27, 2021
1 parent 4bd20fe commit 00747ea
Show file tree
Hide file tree
Showing 12 changed files with 360 additions and 15 deletions.
Expand Up @@ -20,6 +20,7 @@
import com.evolveum.midpoint.repo.sqlbase.mapping.ItemRelationResolver;
import com.evolveum.midpoint.repo.sqlbase.mapping.ItemSqlMapper;
import com.evolveum.midpoint.repo.sqlbase.mapping.QueryModelMapping;
import com.evolveum.midpoint.util.exception.SchemaException;

/**
* This is default item delta processor that decides what to do with the modification.
Expand All @@ -40,7 +41,7 @@ public DelegatingItemDeltaProcessor(SqaleUpdateContext<?, ?, ?> context) {
}

@Override
public void process(ItemDelta<?, ?> modification) throws RepositoryException {
public void process(ItemDelta<?, ?> modification) throws RepositoryException, SchemaException {
QName itemName = resolvePath(modification.getPath());
if (itemName == null) {
// This may indicate forgotten mapping, but normally it means that the item is simply
Expand Down
Expand Up @@ -8,6 +8,7 @@

import com.evolveum.midpoint.prism.delta.ItemDelta;
import com.evolveum.midpoint.repo.sqlbase.RepositoryException;
import com.evolveum.midpoint.util.exception.SchemaException;

/**
* Essential contract for processing item delta modifications.
Expand All @@ -16,5 +17,5 @@
*/
public interface ItemDeltaProcessor {

void process(ItemDelta<?, ?> modification) throws RepositoryException;
void process(ItemDelta<?, ?> modification) throws RepositoryException, SchemaException;
}
Expand Up @@ -15,6 +15,7 @@
import com.evolveum.midpoint.prism.delta.ItemDelta;
import com.evolveum.midpoint.repo.sqale.update.SqaleUpdateContext;
import com.evolveum.midpoint.repo.sqlbase.RepositoryException;
import com.evolveum.midpoint.util.exception.SchemaException;

/**
* Applies item delta values to an item and arranges necessary SQL changes using update context.
Expand All @@ -39,9 +40,14 @@ protected ItemDeltaValueProcessor(SqaleUpdateContext<?, ?, ?> context) {
this.context = context;
}

/** Default process implementation, most generic case covering especially multi-values. */
/**
* Default process implementation, most generic case covering especially multi-values
* stored in separate rows.
* This works when implementations of {@link #deleteRealValues} and {@link #addRealValues}
* are independent, it's not usable for array update where a single `SET` clause is allowed.
*/
@Override
public void process(ItemDelta<?, ?> modification) throws RepositoryException {
public void process(ItemDelta<?, ?> modification) throws RepositoryException, SchemaException {
if (modification.isReplace()) {
setRealValues(modification.getRealValuesToReplace());
return;
Expand Down
@@ -0,0 +1,88 @@
/*
* Copyright (C) 2010-2021 Evolveum and contributors
*
* This work is dual-licensed under the Apache License 2.0
* and European Union Public License. See LICENSE file for details.
*/
package com.evolveum.midpoint.repo.sqale.delta.item;

import java.lang.reflect.Array;
import java.util.Collection;
import java.util.function.Function;
import java.util.function.IntFunction;

import com.querydsl.core.types.dsl.ArrayPath;
import org.jetbrains.annotations.Nullable;

import com.evolveum.midpoint.prism.Item;
import com.evolveum.midpoint.prism.PrismValue;
import com.evolveum.midpoint.prism.delta.ItemDelta;
import com.evolveum.midpoint.repo.sqale.delta.ItemDeltaValueProcessor;
import com.evolveum.midpoint.repo.sqale.update.SqaleUpdateContext;
import com.evolveum.midpoint.repo.sqlbase.RepositoryException;
import com.evolveum.midpoint.repo.sqlbase.querydsl.FlexibleRelationalPathBase;
import com.evolveum.midpoint.util.exception.SchemaException;

/**
* Filter processor for multi-value property represented by single array column.
* These paths support only value equality (of any value), which is "contains" in DB terminology.
* Our filter "contains" (meaning substring) is *not* supported.
*
* @param <V> type of value in schema
* @param <E> type of element in DB (can be the same like `V`)
*/
public class ArrayItemDeltaProcessor<V, E> extends ItemDeltaValueProcessor<E> {

private final ArrayPath<E[], E> path;
private final Class<E> elementType;
@Nullable private final Function<V, E> conversionFunction;

/**
* @param <Q> entity query type from which the attribute is resolved
* @param <R> row type related to {@link Q}
* @param elementType class of {@link E} necessary for array creation
* @param conversionFunction optional conversion function, can be null if no conversion is necessary
*/
public <Q extends FlexibleRelationalPathBase<R>, R> ArrayItemDeltaProcessor(
SqaleUpdateContext<?, Q, R> context,
Function<Q, ArrayPath<E[], E>> rootToQueryItem,
Class<E> elementType,
@Nullable Function<V, E> conversionFunction) {
super(context);
this.path = rootToQueryItem.apply(context.path());
this.elementType = elementType;
this.conversionFunction = conversionFunction;
}

@Override
public void process(ItemDelta<?, ?> modification) throws RepositoryException, SchemaException {
Item<PrismValue, ?> item = context.findItem(modification.getPath());
Collection<?> realValues = item != null ? item.getRealValues() : null;

if (realValues == null || realValues.isEmpty()) {
delete();
} else {
// Whatever the operation is, we just set the new value here.
setRealValues(realValues);
}
}

@Override
public void setRealValues(Collection<?> values) {
//noinspection unchecked
IntFunction<E[]> arrayConstructor = i -> (E[]) Array.newInstance(elementType, i);

// valueArray can't be just Object[], it must be concrete type, e.g. String[],
// otherwise PG JDBC driver will complain.
//noinspection SuspiciousToArrayCall,unchecked
E[] valueArray = conversionFunction != null
? ((Collection<V>) values).stream().map(conversionFunction).toArray(arrayConstructor)
: values.toArray(arrayConstructor);
context.set(path, valueArray);
}

@Override
public void delete() {
context.set(path, null);
}
}
Expand Up @@ -21,6 +21,8 @@
*
* This hierarchy branch should not need {@link #addRealValues} and {@link #deleteRealValues},
* so it's not overridden and throws {@link UnsupportedOperationException}.
*
* @param <T> expected type of the real value for the modification (after optional conversion)
*/
public abstract class ItemDeltaSingleValueProcessor<T> extends ItemDeltaValueProcessor<T> {

Expand Down
Expand Up @@ -14,6 +14,8 @@
import com.evolveum.midpoint.repo.sqlbase.querydsl.FlexibleRelationalPathBase;

/**
* Processor for items represented by a single column (query path).
*
* @param <T> type of real value after optional conversion ({@link #convertRealValue(Object)}
* to match the column (attribute) type in the row bean (M-type)
* @param <P> type of the corresponding path in the Q-type
Expand Down
Expand Up @@ -27,6 +27,9 @@
* Filter processor for multi-value property represented by single array column.
* These paths support only value equality (of any value), which is "contains" in DB terminology.
* Our filter "contains" (meaning substring) is *not* supported.
*
* @param <T> type of value in schema
* @param <E> type of element in DB (can be the same like `T`)
*/
public class ArrayPathItemFilterProcessor<T, E>
extends SinglePathItemFilterProcessor<T, ArrayPath<E[], E>> {
Expand All @@ -39,6 +42,8 @@ public class ArrayPathItemFilterProcessor<T, E>
* Creates filter processor for array column.
*
* @param dbType name of the type for element in DB (without []) for the cast part of the condition
* @param elementType class of {@link E} necessary for array creation
* @param conversionFunction optional conversion function, can be null if no conversion is necessary
*/
public <Q extends FlexibleRelationalPathBase<R>, R> ArrayPathItemFilterProcessor(
SqlQueryContext<?, Q, R> context,
Expand Down
Expand Up @@ -221,11 +221,12 @@ protected <MS> SqaleItemSqlMapper<MS, Q, R> multiStringMapper(
return new SqaleItemSqlMapper<>(
ctx -> new ArrayPathItemFilterProcessor<String, String>(
ctx, rootToQueryItem, "TEXT", String.class, null),
ctx -> null); // TODO
ctx -> new ArrayItemDeltaProcessor<String, String>(
ctx, rootToQueryItem, String.class, null));
}

/**
* Returns the mapper creating integer multi-value filter/delta processors from context.
* Returns the mapper creating cached URI multi-value filter/delta processors from context.
*
* @param <MS> mapped schema type, see javadoc in {@link QueryTableMapping}
*/
Expand All @@ -235,7 +236,8 @@ protected <MS> SqaleItemSqlMapper<MS, Q, R> multiUriMapper(
ctx -> new ArrayPathItemFilterProcessor<>(
ctx, rootToQueryItem, "INTEGER", Integer.class,
((SqaleRepoContext) ctx.repositoryContext())::searchCachedUriId),
ctx -> null); // TODO
ctx -> new ArrayItemDeltaProcessor<>(ctx, rootToQueryItem, Integer.class,
((SqaleRepoContext) ctx.repositoryContext())::processCacheableUri));
}

@Override
Expand Down
Expand Up @@ -98,6 +98,8 @@ private QAssignmentMapping(
q -> q.tenantRefTargetOid,
q -> q.tenantRefTargetType,
q -> q.tenantRefRelationId));
addItemMapping(F_POLICY_SITUATION, multiUriMapper(q -> q.policySituations));

// TODO no idea how extId/Oid works, see RAssignment.getExtension
// TODO ext mapping can't be done statically
addNestedMapping(F_CONSTRUCTION, ConstructionType.class)
Expand Down
Expand Up @@ -13,10 +13,12 @@

import com.querydsl.core.types.Path;
import com.querydsl.sql.dml.SQLUpdateClause;
import org.jetbrains.annotations.NotNull;

import com.evolveum.midpoint.prism.*;
import com.evolveum.midpoint.prism.delta.ItemDelta;
import com.evolveum.midpoint.prism.equivalence.EquivalenceStrategy;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.repo.sqale.ContainerValueIdGenerator;
import com.evolveum.midpoint.repo.sqale.SqaleRepoContext;
import com.evolveum.midpoint.repo.sqale.SqaleUtils;
Expand All @@ -26,7 +28,7 @@
import com.evolveum.midpoint.repo.sqale.qmodel.object.QObjectMapping;
import com.evolveum.midpoint.repo.sqlbase.JdbcSession;
import com.evolveum.midpoint.repo.sqlbase.RepositoryException;
import com.evolveum.midpoint.repo.sqlbase.mapping.QueryTableMapping;
import com.evolveum.midpoint.repo.sqlbase.mapping.QueryModelMapping;
import com.evolveum.midpoint.util.DebugUtil;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType;
Expand Down Expand Up @@ -68,7 +70,7 @@ public Q path() {
}

@Override
public QueryTableMapping<S, Q, R> mapping() {
public QueryModelMapping<S, Q, R> mapping() {
return mapping;
}

Expand Down Expand Up @@ -156,6 +158,11 @@ protected void finishExecutionOwn() throws SchemaException, RepositoryException
}
}

@Override
public <V extends PrismValue> Item<V, ?> findItem(@NotNull ItemPath path) {
return object.asPrismObject().findItem(path);
}

public SQLUpdateClause update() {
return update;
}
Expand Down
Expand Up @@ -10,7 +10,10 @@
import java.util.Map;

import com.querydsl.core.types.Path;
import org.jetbrains.annotations.NotNull;

import com.evolveum.midpoint.prism.Item;
import com.evolveum.midpoint.prism.PrismValue;
import com.evolveum.midpoint.prism.path.ItemName;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.repo.sqale.SqaleRepoContext;
Expand Down Expand Up @@ -149,4 +152,12 @@ protected final void finishExecution() throws SchemaException, RepositoryExcepti
}

protected abstract void finishExecutionOwn() throws SchemaException, RepositoryException;

public <V extends PrismValue> Item<V, ?> findItem(@NotNull ItemPath path) {
if (parentContext == null) {
throw new UnsupportedOperationException(
"findItem() is unsupported on non-root update context");
}
return parentContext.findItem(path);
}
}

0 comments on commit 00747ea

Please sign in to comment.