org.babyfish.jimmer.sql.ast.impl.AbstractMutableStatementImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jimmer-sql Show documentation
Show all versions of jimmer-sql Show documentation
A revolutionary ORM framework for both java and kotlin
package org.babyfish.jimmer.sql.ast.impl;
import org.babyfish.jimmer.meta.ImmutableProp;
import org.babyfish.jimmer.meta.ImmutableType;
import org.babyfish.jimmer.sql.JoinType;
import org.babyfish.jimmer.sql.ast.Expression;
import org.babyfish.jimmer.sql.ast.Predicate;
import org.babyfish.jimmer.sql.ast.Selection;
import org.babyfish.jimmer.sql.ast.impl.associated.VirtualPredicateMergedResult;
import org.babyfish.jimmer.sql.ast.impl.util.IdentityMap;
import org.babyfish.jimmer.sql.ast.impl.query.*;
import org.babyfish.jimmer.sql.ast.impl.table.StatementContext;
import org.babyfish.jimmer.sql.ast.impl.table.TableImplementor;
import org.babyfish.jimmer.sql.ast.impl.table.TableProxies;
import org.babyfish.jimmer.sql.ast.impl.table.TableUtils;
import org.babyfish.jimmer.sql.ast.impl.util.*;
import org.babyfish.jimmer.sql.ast.query.*;
import org.babyfish.jimmer.sql.ast.table.AssociationTable;
import org.babyfish.jimmer.sql.ast.table.Props;
import org.babyfish.jimmer.sql.ast.table.Table;
import org.babyfish.jimmer.sql.ast.table.TableEx;
import org.babyfish.jimmer.sql.ast.table.spi.TableProxy;
import org.babyfish.jimmer.sql.filter.Filter;
import org.babyfish.jimmer.sql.filter.impl.FilterArgsImpl;
import org.babyfish.jimmer.sql.filter.impl.FilterManager;
import org.babyfish.jimmer.sql.runtime.ExecutionPurpose;
import org.babyfish.jimmer.sql.runtime.JSqlClientImplementor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
public abstract class AbstractMutableStatementImpl implements FilterableImplementor, MutableStatementImplementor {
private static final Predicate[] EMPTY_PREDICATES = new Predicate[0];
private final JSqlClientImplementor sqlClient;
private final ImmutableType type;
private List predicates = new ArrayList<>();
private Table> table;
private TableImplementor> tableImplementor;
private boolean frozen;
private int modCount;
private final IdentityMap, List> filterPredicates = new IdentityMap<>();
public AbstractMutableStatementImpl(
JSqlClientImplementor sqlClient,
ImmutableType type
) {
if (!type.isEntity()) {
throw new IllegalArgumentException("\"" + type + "\" is not entity");
}
if (sqlClient != null && !sqlClient.getMicroServiceName().equals(type.getMicroServiceName())) {
throw new IllegalArgumentException(
"The sql client and entity type \"" +
type +
"\" do not belong to the same micro service: " +
"{sqlClient: \"" +
sqlClient.getMicroServiceName() +
"\", entity: \"" +
type.getMicroServiceName() +
"\"}"
);
}
this.sqlClient = sqlClient;
this.type = type;
}
public AbstractMutableStatementImpl(
JSqlClientImplementor sqlClient,
TableProxy> table
) {
if (table.__unwrap() != null) {
throw new IllegalArgumentException("table proxy cannot be wrapper");
}
if (table.__prop() != null) {
throw new IllegalArgumentException("table proxy must be root table");
}
this.sqlClient = Objects.requireNonNull(
sqlClient,
"sqlClient cannot be null"
);
if (!sqlClient.getMicroServiceName().equals(table.getImmutableType().getMicroServiceName())) {
throw new IllegalArgumentException(
"The sql client and entity type \"" +
table.getImmutableType() +
"\" do not belong to the same micro service: " +
"{sqlClient: \"" +
sqlClient.getMicroServiceName() +
"\", entity: \"" +
table.getImmutableType().getMicroServiceName() +
"\"}"
);
}
this.table = table;
this.type = table.getImmutableType();
}
@SuppressWarnings("unchecked")
public > T getTable() {
Table> table = this.table;
if (table == null) {
this.table = table = TableProxies.wrap(getTableImplementor());
}
return (T)table;
}
public TableImplementor> getTableImplementor() {
TableImplementor> tableImplementor = this.tableImplementor;
if (tableImplementor == null) {
this.tableImplementor = tableImplementor =
TableImplementor.create(this, type);
}
return tableImplementor;
}
public List getPredicates() {
return Collections.unmodifiableList(predicates);
}
protected List> getGroupExpressions() {
return Collections.emptyList();
}
protected List getHavingPredicates() {
return Collections.emptyList();
}
protected void setHavingPredicates(List havingPredicates) {}
protected List getOrders() {
return Collections.emptyList();
}
public final Iterable unfrozenPredicates() {
return new Iterable() {
@NotNull
@Override
public Iterator iterator() {
return ConcattedIterator.of(
predicates.iterator(),
new FlaternIterator<>(filterPredicates.iterator())
);
}
};
}
public void whereByFilter(TableImplementor> tableImplementor, List predicates) {
filterPredicates.put(tableImplementor, predicates);
}
public Predicate getPredicate(AstContext astContext) {
freeze(astContext);
List ps = predicates;
return ps.isEmpty() ? null : ps.get(0);
}
public Predicate getFilterPredicate(TableImplementor> tableImplementor, AstContext astContext) {
freeze(astContext);
List ps = filterPredicates.get(tableImplementor);
return ps == null || ps.isEmpty() ? null : ps.get(0);
}
public abstract StatementContext getContext();
public abstract AbstractMutableStatementImpl getParent();
@Override
public MutableSubQuery createSubQuery(TableProxy> table) {
return sqlClient.createSubQuery(table);
}
@Override
public , TE, TT extends TableEx> MutableSubQuery createAssociationSubQuery(
AssociationTable table
) {
return sqlClient.createAssociationSubQuery(table);
}
@Override
public boolean hasVirtualPredicate() {
for (Predicate predicate : unfrozenPredicates()) {
if (((Ast)predicate).hasVirtualPredicate()) {
return true;
}
}
return false;
}
@Override
public void resolveVirtualPredicate(AstContext ctx) {
ctx.pushStatement(this);
// Resolve real table implementation to get table alias immediately before resolving virtual predicates,
// this is important because it makes table aliases of SQL looks beautiful
getTableImplementor();
predicates = ctx.resolveVirtualPredicates(predicates);
List havingPredicates = getHavingPredicates();
if (!havingPredicates.isEmpty()) {
setHavingPredicates(ctx.resolveVirtualPredicates(havingPredicates));
}
filterPredicates.replaceAll(ctx::resolveVirtualPredicates);
ctx.popStatement();
}
public final boolean freeze(AstContext ctx) {
if (frozen) {
return false;
}
onFrozen(ctx);
frozen = true;
return true;
}
public boolean isFrozen() {
return frozen;
}
protected void onFrozen(AstContext ctx) {
filterPredicates.removeAll((t, ps) -> {
if (ps.isEmpty()) {
return true;
}
if (t.getParent() == null || t.getJoinType() == JoinType.INNER) {
predicates.addAll(ps);
return true;
}
return false;
});
predicates = mergePredicates(predicates);
filterPredicates.replaceAll(AbstractMutableStatementImpl::mergePredicates);
}
public void applyVirtualPredicates(AstContext ctx) {
int modCount = -1;
while (modCount != ctx.modCount()) {
modCount = ctx.modCount();
resolveVirtualPredicate(ctx);
}
}
public final void applyGlobalFilters(
AstContext astContext,
FilterLevel level,
@Nullable List> selections
) {
if (level != FilterLevel.IGNORE_ALL) {
applyGlobalFiltersImpl(new ApplyFilterVisitor(astContext, level), selections, null);
}
}
public final void applyDataLoaderGlobalFilters(TableImplementor> table) {
AstContext astContext = new AstContext(sqlClient);
ApplyFilterVisitor visitor = new ApplyFilterVisitor(astContext, FilterLevel.DEFAULT);
for (Predicate predicate : unfrozenPredicates()) {
visitor.apply(this, predicate);
}
for (Order order : getOrders()) {
visitor.apply(this, order);
}
TableImplementor> root = getTableImplementor();
applyGlobalFiltersImpl(visitor, null, table);
}
private void applyGlobalFiltersImpl(
ApplyFilterVisitor visitor,
List> selections,
TableImplementor> start
) {
AstContext astContext = visitor.getAstContext();
astContext.pushStatement(this);
try {
applyGlobalFilerImpl(visitor, start != null ? start : getTableImplementor());
int modCount = -1;
__APPLY_STEP__:
while (modCount != modCount()) {
modCount = modCount();
if (selections != null) {
for (Selection> selection : selections) {
if (!visitor.isApplied(this, selection)) {
Ast.from(selection, astContext).accept(visitor);
if (modCount != modCount()) {
continue __APPLY_STEP__;
}
visitor.apply(this, selection);
}
}
}
for (Predicate predicate : getPredicates()) {
if (!visitor.isApplied(this, predicate)) {
((Ast) predicate).accept(visitor);
if (modCount != modCount()) {
continue __APPLY_STEP__;
}
visitor.apply(this, predicate);
}
}
for (Expression> groupExpr : getGroupExpressions()) {
if (!visitor.isApplied(this, groupExpr)) {
((Ast) groupExpr).accept(visitor);
if (modCount != modCount()) {
continue __APPLY_STEP__;
}
visitor.apply(this, groupExpr);
}
}
for (Predicate havingPredicate : getHavingPredicates()) {
if (!visitor.isApplied(this, havingPredicate)) {
((Ast) havingPredicate).accept(visitor);
if (modCount != modCount()) {
continue __APPLY_STEP__;
}
visitor.apply(this, havingPredicate);
}
}
for (Order order : getOrders()) {
if (!visitor.isApplied(this, order)) {
((Ast) order.getExpression()).accept(visitor);
if (modCount != modCount()) {
continue __APPLY_STEP__;
}
visitor.apply(this, order);
}
}
}
} finally {
astContext.popStatement();
}
}
private void applyGlobalFilerImpl(ApplyFilterVisitor visitor, TableImplementor> table) {
FilterLevel level = visitor.level;
if (level == FilterLevel.IGNORE_ALL || filterPredicates.get(table) != null) {
return;
}
Filter globalFilter;
if (level == FilterLevel.IGNORE_USER_FILTERS) {
globalFilter = getSqlClient().getFilters().getLogicalDeletedFilter(table.getImmutableType());
} else {
globalFilter = getSqlClient().getFilters().getFilter(table.getImmutableType());
}
if (globalFilter != null) {
FilterArgsImpl args = new FilterArgsImpl<>(
table,
TableProxies.wrap(table),
false
);
globalFilter.filter(args);
whereByFilter(table, args.toPredicates());
modify();
}
}
public void validateMutable() {
if (frozen) {
throw new IllegalStateException(
"Cannot mutate the statement because it has been frozen"
);
}
}
public JSqlClientImplementor getSqlClient() {
return sqlClient;
}
@Override
public Filterable where(Predicate ... predicates) {
validateMutable();
for (Predicate predicate : predicates) {
if (predicate != null) {
this.predicates.add(predicate);
modify();
}
}
return this;
}
public ExecutionPurpose getPurpose() {
return getContext().getPurpose();
}
protected static List mergePredicates(List predicates) {
if (predicates.size() < 2) {
return predicates;
}
VirtualPredicateMergedResult.removeEmptyResult(predicates);
return Collections.singletonList(
Predicate.and(
predicates.toArray(EMPTY_PREDICATES)
)
);
}
public final int modCount() {
return modCount;
}
protected final void modify() {
modCount++;
AbstractMutableStatementImpl parent = getParent();
if (parent != null) {
parent.modify();
}
}
private static class ApplyFilterVisitor extends AstVisitor {
final FilterLevel level;
private final IdentityPairSet appliedSet = new IdentityPairSet<>();
public ApplyFilterVisitor(AstContext ctx, FilterLevel level) {
super(ctx);
this.level = level;
}
@Override
public boolean visitSubQuery(TypedSubQuery> subQuery) {
if (subQuery instanceof ConfigurableSubQueryImpl>) {
AbstractMutableStatementImpl statement = ((ConfigurableSubQueryImpl>)subQuery).getBaseQuery();
FilterManager.executing(((MutableSubQueryImpl) statement).filterOwner(), () -> {
statement.applyGlobalFiltersImpl(this, null, null);
});
return false;
}
return true;
}
@Override
public void visitTableReference(TableImplementor> table, ImmutableProp prop, boolean rawId) {
AstContext ctx = getAstContext();
if (prop != null && prop.isId() && (rawId || TableUtils.isRawIdAllowed(table, ctx.getSqlClient()))) {
table = table.getParent();
}
while (table != null) {
table.getStatement().applyGlobalFilerImpl(this, table);
table = table.getParent();
}
}
public boolean isApplied(AbstractMutableStatementImpl statement, Selection> selection) {
return appliedSet.has(statement, selection);
}
public boolean isApplied(AbstractMutableStatementImpl statement, Expression> expression) {
return appliedSet.has(statement, expression);
}
public boolean isApplied(AbstractMutableStatementImpl statement, Order order) {
return appliedSet.has(statement, order);
}
public boolean apply(AbstractMutableStatementImpl statement, Selection> selection) {
return appliedSet.add(statement, selection);
}
public void apply(AbstractMutableStatementImpl statement, Expression> expression) {
appliedSet.add(statement, expression);
}
public void apply(AbstractMutableStatementImpl statement, Order order) {
appliedSet.add(statement, order);
}
}
}