Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
mederly committed Apr 16, 2021
2 parents 06f211b + 85fd5bf commit f9e31bc
Show file tree
Hide file tree
Showing 19 changed files with 280 additions and 158 deletions.
Expand Up @@ -14,6 +14,7 @@

import com.google.common.base.Strings;
import com.querydsl.core.Tuple;
import com.querydsl.sql.SQLQuery;
import org.apache.commons.lang3.Validate;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
Expand Down Expand Up @@ -123,8 +124,8 @@ public SqaleRepositoryService(
try (JdbcSession jdbcSession =
sqlRepoContext.newJdbcSession().startReadOnlyTransaction()) {
//noinspection unchecked
object = (PrismObject<T>) readByOid(
jdbcSession, type, oidUuid, options).asPrismObject();
object = (PrismObject<T>) readByOid(jdbcSession, type, oidUuid, options)
.asPrismObject();
}

// "objectLocal" is here just to provide effectively final variable for the lambda below
Expand Down Expand Up @@ -164,14 +165,40 @@ private <S extends ObjectType, Q extends QObject<R>, R extends MObject> S readBy
@NotNull UUID oid,
Collection<SelectorOptions<GetOperationOptions>> options)
throws SchemaException, ObjectNotFoundException {
return readByOidInternal(jdbcSession, schemaType, oid, options, false);
}

/** Read object using provided {@link JdbcSession} and locks it for update. */
private <S extends ObjectType, Q extends QObject<R>, R extends MObject> S readByOidAndLock(
@NotNull JdbcSession jdbcSession,
@NotNull Class<S> schemaType,
@NotNull UUID oid,
Collection<SelectorOptions<GetOperationOptions>> options)
throws SchemaException, ObjectNotFoundException {
return readByOidInternal(jdbcSession, schemaType, oid, options, true);
}

// use one of above to avoid ugly last boolean parameter
private <S extends ObjectType, Q extends QObject<R>, R extends MObject> S readByOidInternal(
@NotNull JdbcSession jdbcSession,
@NotNull Class<S> schemaType,
@NotNull UUID oid,
Collection<SelectorOptions<GetOperationOptions>> options,
boolean forUpdate)
throws SchemaException, ObjectNotFoundException {

// context.processOptions(options); TODO how to process option, is setting of select expressions enough?

SqaleTableMapping<S, Q, R> rootMapping =
sqlRepoContext.getMappingBySchemaType(schemaType);
Q root = rootMapping.defaultAlias();

Tuple result = sqlRepoContext.newQuery(jdbcSession.connection())
SQLQuery<?> sqlQuery = sqlRepoContext.newQuery(jdbcSession.connection());
if (forUpdate) {
sqlQuery.forUpdate();
}

Tuple result = sqlQuery
.from(root)
.select(rootMapping.selectExpressions(root, options))
.where(root.oid.eq(oid))
Expand Down Expand Up @@ -358,7 +385,7 @@ public <T extends ObjectType> ModifyObjectResult<T> modifyObject(

try (JdbcSession jdbcSession = sqlRepoContext.newJdbcSession().startTransaction()) {
GetOperationOptionsBuilder getOptionsBuilder = schemaService.getOperationOptionsBuilder();
T object = readByOid(jdbcSession, type, oidUuid, getOptionsBuilder.build());
T object = readByOidAndLock(jdbcSession, type, oidUuid, getOptionsBuilder.build());
//noinspection unchecked
PrismObject<T> prismObject = (PrismObject<T>) object.asPrismObject();
if (precondition != null && !precondition.holds(prismObject)) {
Expand Down
Expand Up @@ -4,99 +4,88 @@
* 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.qmodel;
package com.evolveum.midpoint.repo.sqale.delta;

import java.util.Map;
import javax.xml.namespace.QName;

import com.evolveum.midpoint.prism.Containerable;
import com.evolveum.midpoint.prism.Item;
import com.evolveum.midpoint.prism.PrismContainerValue;
import com.evolveum.midpoint.prism.delta.ItemDelta;
import com.evolveum.midpoint.repo.sqale.SqaleUpdateContext;
import com.evolveum.midpoint.repo.sqale.delta.item.ItemDeltaValueProcessor;
import com.evolveum.midpoint.repo.sqale.delta.item.ItemDeltaSingleValueProcessor;
import com.evolveum.midpoint.repo.sqale.mapping.SqaleItemSqlMapper;
import com.evolveum.midpoint.repo.sqlbase.RepositoryException;
import com.evolveum.midpoint.repo.sqale.qmodel.SqaleNestedMapping;
import com.evolveum.midpoint.repo.sqlbase.mapping.ItemSqlMapper;

/**
* Processor for embedded containers.
* Due to stateful nature of processing call either {@link #process(ItemDelta)}
* or {@link #delete()} and call it only once - *do not reuse this processor instance*.
*/
public class EmbeddedContainerDeltaProcessor<T extends Containerable>
extends ItemDeltaValueProcessor<T> {
extends ItemDeltaSingleValueProcessor<T> {

private final SqaleNestedMapping<?, ?, ?> mapping;
private final Map<QName, ItemSqlMapper> mappers; // iterated in a stateful manner

public EmbeddedContainerDeltaProcessor(
SqaleUpdateContext<?, ?, ?> context, SqaleNestedMapping<?, ?, ?> nestedMapping) {
super(context);
this.mapping = nestedMapping;
mappers = mapping.getItemMappings();
}

@Override
public void process(ItemDelta<?, ?> modification) throws RepositoryException {
if (!modification.isDelete()) {
setValue(getAnyValue(modification));
}

// we want to delete values for all mapper unused until now
delete();
}

/**
* Sets the values for items in the PCV that are mapped to database columns.
* Removes entries for used items from the {@link #mappers}.
* Sets the values for items in the PCV that are mapped to database columns and nulls the rest.
*/
public void setValue(T value) {
PrismContainerValue<T> pcv = Containerable.asPrismContainerValue(value);
if (pcv == null) {
delete(); // we need to clear existing values
return;
}

Map<QName, ItemSqlMapper> mappers = mapping.getItemMappings();
for (Item<?, ?> item : pcv.getItems()) {
ItemSqlMapper mapper = mappers.remove(item.getElementName());
if (mapper == null) {
continue; // ok, not mapped to database
}

if (!(mapper instanceof SqaleItemSqlMapper)) {
throw new IllegalArgumentException("No delta processor available for " + mapper
+ " in mapping " + mapping + "! (Only query mapping is available.)");
}
SqaleItemSqlMapper sqaleMapper = (SqaleItemSqlMapper) mapper;
ItemDeltaValueProcessor<?> processor = sqaleMapper.createItemDeltaProcessor(context);
ItemDeltaValueProcessor<?> processor = createItemDeltaProcessor(mapper);
// TODO remove: this should not happen when all mapper types are covered (e.g. ref tables)
if (processor == null) {
// TODO this should not happen when all mapper types are covered (e.g. ref tables)
System.out.println("PROCESSOR NULL for: " + sqaleMapper);
return;
System.out.println("PROCESSOR NULL for: " + mapper);
continue;
}
processor.setRealValue(item.getAnyValue().getRealValue());
// while the embedded container is single-value, its items may be multi-value
processor.setRealValues(item.getRealValues());
}

// clear the rest of the values
mappers.values().forEach(this::deleteUsing);
}

@Override
public void delete() {
for (ItemSqlMapper mapper : mappers.values()) {
for (ItemSqlMapper mapper : mapping.getItemMappings().values()) {
deleteUsing(mapper);
}
}

private void deleteUsing(ItemSqlMapper mapper) {
ItemDeltaValueProcessor<?> processor = createItemDeltaProcessor(mapper);
// TODO remove: this should not happen when all mapper types are covered (e.g. ref tables)
if (processor == null) {
System.out.println("PROCESSOR NULL for: " + mapper);
return;
}
processor.delete();
}

private ItemDeltaValueProcessor<?> createItemDeltaProcessor(ItemSqlMapper mapper) {
if (!(mapper instanceof SqaleItemSqlMapper)) {
throw new IllegalArgumentException("No delta processor available for " + mapper
+ " in mapping " + mapping + "! (Only query mapping is available.)");
}
SqaleItemSqlMapper sqaleMapper = (SqaleItemSqlMapper) mapper;
ItemDeltaValueProcessor<?> processor = sqaleMapper.createItemDeltaProcessor(context);
if (processor == null) {
// TODO this should not happen when all mapper types are covered (e.g. ref tables)
System.out.println("PROCESSOR NULL for: " + sqaleMapper);
return;
}
processor.delete();
return sqaleMapper.createItemDeltaProcessor(context);
}
}
Expand Up @@ -9,6 +9,11 @@
import com.evolveum.midpoint.prism.delta.ItemDelta;
import com.evolveum.midpoint.repo.sqlbase.RepositoryException;

/**
* Essential contract for processing item delta modifications.
* There are two basic subtypes, {@link DelegatingItemDeltaProcessor} taking care of the path
* and then various subtypes of {@link ItemDeltaValueProcessor} for processing the value changes.
*/
public interface ItemDeltaProcessor {

void process(ItemDelta<?, ?> modification) throws RepositoryException;
Expand Down
@@ -0,0 +1,39 @@
/*
* 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;

import java.util.Collection;

import org.jetbrains.annotations.Nullable;

/**
* Applies item delta values to an item and arranges necessary SQL changes using update context.
* This typically means adding set clauses to the update but can also mean adding rows
* for containers, etc.
* This kind of item delta processor does not resolve multi-part item paths, see other
* subclasses of {@link ItemDeltaProcessor} for that.
*
* @param <T> expected type of the real value for the modification (after optional conversion)
*/
public interface ItemDeltaValueProcessor<T> extends ItemDeltaProcessor {

/** Default conversion for one value is a mere type cast, override as necessary. */
@Nullable
default T convertRealValue(Object realValue) {
//noinspection unchecked
return (T) realValue;
}

/**
* Sets the database columns to reflect the provided real values (may be conversion).
* This is a general case covering both multi-value and single-value items.
*/
void setRealValues(Collection<?> value);

/** Resets the database columns, exposed for the needs of container processing. */
void delete();
}
Expand Up @@ -10,7 +10,6 @@

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

import com.evolveum.midpoint.repo.sqale.SqaleUpdateContext;

Expand All @@ -25,10 +24,4 @@ public EnumItemDeltaProcessor(SqaleUpdateContext<?, ?, ?> context,
Function<EntityPath<?>, EnumPath<E>> rootToQueryItem) {
super(context, rootToQueryItem);
}

@Override
protected @Nullable E transformRealValue(Object realValue) {
//noinspection unchecked
return (E) realValue;
}
}
@@ -0,0 +1,67 @@
/*
* 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.util.Collection;

import com.evolveum.midpoint.prism.PrismValue;
import com.evolveum.midpoint.prism.delta.ItemDelta;
import com.evolveum.midpoint.repo.sqale.SqaleUpdateContext;
import com.evolveum.midpoint.repo.sqale.delta.ItemDeltaValueProcessor;
import com.evolveum.midpoint.repo.sqlbase.RepositoryException;

/**
* Applies single item delta value to an item.
* This class implements {@link #setRealValues} (for multiple values) and requires
* {@link #setValue} (for a single value) to be implemented by the subclasses.
*/
public abstract class ItemDeltaSingleValueProcessor<T> implements ItemDeltaValueProcessor<T> {

protected final SqaleUpdateContext<?, ?, ?> context;

protected ItemDeltaSingleValueProcessor(SqaleUpdateContext<?, ?, ?> context) {
this.context = context;
}

@Override
public void process(ItemDelta<?, ?> modification) throws RepositoryException {
T value = getAnyValue(modification);

if (modification.isDelete() || value == null) {
// Repo does not check deleted value for single-value properties.
// This should be handled already by narrowing the modifications.
delete();
} else {
// We treat add and replace the same way for single-value properties.
setValue(value);
}
}

/**
* Often the single real value is necessary, optionally converted using
* {@link #convertRealValue(Object)} to get expected type.
* Either method can be overridden or not used at all depending on the complexity
* of the concrete delta processor.
*/
protected T getAnyValue(ItemDelta<?, ?> modification) {
PrismValue anyValue = modification.getAnyValue();
return anyValue != null ? convertRealValue(anyValue.getRealValue()) : null;
}

/** Sets the database columns to reflect the provided value (converted if necessary). */
public abstract void setValue(T value);

@Override
public void setRealValues(Collection<?> values) {
if (values.size() > 1) {
throw new IllegalArgumentException(
"Multiple values when single value is expected: " + values);
}

setValue(values.isEmpty() ? null : convertRealValue(values.iterator().next()));
}
}

This file was deleted.

0 comments on commit f9e31bc

Please sign in to comment.