Skip to content

Commit

Permalink
repo-sqale: TableRelationResolver used via factory method, JOIN support
Browse files Browse the repository at this point in the history
To support ordering by single-value (embedded) ref target attributes
(e.g. creatorRef/@/name) these must be JOINed, not EXIST subquery.
Support to do so is added, of course only for single-value traverses.
  • Loading branch information
virgo47 committed Sep 14, 2021
1 parent 28d568d commit 301eb76
Show file tree
Hide file tree
Showing 11 changed files with 59 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public <TS, TQ extends QObject<TR>, TR extends MObject> EmbeddedReferenceResolve
@NotNull Supplier<QueryTableMapping<TS, TQ, TR>> targetMappingSupplier) {
mapping = new SqaleNestedMapping<>(Referencable.class, queryType);
mapping.addRelationResolver(PrismConstants.T_OBJECT_REFERENCE,
new TableRelationResolver<>(
TableRelationResolver.usingJoin(
targetMappingSupplier, (q, t) -> rootToOidPath.apply(q).eq(t.oid)));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ SqaleMappingMixin<S, Q, R> addRefMapping(
ctx -> new RefTableItemDeltaProcessor<>(ctx, referenceMapping)));

// Needed for queries with ref/@/... paths, this resolves the "ref/" part before @.
addRelationResolver(itemName, new TableRelationResolver<>(
addRelationResolver(itemName, TableRelationResolver.usingSubquery(
referenceMapping, referenceMapping.correlationPredicate()));
return this;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ private QAccessCertificationCaseMapping(@NotNull SqaleRepoContext repositoryCont

addRelationResolver(PrismConstants.T_PARENT,
// mapping supplier is used to avoid cycles in the initialization code
new TableRelationResolver<>(
TableRelationResolver.usingJoin(
QAccessCertificationCampaignMapping::getAccessCertificationCampaignMapping,
(q, p) -> q.ownerOid.eq(p.oid)));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ private QAccessCertificationWorkItemMapping(@NotNull SqaleRepoContext repository

addRelationResolver(PrismConstants.T_PARENT,
// mapping supplier is used to avoid cycles in the initialization code
new TableRelationResolver<>(
TableRelationResolver.usingJoin(
QAccessCertificationCaseMapping::getAccessCertificationCaseMapping,
(q, p) -> q.ownerOid.eq(p.ownerOid)
.and(q.accessCertCaseCid.eq(p.cid))));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ private QAssignmentMapping(

addRelationResolver(PrismConstants.T_PARENT,
// mapping supplier is used to avoid cycles in the initialization code
new TableRelationResolver<>(QAssignmentHolderMapping::getAssignmentHolderMapping,
TableRelationResolver.usingJoin(
QAssignmentHolderMapping::getAssignmentHolderMapping,
// Adding and(q.ownerType.eq(p.objectType) doesn't help the planner.
(q, p) -> q.ownerOid.eq(p.oid)));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ private QCaseWorkItemMapping(@NotNull SqaleRepoContext repositoryContext) {

addRelationResolver(PrismConstants.T_PARENT,
// mapping supplier is used to avoid cycles in the initialization code
new TableRelationResolver<>(
TableRelationResolver.usingJoin(
QCaseMapping::getCaseMapping,
(q, p) -> q.ownerOid.eq(p.oid)));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ private QLookupTableRowMapping(@NotNull SqaleRepoContext repositoryContext) {

addRelationResolver(PrismConstants.T_PARENT,
// mapping supplier is used to avoid cycles in the initialization code
new TableRelationResolver<>(QLookupTableMapping::get,
TableRelationResolver.usingJoin(
QLookupTableMapping::get,
(q, p) -> q.ownerOid.eq(p.oid)));

addItemMapping(F_KEY, stringMapper(q -> q.key));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ private QOperationExecutionMapping(@NotNull SqaleRepoContext repositoryContext)

addRelationResolver(PrismConstants.T_PARENT,
// mapping supplier is used to avoid cycles in the initialization code
new TableRelationResolver<>(QObjectMapping::getObjectMapping,
TableRelationResolver.usingJoin(
QObjectMapping::getObjectMapping,
(q, p) -> q.ownerOid.eq(p.oid)));

addItemMapping(F_STATUS, enumMapper(q -> q.status));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ private QTriggerMapping(@NotNull SqaleRepoContext repositoryContext) {

addRelationResolver(PrismConstants.T_PARENT,
// mapping supplier is used to avoid cycles in the initialization code
new TableRelationResolver<>(QObjectMapping::getObjectMapping,
TableRelationResolver.usingJoin(
QObjectMapping::getObjectMapping,
(q, p) -> q.ownerOid.eq(p.oid)));

addItemMapping(TriggerType.F_HANDLER_URI, uriMapper(q -> q.handlerUriId));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public DefaultItemSqlMapper(
}

@Override
public @Nullable Path<?> itemOrdering(Q root, ItemDefinition<?> itemName) {
public @Nullable Path<?> itemOrdering(Q root, ItemDefinition<?> unused) {
return primaryItemMapping != null ? primaryItemMapping.apply(root) : null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,19 +35,52 @@ public class TableRelationResolver<

protected final Supplier<QueryTableMapping<TS, TQ, TR>> targetMappingSupplier;
protected final BiFunction<Q, TQ, Predicate> correlationPredicate;
private final boolean useSubquery;

public TableRelationResolver(
public static <Q extends FlexibleRelationalPathBase<R>, R, TS, TQ extends FlexibleRelationalPathBase<TR>, TR>
TableRelationResolver<Q, R, TS, TQ, TR> usingSubquery(
@NotNull QueryTableMapping<TS, TQ, TR> targetMapping,
@NotNull BiFunction<Q, TQ, Predicate> correlationPredicate) {
return new TableRelationResolver<>(targetMapping, correlationPredicate);
}

/**
* Currently the decision to use `JOIN` is static in the mapping, but it can be more flexible.
* If the query does not order by such a path, `EXISTS` is more efficient and should be used.
* This would require order examination first and then using this info in {@link #resolve(SqlQueryContext)},
* perhaps accessible via the context parameter.
*/
public static <Q extends FlexibleRelationalPathBase<R>, R, TS, TQ extends FlexibleRelationalPathBase<TR>, TR>
TableRelationResolver<Q, R, TS, TQ, TR> usingJoin(
@NotNull Supplier<QueryTableMapping<TS, TQ, TR>> targetMappingSupplier,
@NotNull BiFunction<Q, TQ, Predicate> correlationPredicate) {
return new TableRelationResolver<>(targetMappingSupplier, correlationPredicate);
}

/**
* Constructor for relation resolver using `EXISTS` subquery to the table.
* This is good for multi-value containers.
*/
protected TableRelationResolver(
@NotNull QueryTableMapping<TS, TQ, TR> targetMapping,
@NotNull BiFunction<Q, TQ, Predicate> correlationPredicate) {
this.targetMappingSupplier = () -> targetMapping;
this.correlationPredicate = correlationPredicate;
this.useSubquery = true;
}

public TableRelationResolver(
/**
* Constructor for table-stored relation resolver using `LEFT JOIN`.
* This is good when we know only one result will match the joining condition,
* e.g. owning object or object referenced by embedded (single-value) reference.
* Using `JOIN` is necessary if ordering by the target is required.
*/
private TableRelationResolver(
@NotNull Supplier<QueryTableMapping<TS, TQ, TR>> targetMappingSupplier,
@NotNull BiFunction<Q, TQ, Predicate> correlationPredicate) {
this.targetMappingSupplier = targetMappingSupplier;
this.correlationPredicate = correlationPredicate;
this.useSubquery = false;
}

/**
Expand All @@ -58,10 +91,16 @@ public TableRelationResolver(
*/
@Override
public ResolutionResult<TQ, TR> resolve(SqlQueryContext<?, Q, R> context) {
SqlQueryContext<TS, TQ, TR> subcontext = context.subquery(targetMappingSupplier.get());
SQLQuery<?> subquery = subcontext.sqlQuery();
subquery.where(correlationPredicate.apply(context.path(), subcontext.path()));
if (useSubquery) {
SqlQueryContext<TS, TQ, TR> subcontext = context.subquery(targetMappingSupplier.get());
SQLQuery<?> subquery = subcontext.sqlQuery();
subquery.where(correlationPredicate.apply(context.path(), subcontext.path()));

return new ResolutionResult<>(subcontext, subcontext.mapping(), true);
return new ResolutionResult<>(subcontext, subcontext.mapping(), true);
} else {
SqlQueryContext<TS, TQ, TR> subcontext = context.leftJoin(
targetMappingSupplier.get(), correlationPredicate);
return new ResolutionResult<>(subcontext, subcontext.mapping());
}
}
}

0 comments on commit 301eb76

Please sign in to comment.