org.protempa.backend.dsb.relationaldb.AbstractWhereClause Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of protempa-dsb-relationaldb Show documentation
Show all versions of protempa-dsb-relationaldb Show documentation
Implements support for retrieving data from relational
databases.
The newest version!
/*
* #%L
* Protempa Commons Backend Provider
* %%
* Copyright (C) 2012 - 2013 Emory University
* %%
* 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.
* #L%
*/
package org.protempa.backend.dsb.relationaldb;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.arp.javautil.arrays.Arrays;
import org.protempa.backend.dsb.filter.Filter;
import org.protempa.backend.dsb.filter.PropertyValueFilter;
import org.protempa.backend.dsb.relationaldb.mappings.Mappings;
import org.protempa.proposition.value.BooleanValue;
import org.protempa.proposition.value.DateValue;
import org.protempa.proposition.value.InequalityNumberValue;
import org.protempa.proposition.value.NominalValue;
import org.protempa.proposition.value.NumberValue;
import org.protempa.proposition.value.OrdinalValue;
import org.protempa.proposition.value.Value;
import org.protempa.proposition.value.ValueComparator;
import org.protempa.proposition.value.ValueList;
import org.protempa.proposition.value.ValueVisitor;
public abstract class AbstractWhereClause implements WhereClause {
private final Set propIds;
private final ColumnSpecInfo info;
private final List entitySpecs;
private final Set filters;
private final TableAliaser referenceIndices;
private final Set keyIds;
private final SQLOrderBy order;
private final SQLGenResultProcessor resultProcessor;
private final SelectClause selectClause;
private final StagingSpec[] stagedTables;
protected AbstractWhereClause(Set propIds, ColumnSpecInfo info,
List entitySpecs, Set filters,
TableAliaser referenceIndices, Set keyIds,
SQLOrderBy order, SQLGenResultProcessor resultProcessor,
SelectClause selectClause, StagingSpec[] stagedTables) {
this.propIds = propIds;
this.info = info;
this.entitySpecs = Collections.unmodifiableList(entitySpecs);
this.filters = Collections.unmodifiableSet(filters);
this.referenceIndices = referenceIndices;
this.keyIds = Collections.unmodifiableSet(keyIds);
this.order = order;
this.resultProcessor = resultProcessor;
this.selectClause = selectClause;
this.stagedTables = stagedTables;
}
protected Set getPropIds() {
return propIds;
}
protected ColumnSpecInfo getInfo() {
return info;
}
protected List getEntitySpecs() {
return entitySpecs;
}
protected Set getFilters() {
return filters;
}
protected TableAliaser getReferenceIndices() {
return referenceIndices;
}
protected Set getKeyIds() {
return keyIds;
}
protected SQLOrderBy getOrder() {
return order;
}
protected SQLGenResultProcessor getResultProcessor() {
return resultProcessor;
}
protected SelectClause getSelectClause() {
return selectClause;
}
protected StagingSpec[] getStagedTables() {
return stagedTables;
}
@Override
public abstract InClause getInClause(ColumnSpec columnSpec,
Object[] elements, boolean not);
@Override
public abstract OrderByClause getOrderByClause(ColumnSpec keyIdSpec);
@Override
public String generateClause() {
StringBuilder wherePart = new StringBuilder();
EntitySpec prevEntitySpec = null;
boolean first = true;
boolean inGroup = false;
for (int j = 0, n = entitySpecs.size(); j < n; j++) {
EntitySpec entitySpec = entitySpecs.get(j);
if (n > 1 && j > 0) {
if (prevEntitySpec.getName().equals(entitySpec.getName())) {
if (!inGroup) {
if (!first) {
wherePart.append(" AND ");
first = true;
}
wherePart.append(" ((");
inGroup = true;
} else {
wherePart.append(") OR (");
first = true;
}
int wherePartLength = wherePart.length();
wherePart.append(processForWhereClause(prevEntitySpec,
first));
if (wherePart.length() > wherePartLength) {
first = false;
}
} else {
if (inGroup) {
first = true;
int wherePartLength = wherePart.length();
wherePart.append(") OR (");
wherePart.append(processForWhereClause(prevEntitySpec,
first));
wherePart.append(")) ");
if (wherePart.length() > wherePartLength) {
first = false;
}
inGroup = false;
} else {
int wherePartLength = wherePart.length();
wherePart.append(processForWhereClause(prevEntitySpec,
first));
if (wherePart.length() > wherePartLength) {
first = false;
}
}
}
}
prevEntitySpec = entitySpec;
}
if (inGroup) {
first = true;
wherePart.append(") OR (");
wherePart.append(processForWhereClause(prevEntitySpec, first));
wherePart.append(")) ");
} else {
wherePart.append(processForWhereClause(prevEntitySpec, first));
}
processKeyIdConstraintsForWhereClause(info, wherePart, keyIds);
if (wherePart.length() > 0) {
wherePart.insert(0, "WHERE ");
}
wherePart.append(processOrder(info));
return wherePart.toString();
}
private void processKeyIdConstraintsForWhereClause(ColumnSpecInfo info,
StringBuilder wherePart, Set keyIds) {
if (keyIds != null && !keyIds.isEmpty()) {
if (wherePart.length() > 0) {
wherePart.append(" AND ");
}
ColumnSpec keySpec = info.getColumnSpecs().get(0).getColumnSpec();
wherePart.append(getInClause(keySpec, keyIds.toArray(), false)
.generateClause());
}
}
private String processForWhereClause(EntitySpec entitySpec,
boolean first) {
StringBuilder wherePart = new StringBuilder();
int wherePartLength = wherePart.length();
wherePart.append(TimeSpecProcessor.processStartTimeSpec(entitySpec,
filters, first, referenceIndices));
if (wherePart.length() > wherePartLength) {
first = false;
}
wherePartLength = wherePart.length();
wherePart.append(TimeSpecProcessor.processFinishTimeSpec(
entitySpec, filters, first, referenceIndices));
if (wherePart.length() > wherePartLength) {
first = false;
}
wherePartLength = wherePart.length();
if (wherePart.length() > wherePartLength) {
first = false;
}
wherePartLength = wherePart.length();
wherePart.append(processConstraintSpecsForWhereClause(entitySpec,
first));
return wherePart.toString();
}
private StringBuilder processConstraintSpecs(EntitySpec entitySpec,
boolean first) {
StringBuilder wherePart = new StringBuilder();
CONSTRAINT_LOOP: for (ColumnSpec constraintSpec : entitySpec
.getConstraintSpecs()) {
// Skip any constraints that were included in any staged data
// Joining against that data implicitly applies the constraint
for (StagingSpec stagingSpec : getStagedTables()) {
if (constraintSpec.getLastSpec().isSameSchemaAndTable(
stagingSpec.getReplacedTable())) {
continue CONSTRAINT_LOOP;
}
}
int wherePartLength = wherePart.length();
wherePart.append(processConstraintSpecForWhereClause(
constraintSpec, null, first));
if (wherePart.length() > wherePartLength) {
first = false;
}
}
return wherePart;
}
private StringBuilder processPropertySpecs(PropertySpec[] propertySpecs,
boolean first) {
StringBuilder wherePart = new StringBuilder();
for (PropertySpec ps : propertySpecs) {
ColumnSpec constraintSpec = ps.getConstraintSpec();
if (constraintSpec != null) {
int wherePartLength = wherePart.length();
wherePart.append(processConstraint(constraintSpec, null, null, first));
if (wherePart.length() > wherePartLength) {
first = false;
}
}
}
for (Filter filter : filters) {
for (PropertySpec ps : propertySpecs) {
if (filter instanceof PropertyValueFilter) {
PropertyValueFilter pvf = (PropertyValueFilter) filter;
if (pvf.getProperty().equals(ps.getName())) {
ColumnSpec colSpec = ps.getCodeSpec();
int wherePartLength = wherePart.length();
wherePart.append(processPropertyValueFilter(colSpec,
pvf.getValueComparator(), pvf.getValues(),
first));
if (wherePart.length() > wherePartLength) {
first = false;
}
break;
}
}
}
}
return wherePart;
}
private StringBuilder processCodeSpec(EntitySpec entitySpec, boolean first) {
StringBuilder wherePart = new StringBuilder();
ColumnSpec codeSpec = entitySpec.getCodeSpec();
if (codeSpec != null) {
List codeSpecL = codeSpec.asList();
if (codeSpecL.get(codeSpecL.size() - 1).isPropositionIdsComplete()
&& !needsPropIdInClause(entitySpec.getPropositionIds())) {
setCaseClauseIfNeeded(codeSpec, this.propIds);
} else {
int wherePartLength = wherePart.length();
wherePart.append(processConstraintSpecForWhereClause(codeSpec,
this.propIds, first));
if (wherePart.length() > wherePartLength) {
first = false;
}
}
}
return wherePart;
}
private String processConstraintSpecsForWhereClause(EntitySpec entitySpec,
boolean first) {
StringBuilder wherePart = new StringBuilder();
int wherePartLength = wherePart.length();
wherePart.append(processConstraintSpecs(entitySpec, first));
if (first && wherePart.length() > wherePartLength) {
first = false;
wherePartLength = wherePart.length();
}
wherePart.append(processPropertySpecs(entitySpec.getPropertySpecs(),
first));
if (first && wherePart.length() > wherePartLength) {
first = false;
wherePartLength = wherePart.length();
}
wherePart.append(processCodeSpec(entitySpec, first));
return wherePart.toString();
}
private void setCaseClauseIfNeeded(ColumnSpec columnSpec, Set> propIds) {
if (hasConstraint(columnSpec)) {
if (resultProcessor != null) {
resultProcessor
.setCasePresent(columnSpec.getConstraint() == Operator.LIKE);
Mappings filteredMappings = filterMappingsByTarget(
this.propIds, columnSpec.getMappings());
selectClause.setCaseClause(
filteredSqlCodes(propIds, filteredMappings),
columnSpec, filteredMappings);
}
}
}
private String processConstraintSpecForWhereClause(ColumnSpec columnSpec,
Set> propIds, boolean first) {
StringBuilder wherePart = new StringBuilder();
if (hasConstraint(columnSpec)) {
setCaseClauseIfNeeded(columnSpec, null);
wherePart
.append(processConstraint(columnSpec, propIds, null, first));
}
return wherePart.toString();
}
private String processPropertyValueFilter(ColumnSpec columnSpec,
ValueComparator comparator, Value[] values, boolean first) {
StringBuilder wherePart = new StringBuilder();
Operator constraint = valueComparatorToSqlOp(comparator);
if (columnSpec != null && constraint != null) {
ValueExtractor ve = new ValueExtractor();
for (Value value : values) {
value.accept(ve);
}
wherePart.append(processConstraint(columnSpec, ve.values,
constraint, first));
}
return wherePart.toString();
}
private String processConstraint(ColumnSpec columnSpec, Set> propIds,
Operator constraintOverride, boolean first) {
StringBuilder wherePart = new StringBuilder();
ColumnSpec cs = columnSpec.getLastSpec();
Operator constraint = cs.getConstraint();
if (constraintOverride != null) {
constraint = constraintOverride;
}
Mappings propIdToSqlCodes = cs.getMappings();
if (constraint != null && referenceIndices.getIndex(cs) > -1) {
Mappings filteredConstraintValues =
filterMappingsByTarget(propIds, propIdToSqlCodes);
if (!first) {
wherePart.append(" AND ");
}
wherePart.append('(');
Object[] sqlCodes = filteredSqlCodes(propIds,
filteredConstraintValues);
wherePart.append(
WhereConstraintProcessor.getInstance(cs,
constraint, this, sqlCodes, referenceIndices)
.processConstraint()).append(')');
}
return wherePart.toString();
}
/**
* Returns whether an IN clause containing the proposition ids of interest
* should be added to the WHERE clause.
*
* @param entitySpecPropIds
* the proposition ids corresponding to the current entity spec.
* @return true if the query contains < 85% of the proposition
* ids that are known to the data source and if the where clause
* would contain less than or equal to 2000 codes.
*/
private boolean needsPropIdInClause(String[] entitySpecPropIds) {
Set entitySpecPropIdsSet = Arrays.asSet(entitySpecPropIds);
// Filter propIds that are not in the entitySpecPropIds array.
List filteredPropIds = new ArrayList<>(
entitySpecPropIds.length);
for (String propId : propIds) {
if (entitySpecPropIdsSet.contains(propId)) {
filteredPropIds.add(propId);
}
}
return (filteredPropIds.size() < entitySpecPropIds.length * 0.85f)
&& (filteredPropIds.size() <= 2000);
}
private String processOrder(ColumnSpecInfo info) {
StringBuilder wherePart = new StringBuilder();
if (order != null && info.isUsingKeyIdIndex()) {
ColumnSpec keyIdSpec = info.getColumnSpecs().get(0).getColumnSpec();
wherePart.append(getOrderByClause(keyIdSpec)
.generateClause());
}
return wherePart.toString();
}
private static class ValueExtractor implements ValueVisitor {
Set © 2015 - 2025 Weber Informatics LLC | Privacy Policy