/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
/*
* RetrieveDescImpl.java
*
* Created on March 3, 2000
*
*/
package com.sun.jdo.spi.persistence.support.sqlstore.sql;
import com.sun.jdo.api.persistence.support.JDOFatalInternalException;
import com.sun.jdo.spi.persistence.support.sqlstore.ActionDesc;
import com.sun.jdo.spi.persistence.support.sqlstore.RetrieveDesc;
import com.sun.jdo.spi.persistence.support.sqlstore.SQLStoreManager;
import com.sun.jdo.spi.persistence.support.sqlstore.model.ClassDesc;
import com.sun.jdo.spi.persistence.support.sqlstore.model.LocalFieldDesc;
import com.sun.jdo.spi.persistence.support.sqlstore.sql.concurrency.Concurrency;
import com.sun.jdo.spi.persistence.support.sqlstore.sql.constraint.Constraint;
import com.sun.jdo.spi.persistence.support.sqlstore.sql.constraint.ConstraintFieldName;
import com.sun.jdo.spi.persistence.support.sqlstore.sql.generator.SelectQueryPlan;
import com.sun.jdo.spi.persistence.utility.FieldTypeEnumeration;
import com.sun.jdo.spi.persistence.utility.ParameterInfo;
import org.glassfish.persistence.common.I18NHelper;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.ResourceBundle;
/**
*/
public class RetrieveDescImpl extends Object implements RetrieveDesc, Cloneable {
private static final int OPINFO_FIELD_DISALLOWED = 0x01; // 1
private static final int OPINFO_FIELD_REQUIRED = 0x02; // 2
private static final int OPINFO_VAL_DISALLOWED = 0x04; // 4
private static final int OPINFO_VAL_REQUIRED = 0x08; // 8
private static final int OPINFO_VAL_IS_PCOUNT = 0x10; // 16
private static final int OPINFO_ILLEGAL = 0x20; // 32
private static final int OPINFO_NO_OPERATION = 0x40; // 64
/** Indicates, that the query projects on this RD. */
public static final int OPT_PROJECTION = 0x1; // 1
/** Indicates, that a distinct query should be run. */
public static final int OPT_DISTINCT = 0x2; // 2
/** Indicates, that the selected rows should be locked for update. */
public static final int OPT_FOR_UPDATE = 0x4; // 4
/** Indicates, that an avg aggregate query should be run. */
public static final int OPT_AVG = 0x08; // 8
/** Indicates, that a min aggregate query should be run. */
public static final int OPT_MIN = 0x10; // 16
/** Indicates, that a max aggregate query should be run. */
public static final int OPT_MAX = 0x20; // 32
/** Indicates, that a sum aggregate query should be run. */
public static final int OPT_SUM = 0x40; // 64
/** Indicates, that a count aggregate query should be run. */
public static final int OPT_COUNT = 0x80; // 128
/** Special treatment for count on persistent capable objects. */
public static final int OPT_COUNT_PC = 0x100; // 256
/** Sum of all aggregate options. */
public static final int OPT_AGGREGATE = OPT_AVG + OPT_MIN + OPT_MAX +
OPT_SUM + OPT_COUNT + OPT_COUNT_PC;
/** Sum of all aggregate options excluding count on persistent capable objects. */
public static final int OPT_AGGREGATE_NON_COUNT_PC = OPT_AGGREGATE - OPT_COUNT_PC;
/** Indicates, that fetch group fields should be added. */
public static final int OPT_ADD_FETCHGROUPS = 0x200; // 512
/**
* Indicates, that only key fields should be added. When this option is set,
* it modifies meaning of OPT_ADD_FETCHGROUPS. It is assumed that
* only key fieldes from the fetch group will be added.
*/
public static final int OPT_ADD_KEYS_ONLY = 0x400; // 1024
/**
* Indicates, that even if relationship fields are in DFG, they should not
* be prefetched. The runtime will attempt to fetch relationhip fields only
* when OPT_ADD_FETCH_GROUPS is set.
* @see #OPT_ADD_FETCHGROUPS
* @see #OPT_ADD_KEYS_ONLY
*/
public static final int OPT_DISABLE_RELATIONSHIP_PREFETCH = 0x800; // 2048
/** Indicates special treatment for version consistency verifications. */
public static final int OPT_VERIFY = 0x1000; // 4024
/** Array of ConstraintFieldName. */
private ArrayList fields;
/** The constraint stack */
private Constraint constraint;
/** Bitmask of options for this instance as specified by the OPT_* constants */
private int options;
/** Candidate class of this instance */
private Class pcClass;
/** Config for candidate class of this instance */
private ClassDesc config;
/** SelectQueryPlan for this instance */
private SelectQueryPlan plan;
/**
* Discriminates different retrieve descriptors which use the same
* navigational field.
*/
private Object navigationalId;
/** Result type for an aggregate query. */
private int aggregateResultType = FieldTypeEnumeration.NOT_ENUMERATED;
/**
* I18N message handler
*/
private final static ResourceBundle messages = I18NHelper.loadBundle(
"com.sun.jdo.spi.persistence.support.sqlstore.Bundle", // NOI18N
RetrieveDescImpl.class.getClassLoader());
public RetrieveDescImpl(Class pcClass, ClassDesc config) {
super();
this.pcClass = pcClass;
this.config = config;
fields = new ArrayList();
constraint = new Constraint();
}
/**
* The addResult method is used to specify which fields should be
* returned in a persistent object. If the field requested is a
* reference to another persistent object then a RetrieveDesc may be
* provided which describes which fields of the referenced object
* should be returned and, optionally, constraints on it.
* If the parameter <code>projection</code> is true, the field
* specified by <code>name</code> should be projected.
*
* @param name The name of the field to return.
* @param foreignConstraint
* RetrieveDesc describing fields and constraints for a referenced object.
* @param projection Specifies, if this is a projection.
*/
public void addResult(String name,
RetrieveDesc foreignConstraint,
boolean projection) {
ConstraintFieldName cfName = new ConstraintFieldName(name, foreignConstraint);
if (projection) {
if ((options & OPT_PROJECTION) > 0) {
throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
"sqlstore.retrievedesc.toomanyprojections")); // NOI18N
}
// For projections on foreign fields, mark the foreign constraint.
// For local fields, set the property on the field constraint.
if (foreignConstraint != null) {
((RetrieveDescImpl) foreignConstraint).options |= OPT_PROJECTION;
} else {
cfName.setProjection();
// Set this property if you want to have DFG fields added for
// projections on local fields.
// options = options | OPT_PROJECTION;
}
}
fields.add(cfName);
}
/**
* The addResult method can be used to specify <it>global</it>
* query attributes that don't end up in the where clause.
* Aggregate functions and the distinct op code are examples for
* those query options. The result type defines the object to be
* returned by an aggregate query. In case of distinct the result
* type should be FieldTypeEnumeration.NOT_ENUMERATED. The method
* might be called twice, in case of a JDOQL query having both an
* aggregate and distinct:
* query.setResult("avg (distinct salary)");
* ->
* retrieveDesc.addResult(OP_AVG, FieldTypeEnumeration.DOUBLE);
* retrieveDesc.addResult(OP_DISTINCT, FieldTypeEnumeration.NOT_ENUMERATED);
* retrieveDesc.addResult("salary", null, true);
*
* @param opCode The operation code.
* @param aggregateResultType The object type returned by aggregate queries.
* @see com.sun.jdo.spi.persistence.utility.FieldTypeEnumeration
*/
public void addResult(int opCode, int aggregateResultType) {
switch (opCode) {
case ActionDesc.OP_DISTINCT:
options = options | OPT_DISTINCT;
break;
case ActionDesc.OP_AVG:
options = options | OPT_AVG;
break;
case ActionDesc.OP_MIN:
options = options | OPT_MIN;
break;
case ActionDesc.OP_MAX:
options = options | OPT_MAX;
break;
case ActionDesc.OP_SUM:
options = options | OPT_SUM;
break;
case ActionDesc.OP_COUNT:
options = options | OPT_COUNT;
break;
case ActionDesc.OP_COUNT_PC:
options = options | OPT_COUNT_PC;
break;
default:
throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
"core.constraint.illegalop", "" + opCode)); // NOI18N
}
if (aggregateResultType != FieldTypeEnumeration.NOT_ENUMERATED) {
if (this.aggregateResultType == FieldTypeEnumeration.NOT_ENUMERATED) {
this.aggregateResultType = aggregateResultType;
} else {
// aggregate result type has already been set
throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
"sqlstore.retrievedesc.toomanyresulttypes")); // NOI18N
}
}
}
/**
* Add a field specified by <code>name</code> to the list of fields to be prefetched.
* @param name Name of the field to be prefetched.
* @param foreignConstraint This parameter is null if the field is a local field.
* If the field is a foreign field, this parameter should be not null and must refer
* to an instance of RetrieveDesc correpsonding to the config of the foreign field.
*/
public void addPrefetchedField(String name, RetrieveDesc foreignConstraint) {
fields.add(new ConstraintFieldName(name, foreignConstraint, true));
}
/**
* {@inheritDoc}
*/
public void setPrefetchEnabled(boolean prefetchEnabled) {
if (!prefetchEnabled) {
options |= OPT_DISABLE_RELATIONSHIP_PREFETCH;
} else {
// options has the flag OPT_DISABLE_RELATIONSHIP_PREFETCH unset by default
}
}
/**
* <P>Adds a constraint on the foreign field specified by
* <code>name</code>. This method is used to specify a relationship
* navigation on field <code>name</code> to the class represented by
* the retrieve descriptor <code>foreignConstraint</code>.
* If <code>name</code> is null, an unrelated constraint is added.
* A constraint is unrelated, if there is neither a foreign field
* nor a local field connecting to the retrieve descriptor
* <code>foreignConstraint</code>.
*/
public void addConstraint(String name,
RetrieveDesc foreignConstraint) {
if (name == null) {
constraint.addField(null, foreignConstraint);
} else {
constraint.addForeignField(name, foreignConstraint);
}
}
/**
* <P>Adds a constraint on the relationship field specified by
* <code>name</code>.
* This method is useful e.g. for comparisons of local fields with field of a related object:
* emp.addConstraint("lastName", ActionDesc.OP_EQ, mgr, lastName");
* compares the employee's lastName field with the lastName field of the related manager.
*/
public void addConstraint(String name, int operation,
RetrieveDesc foreignConstraint, String foreignName) {
constraint.addField(foreignName, foreignConstraint);
constraint.addField(name, null);
constraint.addOperation(operation);
}
/**
* The addConstraint method is used to limit the values of fields for
* objects being selected.
* addConstraint pushes the value, name, and operation onto the
* Constraint stack. The constraints are anded together by default.
* Arbitrarily complex relationships on the Constraint stack are supported.
* The Constraint stack can be manipulated directly if necessary.
*
* @param name
* The name parameter specifies the field whose value
* should be limited.
* @param operation
* The operation parameter specifies the relationship the field
* should bear to the value. Values for operation are defined in
* the ActionDesc interface.
* @param value
* The value parameter usually specifies the value to which the
* field should be limited, however it is sometimes used to
* hold a parameter count as for the OP_IN operation.
*/
public void addConstraint(String name,
int operation,
Object value) {
int info = getOperationInfo(operation);
if ((info & OPINFO_ILLEGAL) > 0) {
throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
"core.constraint.illegalop", "" + operation)); // NOI18N
} else if ((info & OPINFO_FIELD_REQUIRED) > 0 && name == null) {
throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
"core.constraint.fieldrequired", "" + operation)); // NOI18N
} else if ((info & OPINFO_FIELD_DISALLOWED) > 0 && name != null) {
throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
"core.constraint.fielddisallowed", "" + operation)); // NOI18N
} else if ((info & OPINFO_VAL_REQUIRED) > 0 && value == null) {
throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
"core.constraint.valrequired", "" + operation)); // NOI18N
} else if ((info & OPINFO_VAL_DISALLOWED) > 0 && value != null) {
throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
"core.constraint.valdisallowed", "" + operation)); // NOI18N
}
if ((info & OPINFO_VAL_IS_PCOUNT) > 0) {
if (name != null) {
constraint.addField(name, null);
}
addValueConstraint(name, value);
} else {
switch (operation) {
case RetrieveDescImpl.OP_PARAMETER:
addParameterConstraint(value);
break;
case ActionDesc.OP_IN:
case ActionDesc.OP_NOTIN:
constraint.addConstraintFieldSubQuery(name,(ActionDesc) value);
break;
default:
if (value != null) {
addValueConstraint(name, value);
}
if (name != null) {
constraint.addField(name, null);
}
break;
}
}
if ((info & OPINFO_NO_OPERATION) == 0) {
constraint.addOperation(operation);
}
}
/**
* Add Constraints corresponding to given <code>fields</code>.
* The constraints are added as Parameter Constraints.
* index of the parameter starts at given <code>startIndex</code>
* @param fields fields for which constraints are to be added.
* @param startIndex starting Index for the parameter.
*/
public void addParameterConstraints(LocalFieldDesc[] fields, int startIndex) {
for (int i = 0; i < fields.length; i++) {
LocalFieldDesc field = fields[i];
addParameterConstraint(field, i + startIndex);
}
}
/**
* Add ParameterConstraint corresponding to given <code>field</code>
* at given <code>index</code>.
* @param field The field for which constraints are to be added.
* @param index Index at which the ParameterConstraint is to be inserted.
*/
public void addParameterConstraint(LocalFieldDesc field, int index) {
// Generate parameter info for this parameter.
String fieldName = field.getName();
int type = field.getEnumType();
addConstraint(null, ActionDesc.OP_PARAMETER,new ParameterInfo(index, type, fieldName));
addConstraint(fieldName, ActionDesc.OP_FIELD, null);
addConstraint(null, ActionDesc.OP_EQ, null);
}
/**
* Adds information about <code>value</code> on the constraint stack.
* @param name Name of the field to which the specified value is bound.
* The query compiler can detect the correct value for name only in
* case of simple expressions in the filter. A simple expression is
* <em>fieldName op value</em>. If the compiler is not able to detect
* correct value for name it will pass null.
* @param value value to which the field's value is constrained.
*/
private void addValueConstraint(String name, Object value) {
constraint.addValue(value, getLocalFieldDesc(name));
}
/**
* Adds information about parameter on the constraint stack.
* @param value Instance of
* <code>com.sun.jdo.spi.persistence.utility.ParameterInfo</code>.
* Contains index, type and name of the field to which
* this parameter is bound. The field name is used when binding
* the parameter to the sql statement.
* name can be null for complex expressions in a filter as described in
* addValueConstraint.
* @see #addValueConstraint
*/
private void addParameterConstraint(Object value) {
if (value instanceof ParameterInfo) {
ParameterInfo parameterInfo = (ParameterInfo)value;
constraint.addParamIndex(parameterInfo.getIndex(), parameterInfo.getType(),
getLocalFieldDesc(parameterInfo.getAssociatedField()));
} else {
throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
"core.constraint.illegalParameterInfo")); // NOI18N
}
}
/**
* Returns the local field descriptor for the field <code>name</code>.
* Delegates to <code>ClassDesc#getLocalFieldDesc(String)<code>.
*
* @param name Field name.
* @return null if the <code>name</code> is null.
* LocalFieldDesc for the field <code>name</code> otherwise.
*/
private LocalFieldDesc getLocalFieldDesc(String name) {
return name == null ? null : config.getLocalFieldDesc(name);
}
/**
* The getOperationInfo method returns information about the operation
* requested. The following constants define different properties of
* an operation and can be or'd together:
* OPINFO_FIELD_DISALLOWED A field parameter cannot be specified
* with this operation
* OPINFO_FIELD_REQUIRED A field parameter must be specified
* with this operation
* OPINFO_VAL_DISALLOWED A value parameter cannot be specified
* with this operation
* OPINFO_VAL_REQUIRED A value parameter must be specified
* with this operation
* OPINFO_NULL This operation is a "null" type operation
* (i.e. "is null", or "is not null")
* OPINFO_ILLEGAL This operation is illegal
* OPINFO_NO_OPERATION This isn't an operation at all,
* it is some other specifier.
* OPINFO_VAL_IS_PCOUNT This operation has a value which is the
* parameter count of following parameter
* nodes
*
* @param operation
* The operation parameter specifies the operation for which
* information is desired.
*/
private static int getOperationInfo(int operation) {
int info;
switch (operation) {
case ActionDesc.OP_ABS:
info = OPINFO_VAL_DISALLOWED;
break;
case ActionDesc.OP_ADD:
info = 0;
break;
case ActionDesc.OP_AND:
info = 0;
break;
case ActionDesc.OP_FIELD:
info = OPINFO_FIELD_REQUIRED | OPINFO_VAL_DISALLOWED | OPINFO_NO_OPERATION;
break;
case ActionDesc.OP_BETWEEN:
info = 0;
break;
case ActionDesc.OP_DIV:
info = 0;
break;
case ActionDesc.OP_EQ:
info = 0;
break;
case ActionDesc.OP_NE:
info = 0;
break;
case ActionDesc.OP_EQUIJOIN:
info = 0;
break;
case ActionDesc.OP_NOT:
info = OPINFO_FIELD_DISALLOWED | OPINFO_VAL_DISALLOWED;
break;
case ActionDesc.OP_GE:
info = 0;
break;
case ActionDesc.OP_GT:
info = 0;
break;
case ActionDesc.OP_IN:
case ActionDesc.OP_NOTIN:
info = OPINFO_FIELD_REQUIRED | OPINFO_VAL_REQUIRED;
break;
case ActionDesc.OP_LE:
info = 0;
break;
case ActionDesc.OP_LEFTJOIN:
info = 0;
break;
case ActionDesc.OP_LENGTH:
info = OPINFO_VAL_DISALLOWED;
break;
case ActionDesc.OP_LIKE:
info = 0;
break;
case ActionDesc.OP_LIKE_ESCAPE:
info = 0;
break;
case ActionDesc.OP_LT:
info = 0;
break;
case ActionDesc.OP_LTRIM:
info = OPINFO_VAL_DISALLOWED;
break;
case ActionDesc.OP_MUL:
info = 0;
break;
case ActionDesc.OP_OR:
info = 0;
break;
case ActionDesc.OP_ORDERBY:
info = 0;
break;
case ActionDesc.OP_ORDERBY_DESC:
info = 0;
break;
case ActionDesc.OP_PARAMETER_COUNT:
info = OPINFO_FIELD_DISALLOWED | OPINFO_VAL_REQUIRED | OPINFO_VAL_IS_PCOUNT | OPINFO_NO_OPERATION;
break;
case ActionDesc.OP_RIGHTJOIN:
info = 0;
break;
case ActionDesc.OP_RTRIM:
info = OPINFO_VAL_DISALLOWED;
break;
case ActionDesc.OP_SQRT:
info = OPINFO_VAL_DISALLOWED;
break;
case ActionDesc.OP_SUB:
info = 0;
break;
case ActionDesc.OP_SUBSTRING:
info = 0;
break;
case ActionDesc.OP_POSITION:
info = 0;
break;
case ActionDesc.OP_POSITION_START:
info = 0;
break;
case ActionDesc.OP_MAYBE_NULL:
info = OPINFO_FIELD_REQUIRED;
break;
case ActionDesc.OP_CONCAT:
info = 0;
break;
case ActionDesc.OP_VALUE:
info = OPINFO_FIELD_DISALLOWED | OPINFO_VAL_REQUIRED | OPINFO_NO_OPERATION;
break;
case ActionDesc.OP_PARAMETER:
info = OPINFO_FIELD_DISALLOWED | OPINFO_VAL_REQUIRED | OPINFO_NO_OPERATION;
break;
case OP_NULL:
case OP_NOTNULL:
info = 0;
break;
case ActionDesc.OP_MOD:
info = 0;
break;
default:
info = OPINFO_ILLEGAL;
break;
}
return info;
}
/**
* Builds the internal query plan and initializes the select statements.
* Projections on collection fields will not be resolved until the actual
* retrieval in {@link SQLStoreManager#retrieve(
* com.sun.jdo.spi.persistence.support.sqlstore.PersistenceManager,
* RetrieveDesc, com.sun.jdo.spi.persistence.support.sqlstore.ValueFetcher)}.
*/
public synchronized SelectQueryPlan buildQueryPlan(SQLStoreManager store,
Concurrency concurrency) {
if (plan == null) {
handleProjection();
plan = SelectQueryPlan.newInstance(this, store, concurrency);
plan.build();
// Generate the text for the select statements.
ArrayList statements = plan.getStatements();
// Sanity check.
if (statements.size() > 1) {
throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
"sqlstore.retrievedesc.stmntsnotjoined")); // NOI18N
}
}
return plan;
}
/**
* Sets the fetch group options for all retrieve descriptors in the
* retrieve descriptor hierarchy. This is important for projection and
* aggregate queries, as we don't want to select additional fields
* from the data store. This is also important for queries where relationship
* prefetch is involved, as we want to propagate user's choice to disable
* relationship prefetch for a finder to all the retrieve descriptors in
* the projection tree.
* Also sets the projection on the candidate class in case of queries without
* user projection.
*/
private void handleProjection() {
// Prepare all the query options that need to be distributed to all the
// RetrieveDescs involved in the projection tree.
final int queryOptions = options & (OPT_AGGREGATE | OPT_DISABLE_RELATIONSHIP_PREFETCH);
RetrieveDescImpl projectedDesc = distributeQueryOptions(
queryOptions, aggregateResultType);
if (projectedDesc == null) {
// Set the default projection on the candidate retrieve descriptor.
options |= OPT_PROJECTION;
// Prepare fetch group options again after default projection has been set.
setFetchGroupOptions(queryOptions);
}
}
/**
* Marks each descriptor with the options <code>queryOptions</code> and
* finds the projection in the retrieve descriptor hierarchy.
* The query options have to be known for each descriptor, because we don't want to
* generate outer joins for OPT_COUNT_PC and we want to propagate users
* choice to disable prefetch for a finder to projected RetrieveDesc.
* This method relies on the fact, that each query can have only
* <b>one</b> projection.
*
* @param queryOptions The options that need to be set on all
* retrieve descriptors. This helps identify aggregate queries, see
* {@link SelectQueryPlan#processJoins()}.
* This also helps propagate user's choice to disable prefetch to the
* projected RetrieveDesc, see
* {@link SelectQueryPlan#addFetchGroup(int, java.util.ArrayList, java.util.ArrayList)}.
* @param aggregateResultType The aggregate result type has to
* be set on the projected retrieve descriptor.
* @return The projected retrieve descriptor, or null there is
* no projection.
*/
private RetrieveDescImpl distributeQueryOptions(int queryOptions,
int aggregateResultType) {
RetrieveDescImpl projectedDesc = null;
if ((options & OPT_PROJECTION) > 0) {
// This is a foreign field projection.
// Set the fetch group properties.
setFetchGroupOptions(queryOptions);
this.aggregateResultType = aggregateResultType;
projectedDesc = this;
}
// Distribute the aggregate option to all retrieve descriptors.
options |= queryOptions;
// Loop through all dependent retrieve descriptors in the field list.
for (int i = 0; i < fields.size(); i++) {
ConstraintFieldName cfn = (ConstraintFieldName) fields.get(i);
if (cfn.isProjection()) {
// This is a local field projection.
// No fetch groups are added.
this.aggregateResultType = aggregateResultType;
projectedDesc = this;
} else if (cfn.desc != null) {
projectedDesc = ((RetrieveDescImpl) cfn.desc).distributeQueryOptions(
queryOptions, aggregateResultType);
}
}
return projectedDesc;
}
/**
* Sets the fetch group policy for the projected retrieve descriptor.
* The policy is based on the following rules:
*
* <ul>
* <li>Fetchgroups are added for a projection w/o aggregates.</li>
* <li>Only keys are added for count(*) queries.</li>
* </ul>
*
* @param queryOptions The quey options that needs to be set for ********
* <code>queryOptions</code> Aggregate queries impose
* special restrictions on which fields to be selected. All aggregate
* queries except count(*) operate on exactly one field. Count(*)
* queries operate on persistence capable objects.
* @see SelectQueryPlan#processFetchGroups(ArrayList, ArrayList)
*/
private void setFetchGroupOptions(int queryOptions) {
if ((queryOptions & OPT_AGGREGATE_NON_COUNT_PC) == 0) {
// Don't add fetch groups except for projections
// w/o aggregates or counts on persistence capable objects.
options |= OPT_ADD_FETCHGROUPS;
if (queryOptions == OPT_COUNT_PC) {
options |= OPT_ADD_KEYS_ONLY;
}
}
}
/**
* Sets a navigational id on the retrieve descriptor. This id
* will be used to discriminate different retrieve descriptors which
* use the same navigational field. If not set, the field name is used.
*
* @param navigationalId Tag to discriminate different retrieve
* descriptors that use the same navigational field.
*/
public void setNavigationalId(Object navigationalId) {
this.navigationalId = navigationalId;
}
/**
* Returns the navigational id of this retrieve descriptor. This id
* will be used to discriminate different retrieve descriptors which
* use the same navigational field. If not set, the field name is used.
*
* @return Tag to discriminate different retrieve descriptors that
* use the same navigational field.
*/
public Object getNavigationalId() {
return navigationalId;
}
/**
* Sets option <code>option</code>. Only used to mark this
* retrieve descriptor as internal. All valid options are defined
* in this class.
*
* @param option Option being set.
*/
public void setOption(int option) {
this.options |= option;
}
/**
* Returns the result type for aggregate queries.
*/
public int getAggregateResultType() {
return aggregateResultType;
}
/**
* Returns the options of this retrieve descriptor.
*
* @return The options of this retrieve descriptor.
*/
public int getOptions() {
return options;
}
public SelectQueryPlan getPlan() {
return plan;
}
public ClassDesc getConfig() {
return config;
}
public void setPlan(SelectQueryPlan plan) {
this.plan = plan;
}
public Class getPersistenceCapableClass() {
return pcClass;
}
public Constraint getConstraint() {
return constraint;
}
public Iterator getFields() {
return fields.iterator();
}
public Object clone() {
try {
RetrieveDescImpl clone = (RetrieveDescImpl) super.clone();
clone.fields = new ArrayList();
clone.constraint = new Constraint();
return clone;
} catch (CloneNotSupportedException e) {
//
// shouldn't happen.
//
return null;
}
}
}