001: /**********************************************************************
002: Copyright (c) 2002 Mike Martin (TJDO) and others. All rights reserved.
003: Licensed under the Apache License, Version 2.0 (the "License");
004: you may not use this file except in compliance with the License.
005: You may obtain a copy of the License at
006:
007: http://www.apache.org/licenses/LICENSE-2.0
008:
009: Unless required by applicable law or agreed to in writing, software
010: distributed under the License is distributed on an "AS IS" BASIS,
011: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: See the License for the specific language governing permissions and
013: limitations under the License.
014:
015: Contributors:
016: 2003 Andy Jefferson - coding standards
017: 2003 Andy Jefferson - renamed from CandidateSetExpression
018: ...
019: **********************************************************************/package org.jpox.store.expression;
020:
021: import org.jpox.ClassLoaderResolver;
022: import org.jpox.exceptions.JPOXUserException;
023: import org.jpox.store.DatastoreIdentifier;
024: import org.jpox.store.IdentifierFactory;
025: import org.jpox.store.mapping.JavaTypeMapping;
026: import org.jpox.store.query.StatementText;
027: import org.jpox.store.scostore.CollectionStore;
028:
029: /**
030: * An expression that represents some collection field in a query candidate class,
031: * or a collection field in an object linked from the candidate class by navigation.
032: * <p>
033: * When navigated through using contains(expr), the elements of the collection
034: * are relationally joined onto the query statement.
035: * </p>
036: * <p>
037: * As this is a Collection Expression it works equally for Sets and Lists, and we use CollectionStore
038: * as the backing store interface, so that ListStore and SetStore are equally applicable.
039: *
040: * @version $Revision: 1.33 $
041: */
042: public class CollectionExpression extends ScalarExpression {
043: private final CollectionStore collStore;
044: private final String fieldName;
045:
046: /**
047: * Constructor.
048: * @param qs The Query Statement
049: * @param ownerMapping The mapping to the owner of this collection
050: * @param te The Table Expression
051: * @param collStore the backing store.
052: * @param fieldName Name of the field for the collection.
053: **/
054: public CollectionExpression(QueryExpression qs,
055: JavaTypeMapping ownerMapping, LogicSetExpression te,
056: CollectionStore collStore, String fieldName) {
057: super (qs);
058:
059: this .mapping = ownerMapping;
060: this .collStore = collStore;
061: this .fieldName = fieldName;
062: this .te = te;
063: }
064:
065: /**
066: * Executed when the size() method is found in a query filter.
067: * @return The NumericExpression resulting from the size() method.
068: */
069: public NumericExpression sizeMethod() {
070: IdentifierFactory idFactory = qs.getStoreManager()
071: .getIdentifierFactory();
072: String ctIdentifier = idFactory.newIdentifier(te.getAlias(),
073: fieldName).getIdentifier();
074: DatastoreIdentifier ctRangeVar = idFactory.newIdentifier(
075: IdentifierFactory.TABLE, ctIdentifier);
076:
077: return new ContainerSizeExpression(qs, collStore
078: .getSizeSubquery(qs, mapping, te, ctRangeVar));
079: }
080:
081: /**
082: * Executed when the contains() method is found in a query filter.
083: * @param expr The ScalarExpression passed as a parameter to contains().
084: * @return The BooleanExpression resulting from the contains() method.
085: */
086: public BooleanExpression containsMethod(ScalarExpression expr) {
087: IdentifierFactory idFactory = qs.getStoreManager()
088: .getIdentifierFactory();
089: ClassLoaderResolver clr = qs.getClassLoaderResolver();
090: if (expr instanceof NullLiteral) {
091: // JPOX doesn't currently support querying for nulls in Collections so just return "1 = 0"
092: // TODO Add support for querying for nulls in collections
093: return new BooleanLiteral(qs, mapping, false)
094: .eq(new BooleanLiteral(qs, mapping, true));
095: } else if (expr instanceof UnboundVariable) {
096: UnboundVariable var = (UnboundVariable) expr;
097: if (var.getVariableType() == null) {
098: // Set the variable type to be the element type for this collection
099: // implicit variable type. We now set the type to the collection type
100: var.setVariableType(clr.classForName(collStore
101: .getElementType()));
102: }
103:
104: // Get the exists query of the collection table
105: String existsTableId = idFactory.newIdentifier(
106: idFactory.newIdentifier(te.getAlias(), fieldName),
107: var.getVariableName()).getIdentifier();
108: DatastoreIdentifier existsTableAlias = idFactory
109: .newIdentifier(IdentifierFactory.TABLE,
110: existsTableId);
111: QueryExpression qexpr = collStore.getExistsSubquery(qs,
112: mapping, te, existsTableAlias);
113:
114: // Join from the collection table to the element table
115: DatastoreIdentifier elementTableAlias = null;
116: if (expr.te == null) {
117: String elementTableId = "UNBOUND" + '.'
118: + var.getVariableName();
119: elementTableAlias = idFactory.newIdentifier(
120: IdentifierFactory.TABLE, elementTableId);
121: } else {
122: elementTableAlias = expr.te.getAlias();
123: }
124: ScalarExpression joinExpr = collStore.joinElementsTo(qexpr,
125: qs, mapping, te, existsTableAlias, var
126: .getVariableType(), expr,
127: elementTableAlias, true);
128:
129: var.bindTo(joinExpr);
130:
131: //START see JDOQLContainerTest.testContainsResultVariable
132: LogicSetExpression elementTblExpr = qs
133: .getTableExpression(elementTableAlias);
134: if (qs.hasCrossJoin(elementTblExpr)) {
135: // Perhaps some description about what this is supposed to be doing and WHY ????
136: qexpr.andCondition(joinExpr.eq(expr.mapping
137: .newScalarExpression(qs, elementTblExpr)));
138: }
139: //END see JDOQLContainerTest.
140:
141: return new ExistsExpression(qs, qexpr, true);
142: } else {
143: // "contains(Literal)", "contains(Expression)"
144: String existsTableId = idFactory.newIdentifier(
145: te.getAlias(), fieldName).getIdentifier();
146: DatastoreIdentifier existsTableAlias = idFactory
147: .newIdentifier(IdentifierFactory.TABLE,
148: existsTableId);
149:
150: DatastoreIdentifier elementTableAlias;
151: if (expr.te == null) // literals
152: {
153: int n = 0;
154: do {
155: String elementTableId = existsTableId + '.' + (++n);
156: elementTableAlias = idFactory.newIdentifier(
157: IdentifierFactory.TABLE, elementTableId);
158: } while (qs.getTableExpression(elementTableAlias) != null);
159: } else // expressions
160: {
161: elementTableAlias = expr.te.getAlias();
162: }
163:
164: if (expr instanceof Literal) {
165: // EXISTS (SELECT 1 FROM COLLECTION_TBL WHERE ...)
166: QueryExpression qexpr = collStore.getExistsSubquery(qs,
167: mapping, te, existsTableAlias);
168: ScalarExpression joinExpr = collStore.joinElementsTo(
169: qexpr, qs, mapping, te, existsTableAlias, clr
170: .classForName(expr.getMapping()
171: .getType()), expr,
172: elementTableAlias, true);
173:
174: // TODO This sometimes adds TBL1.COL1 = TBL1.COL1 - check that the equals() removes such things
175: if (!expr.equals(joinExpr)) {
176: // Join to literal value (?)
177: qexpr.andCondition(expr.eq(joinExpr));
178: }
179:
180: return new ExistsExpression(qs, qexpr, true);
181: } else {
182: boolean existsAlways = false;
183: Object ext = expr.qs
184: .getValueForExtension("org.jpox.rdbms.query.containsUsesExistsAlways");
185: if (ext != null && ((String) ext).equals("true")) {
186: existsAlways = true;
187: }
188:
189: if (existsAlways) {
190: // EXISTS (SELECT 1 FROM COLLECTION_TBL WHERE ...)
191: QueryExpression qexpr = collStore
192: .getExistsSubquery(qs, mapping, te,
193: existsTableAlias);
194: ScalarExpression joinExpr = collStore
195: .joinElementsTo(qexpr, qs, mapping, te,
196: existsTableAlias, clr
197: .classForName(expr
198: .getMapping()
199: .getType()), expr,
200: elementTableAlias, true);
201:
202: // TODO This sometimes adds TBL1.COL1 = TBL1.COL1 - check that the equals() removes such things
203: if (!expr.equals(joinExpr)) {
204: // Join to literal value (?)
205: qexpr.andCondition(expr.eq(joinExpr));
206: }
207:
208: return new ExistsExpression(qs, qexpr, true);
209:
210: } else {
211: // Join to element
212: // TODO This is WRONG - see RDBMS-94. All contains() should do "EXISTS (SELECT ... FROM ...)"
213: // The problem is that when using UnboundVariables that are referenced in other legs of SQL
214: // we need to do cross joins up at the parent query, and no mechanism is readily available yet
215: ScalarExpression joinExpr = collStore
216: .joinElementsTo(expr.getQueryExpression(),
217: qs, mapping, te, existsTableAlias,
218: clr.classForName(collStore
219: .getElementType()), expr,
220: elementTableAlias, false);
221:
222: return joinExpr.eq(expr);
223: }
224: }
225: }
226: }
227:
228: /**
229: * Return the BooleanExpression for a query filter in the form "collection.isEmpty()".
230: * @return The BooleanExpression for a query filter in the form "collection.isEmpty()".
231: */
232: public BooleanExpression isEmptyMethod() {
233: IdentifierFactory idFactory = qs.getStoreManager()
234: .getIdentifierFactory();
235: String existsTableId = idFactory.newIdentifier(te.getAlias(),
236: fieldName).getIdentifier();
237: DatastoreIdentifier existsTableAlias = idFactory.newIdentifier(
238: IdentifierFactory.TABLE, existsTableId);
239: QueryExpression qexpr = collStore.getExistsSubquery(qs,
240: mapping, te, existsTableAlias);
241:
242: return new ExistsExpression(qs, qexpr, false);
243: }
244:
245: /**
246: * Method to return the expression for comparing a collection with a value.
247: * JPOX only supports comparisons with null currently.
248: * @param expr The value to compare with.
249: * @return The expression of equality
250: */
251: public BooleanExpression eq(ScalarExpression expr) {
252: if (expr instanceof NullLiteral) {
253: return isEmptyMethod();
254: } else {
255: throw new JPOXUserException(LOCALISER.msg("037004"));
256: }
257: }
258:
259: /**
260: * Method to return the statement text.
261: * @param mode (0=PROJECTION;1=FILTER)
262: * @return The statement
263: * @throws JPOXUserException since this object is inaccessible directly.
264: **/
265: public StatementText toStatementText(int mode) {
266: throw new JPOXUserException(
267: "Cannot reference collection object directly: field name = "
268: + fieldName);
269: }
270: }
|