All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.gs.fw.common.mithra.finder.ExplicitJoinClause Maven / Gradle / Ivy

There is a newer version: 18.1.0
Show newest version
/*
 Copyright 2016 Goldman Sachs.
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing,
 software distributed under the License is distributed on an
 "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 KIND, either express or implied.  See the License for the
 specific language governing permissions and limitations
 under the License.
 */

package com.gs.fw.common.mithra.finder;

import com.gs.fw.common.mithra.MithraObjectPortal;
import com.gs.fw.common.mithra.attribute.Attribute;

import com.gs.collections.impl.list.mutable.FastList;

import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.List;


public class ExplicitJoinClause implements JoinClause, WhereClause.WhereClauseOwner
{
    private boolean isContainerOnly = true;
    private MithraObjectPortal portal;
    private int tableNumber;
    private List attributes;
    private List attributesFromMappers;
    private List children;
    private boolean isMapped = true;
    private String tableAliasPrefix = "t";
    private ExplicitJoinClause parent;
    private WhereClause whereClause;
    private List sqlJoins = FastList.newList(4);
    private List whereClauseSqlJoins;
    private List extraSelectAttributesWithGroupBy;
    private List extraSelectAttributesWithoutGroupBy;
    private List mixedSqlJoins;
    private List outOfScopeMixedSqlJoins;
    private MapperStackImpl mapperStackImpl;
    private boolean prepared = false;
    private boolean mustAggregate;
    private boolean mustLeftJoin;
    private boolean notExists;
    private boolean mustDistinct;
    private boolean readOnly = false;

    public ExplicitJoinClause(MithraObjectPortal portal, int tableNumber)
    {
        this.portal = portal;
        this.tableNumber = tableNumber;
    }

    public ExplicitJoinClause makeReadOnly()
    {
        this.readOnly = true;
        return this;
    }

    public ExplicitJoinClause(MithraObjectPortal portal, int tableNumber, String tableAliasPrefix,
            MapperStackImpl immutableMapperStack, JoinClause incomingParent)
    {
        this(portal, tableNumber);
        this.tableAliasPrefix = tableAliasPrefix;
        this.mapperStackImpl = immutableMapperStack;
        ExplicitJoinClause parent = (ExplicitJoinClause) incomingParent;
        while(parent != null && parent.isContainerOnly)
        {
            parent = parent.parent;
        }
        this.parent = parent;
        if (this.parent != null)
        {
            this.parent.addChild(this);
        }
        this.isContainerOnly = false;
    }

    private int getTableNumber()
    {
        return tableNumber;
    }

    public void addAttributeFromMapper(Attribute attr)
    {
        if (this.attributes == null)
        {
            this.attributesFromMappers = new FastList(4);
        }
        this.attributesFromMappers.add(attr);
        this.addAttribute(attr);
    }

    public void addAttribute(Attribute attr)
    {
        if (this.attributes == null)
        {
            this.attributes = new FastList(4);
        }
        this.attributes.add(attr);
    }

    public boolean belongsToInnerSql(SqlQuery query)
    {
        return this.whereClause.getOwner() != query;
    }

    private boolean isPartOfAggregation()
    {
        return this.mustAggregate || (parent != null && parent.isPartOfAggregation());
    }

    public void generateSql(SqlQuery query, boolean notExists)
    {
        if (this.prepared) return;
        this.prepared = true;
        WhereClause parentWhereClause = parent == null ? query.getWhereClause() : parent.getWhereClause();
        this.mustLeftJoin = notExists || parentWhereClause.isInOrClause() || hasLeftJoinedParent();
        this.notExists = notExists;
        this.mustDistinct = parentWhereClause.isInOrClause() || !notExists;

        if (mustAggregate || mustLeftJoin)
        {
            this.whereClause = new WhereClause(this);
        }
        else
        {
            this.whereClause = getAggregateOrQueryWhereClause(query);
        }

        if (mustAggregate)
        {
            registerColumnSubstitutes(query);
        }

        if (mustLeftJoin && mustAggregate)
        {
            addParentWhereClause(query, "d"+this.getTableNumber()+".c0 is "+(notExists ? "" : "not ")+"null");
        }
    }

    private boolean isAggregationCandidate()
    {
        if (isDefaultJoin() && this.isNotUnique())
        {
            return true;
        }
        if (this.children != null && this.children.size() > 1)
        {
            int count = 0;
            for(int i=0;i= 2;
        }
        return false;
    }

    private boolean hasLeftJoinedParent()
    {
        ExplicitJoinClause p = this.parent;
        while(p != null)
        {
            if (p.mustAggregate) return false;
            if (p.mustLeftJoin) return true;
            p = p.parent;
        }
        return false;
    }

    private void addChild(ExplicitJoinClause joinClause)
    {
        if (this.children == null)
        {
            this.children = FastList.newList(4);
        }
        this.children.add(joinClause);
    }

    private void addParentWhereClause(SqlQuery query, String sql)
    {
        WhereClause wc = getAggregateOrQueryWhereClause(query);
        wc.beginAnd();
        wc.appendWithSpace(sql);
        wc.endAnd();
    }

    private WhereClause getAggregateOrQueryWhereClause(SqlQuery query)
    {
        WhereClause wc = null;
        ExplicitJoinClause p = this.parent;

        while (p != null && wc == null)
        {
            if (p.isOwnedNonAggregateWhereClause())
            {
                p = p.parent;
            }
            else
            {
                wc = p.whereClause;
            }
        }

        if (wc == null)
        {
            wc = query.getWhereClause();
        }
        return wc;
    }

    private boolean isOwnedNonAggregateWhereClause()
    {
        return this.whereClause.getOwner() == this && !this.mustAggregate;
    }

    public boolean mustUseExistsForMapperOnly(SqlQuery query)
    {
        return this.belongsToInnerSql(query) || isNotUniqueForMapper();

    }

    public boolean allowsInClauseTempJoinReplacement()
    {
        if (this.portal == null) return false; // portal is only null for DEFAULT container clause, which will only get here for an or-clause.
        Object owner = this.whereClause.getOwner();
        if (owner == this)
        {
            return !(this.mustLeftJoin && !this.mustAggregate);
        }
        if (owner instanceof JoinClause)
        {
            return ((JoinClause)owner).allowsInClauseTempJoinReplacement();
        }
        return true;
    }

    public void restoreMapper(SqlQuery sqlQuery)
    {
        sqlQuery.getCurrentIdExtractor().restoreMapperStack(this.mapperStackImpl);
    }

    @Override
    public void computeAggregationForTree()
    {
        if (this.parent == null)
        {
            //we're the root of the tree, so we'll compute the aggregation
            this.computeSelfAndChildAggregation();
        }
    }

    private void computeSelfAndChildAggregation()
    {
        this.mustAggregate = isAggregationCandidate() && (this.parent == null || !this.parent.isPartOfAggregation());
        if (this.children != null)
        {
            for (int i = 0; i < children.size(); i++)
            {
                ExplicitJoinClause child = this.children.get(i);
                child.computeSelfAndChildAggregation();
            }
        }
    }

    private boolean isNotUnique()
    {
        return (this.attributes == null || !this.portal.mapsToUniqueIndex(this.attributes));
    }

    private boolean isNotUniqueForMapper()
    {
        return (this.attributesFromMappers == null || !this.portal.mapsToUniqueIndex(this.attributesFromMappers));
    }

    public String getTableAlias()
    {
        return tableAliasPrefix+tableNumber;
    }

    public boolean isMapped()
    {
        return isMapped;
    }

    public void setMapped(boolean mapped)
    {
        isMapped = mapped;
    }

    private boolean isDefaultJoin()
    {
        return this.tableAliasPrefix.equals("t");
    }

    public void reset()
    {
        this.setMapped(false);
        this.sqlJoins.clear();
        if (this.whereClauseSqlJoins != null)
        {
            this.whereClauseSqlJoins.clear();
        }
        if (this.mixedSqlJoins != null)
        {
            this.mixedSqlJoins.clear();
        }
        if (this.whereClause != null)
        {
            this.whereClause.clear();
        }
        if (this.extraSelectAttributesWithGroupBy != null)
        {
            this.extraSelectAttributesWithGroupBy.clear();
        }
        if (this.extraSelectAttributesWithoutGroupBy != null)
        {
            this.extraSelectAttributesWithoutGroupBy.clear();
        }
    }

    public WhereClause getWhereClause()
    {
        return whereClause;
    }

    @Override
    public WhereClause getParentWhereClause(SqlQuery sqlQuery)
    {
        ExplicitJoinClause p = this.parent;
        while (p != null)
        {
            if (p.getWhereClause() == null)
            {
                p = p.parent;
            }
            else
            {
                return p.getWhereClause();
            }
        }
        return sqlQuery.getWhereClause();
    }

    public void appendJoinsToFromClause(SqlQuery query, StringBuilder fromClause, String withLock, String withoutLock)
    {
        if (this.parent == null)
        {
            createAndAppendJoinClause(query, fromClause, withLock, withoutLock);
        }
    }

    private void createAndAppendJoinClause(SqlQuery query, StringBuilder fromClause, String withLock, String withoutLock)
    {
        if (this.outOfScopeMixedSqlJoins != null)
        {
            for(int i=0;i 0;
    }

    private void appendChildJoins(SqlQuery query, StringBuilder fromClause, String withLock, String withoutLock)
    {
        if (this.children != null)
        {
            for(int i=0;i this.getTableNumber())
                    {
                        other.addOutOfScopeMixedSqlJoin(mixedSqlJoin);
                    }
                    else
                    {
                        this.addOutOfScopeMixedSqlJoin(mixedSqlJoin);
                    }
                }
                else
                {
                    addOutOfScopeMixedSqlJoin(mixedSqlJoin);
                }
            }
        }
        else
        {
            if (mustAggregate)
            {
                if (this.extraSelectAttributesWithGroupBy == null)
                {
                    this.extraSelectAttributesWithGroupBy = FastList.newList();
                }
                if (reversed)
                {
                    this.extraSelectAttributesWithGroupBy.add(fullyQualifiedLeftColumnName);
                    fullyQualifiedLeftColumnName = "d"+this.getTableNumber()+".s"+(this.extraSelectAttributesWithGroupBy.size() - 1);
                }
                else
                {
                    this.extraSelectAttributesWithGroupBy.add(fullyQualifiedFromColumn);
                    fullyQualifiedFromColumn = "d"+this.getTableNumber()+".s"+(this.extraSelectAttributesWithGroupBy.size() - 1);
                    this.extraSelectAttributesWithGroupBy.add(fullyQualifiedToColumn);
                    fullyQualifiedToColumn = "d"+this.getTableNumber()+".s"+(this.extraSelectAttributesWithGroupBy.size() - 1);
                }
            }
            this.parent.generateMixedJoinSql(mapperStack, otherJoinClause, fullyQualifiedLeftColumnName, fullyQualifiedFromColumn,
                    fullyQualifiedToColumn, parameterSetter, reversed, query);
        }
    }

    private void addOutOfScopeMixedSqlJoin(MixedSqlJoin mixedSqlJoin)
    {
        if (this.outOfScopeMixedSqlJoins == null) this.outOfScopeMixedSqlJoins = FastList.newList(2);
        this.outOfScopeMixedSqlJoins.add(mixedSqlJoin);
    }

    public void generateJoinSql(SqlQuery query, String fullyQualifiedLeftColumnName, String fullyQualifiedRightHandColumn, String operator)
    {
        SqlJoin sqlJoin = new SqlJoin(fullyQualifiedLeftColumnName, fullyQualifiedRightHandColumn, operator);
        this.sqlJoins.add(sqlJoin);
        if (sqlJoins.size() == 1 && this.mustLeftJoin && !this.mustAggregate)
        {
            addParentWhereClause(query, fullyQualifiedRightHandColumn+" is "+(notExists ? "" : "not ")+"null");
        }
        if (this.mustAggregate && this.whereClause != null)
        {
            query.addDerivedColumnSubstitution(fullyQualifiedRightHandColumn, "d" + this.getTableNumber() + ".c" + (sqlJoins.size() - 1), this.whereClause);
        }
    }

    public void generateAsOfJoinSql(SqlQuery query, MapperStackImpl mapperStack, String fullyQualifiedLeftColumnName,
            String fullyQualifiedRightHandColumn, String operator)
    {
        // enable this for testing
//        if (this.readOnly)
//        {
//            throw new RuntimeException("read only");
//        }
        if (isInScope(mapperStack))
        {
            if (isAccessible(mapperStack))
            {
                generateJoinSql(query, fullyQualifiedLeftColumnName, fullyQualifiedRightHandColumn, operator);
            }
            else
            {
                if (mustAggregate)
                {
                    SqlJoin sqlJoin = new SqlJoin(fullyQualifiedLeftColumnName, fullyQualifiedRightHandColumn, operator);
                    if (this.whereClauseSqlJoins == null)
                    {
                        this.whereClauseSqlJoins = FastList.newList(4);
                    }
                    this.whereClauseSqlJoins.add(sqlJoin);
                }
                else
                {
                    this.parent.generateAsOfJoinSql(query, mapperStack, fullyQualifiedLeftColumnName, fullyQualifiedRightHandColumn, operator);
                }
            }
        }
        else
        {
            if (mustAggregate)
            {
                if (this.extraSelectAttributesWithGroupBy == null)
                {
                    this.extraSelectAttributesWithGroupBy = FastList.newList();
                }
                this.extraSelectAttributesWithGroupBy.add(fullyQualifiedRightHandColumn);
                fullyQualifiedRightHandColumn = "d"+this.getTableNumber()+".s"+(this.extraSelectAttributesWithGroupBy.size() - 1);
            }
            this.parent.generateAsOfJoinSql(query, mapperStack, fullyQualifiedLeftColumnName, fullyQualifiedRightHandColumn, operator);
        }
    }

    private boolean isAccessible(MapperStackImpl mapperStack)
    {
        if (this.mapperStackImpl == null)
        {
            return mapperStack == null || mapperStack.isFullyEmpty();
        }
        return this.mapperStackImpl.isGreaterThanEqualsWithEqualityCheck(mapperStack);
    }

    private boolean isInScope(MapperStackImpl mapperStack)
    {
        if (parent == null) return true;
        if (mapperStack.equals(parent.mapperStackImpl)) return true; // most common case where the join is to the immediate parent
        ExplicitJoinClause aggregateParent = this.parent;
        while(aggregateParent != null && !aggregateParent.mustAggregate)
        {
            aggregateParent = aggregateParent.parent;
        }
        return aggregateParent == null || aggregateParent.mapperStackImpl.compareTo(mapperStack) <= 0;
    }

    public boolean isTopLevel()
    {
        return this.parent == null;
    }

    private boolean hasNoAggregateParent()
    {
        return parent == null || !parent.isAggregateJoin();
    }

    public String getFullyQualifiedColumnNameFor(String fullyQualifiedColumnName)
    {
        if (this.isAggregateJoin())
        {
            if (mustAggregate)
            {
                fullyQualifiedColumnName = this.getFullyQualifiedColumnNameFromJoins(fullyQualifiedColumnName);
            }
            if (hasNoAggregateParent())
            {
                return fullyQualifiedColumnName;
            }
            else
            {
                // we have to now pass this all the way up to the query through one or more aggregate parents!!
                return parent.addToSelectAttributes(fullyQualifiedColumnName);
            }
        }
        else
        {
            return fullyQualifiedColumnName;
        }
    }

    private String addToSelectAttributes(String fullyQualifiedColumnName)
    {
        if (mustAggregate)
        {
            if (extraSelectAttributesWithGroupBy == null) extraSelectAttributesWithGroupBy = FastList.newList();
            extraSelectAttributesWithGroupBy.add(fullyQualifiedColumnName);
            fullyQualifiedColumnName = "d"+this.getTableNumber()+".s"+(extraSelectAttributesWithGroupBy.size() - 1);
        }
        if (hasNoAggregateParent())
        {
            return fullyQualifiedColumnName;
        }
        else
        {
            return parent.addToSelectAttributes(fullyQualifiedColumnName);
        }
    }

    private String addToUngroupedSelectAttributes(String fullyQualifiedColumnName)
    {
        if (mustAggregate)
        {
            if (extraSelectAttributesWithoutGroupBy == null) extraSelectAttributesWithoutGroupBy = FastList.newList();
            extraSelectAttributesWithoutGroupBy.add("min("+fullyQualifiedColumnName+")");
            fullyQualifiedColumnName = "d"+this.getTableNumber()+".r"+(extraSelectAttributesWithoutGroupBy.size() - 1);
        }
        if (hasNoAggregateParent())
        {
            return fullyQualifiedColumnName;
        }
        else
        {
            return parent.addToUngroupedSelectAttributes(fullyQualifiedColumnName);
        }
    }

    private String getFullyQualifiedColumnNameFromJoins(String fullyQualifiedColumnName)
    {
        // simple case, just find the column number:
        for(int i=0;i= ");
            builder.append(this.fullyQualifiedFromColumn).append(" and ") ;
            builder.append(left).append(" < ");
            builder.append(this.fullyQualifiedToColumn).append(") or (");
            builder.append(this.fullyQualifiedToColumn).append(" = ? and ");
            builder.append(left).append(" = ? ))");
        }

        private void appendOnClause(StringBuilder builder, int joinNumber, int tableNumber)
        {
            if (reversed)
            {
                builder.append("((");
                builder.append("d").append(tableNumber).append(".cf").append(joinNumber).append(" >= ");
                builder.append(this.fullyQualifiedFromColumn).append(" and ") ;
                builder.append("d").append(tableNumber).append(".cf").append(joinNumber).append(" < ");
                builder.append(this.fullyQualifiedToColumn).append(") or (");
                builder.append(this.fullyQualifiedToColumn).append(" = ? and ");
                builder.append("d").append(tableNumber).append(".cf").append(joinNumber).append(" = ? ))");
            }
            else
            {
                builder.append("((");
                builder.append(this.fullyQualifiedLeftColumnName).append(" >= ");
                builder.append("d").append(tableNumber).append(".cf").append(joinNumber).append(" and ") ;
                builder.append(this.fullyQualifiedLeftColumnName).append(" < ");
                builder.append("d").append(tableNumber).append(".ct").append(joinNumber).append(") or (");
                builder.append("d").append(tableNumber).append(".ct").append(joinNumber).append(" = ? and ");
                builder.append(this.fullyQualifiedLeftColumnName).append(" = ? ))");
            }
        }

        private void appendLeftJoinAttributes(StringBuilder builder, int joinNumber)
        {
            if (reversed)
            {
                builder.append(this.fullyQualifiedLeftColumnName).append(" cf").append(joinNumber);
            }
            else
            {
                builder.append(this.fullyQualifiedFromColumn).append(" cf").append(joinNumber).append(", ");
                builder.append(this.fullyQualifiedToColumn).append(" ct").append(joinNumber);
            }
        }

        private void appendGroupByAttributes(StringBuilder builder)
        {
            if (reversed)
            {
                builder.append(this.fullyQualifiedLeftColumnName);
            }
            else
            {
                builder.append(this.fullyQualifiedFromColumn).append(", ");
                builder.append(this.fullyQualifiedToColumn);
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy