PageRenderTime 42ms CodeModel.GetById 20ms app.highlight 17ms RepoModel.GetById 1ms app.codeStats 0ms

/src/com/google/appengine/datanucleus/query/JDOQLQuery.java

http://datanucleus-appengine.googlecode.com/
Java | 258 lines | 164 code | 28 blank | 66 comment | 48 complexity | aeef8294ce05362a51aba82310f4572b MD5 | raw file
  1/**********************************************************************
  2Copyright (c) 2009 Google Inc.
  3
  4Licensed under the Apache License, Version 2.0 (the "License");
  5you may not use this file except in compliance with the License.
  6You may obtain a copy of the License at
  7
  8http://www.apache.org/licenses/LICENSE-2.0
  9
 10Unless required by applicable law or agreed to in writing, software
 11distributed under the License is distributed on an "AS IS" BASIS,
 12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13See the License for the specific language governing permissions and
 14limitations under the License.
 15**********************************************************************/
 16package com.google.appengine.datanucleus.query;
 17
 18import org.datanucleus.ClassLoaderResolver;
 19import org.datanucleus.exceptions.NucleusFatalUserException;
 20import org.datanucleus.metadata.AbstractClassMetaData;
 21
 22import com.google.appengine.datanucleus.DatastoreManager;
 23import com.google.appengine.datanucleus.MetaDataUtils;
 24
 25import org.datanucleus.query.evaluator.JDOQLEvaluator;
 26import org.datanucleus.query.evaluator.JavaQueryEvaluator;
 27import org.datanucleus.ExecutionContext;
 28import org.datanucleus.store.Extent;
 29import org.datanucleus.store.StoreManager;
 30import org.datanucleus.store.connection.ManagedConnection;
 31import org.datanucleus.store.connection.ManagedConnectionResourceListener;
 32import org.datanucleus.store.query.AbstractJDOQLQuery;
 33import org.datanucleus.store.query.AbstractQueryResult;
 34import org.datanucleus.store.query.CandidateIdsQueryResult;
 35import org.datanucleus.store.query.Query;
 36import org.datanucleus.util.NucleusLogger;
 37
 38import java.util.ArrayList;
 39import java.util.Iterator;
 40import java.util.List;
 41import java.util.Map;
 42
 43/**
 44 * Implementation of JDOQL for the app engine datastore.
 45 *
 46 * @author Max Ross <maxr@google.com>
 47 */
 48public class JDOQLQuery extends AbstractJDOQLQuery {
 49  /** The underlying Datastore query implementation. */
 50  private final DatastoreQuery datastoreQuery;
 51
 52  /**
 53   * Constructs a new query instance that uses the given StoreManager and ExecutionContext.
 54   * @param storeMgr StoreManager
 55   * @param ec ExecutionContext
 56   */
 57  public JDOQLQuery(StoreManager storeMgr, ExecutionContext ec) {
 58    this(storeMgr, ec, (JDOQLQuery) null);
 59  }
 60
 61  /**
 62   * Constructs a new query instance having the same criteria as the given query.
 63   * @param storeMgr StoreManager
 64   * @param ec ExecutionContext
 65   * @param q The query from which to copy criteria.
 66   */
 67  public JDOQLQuery(StoreManager storeMgr, ExecutionContext ec, JDOQLQuery q) {
 68    super(storeMgr, ec, q);
 69    datastoreQuery = new DatastoreQuery(this);
 70  }
 71
 72  /**
 73   * Constructor for a JDOQL query where the query is specified using the "Single-String" format.
 74   * @param storeMgr StoreManager
 75   * @param ec ExecutionContext.
 76   * @param query The JDOQL query string.
 77   */
 78  public JDOQLQuery(StoreManager storeMgr, ExecutionContext ec, String query) {
 79    super(storeMgr, ec, query);
 80    datastoreQuery = new DatastoreQuery(this);
 81  }
 82
 83  /**
 84   * Convenience method to return whether the query should be evaluated in-memory.
 85   * @return Use in-memory evaluation?
 86   */
 87  protected boolean evaluateInMemory() {
 88    if (candidateCollection != null) {
 89      if (compilation != null && compilation.getSubqueryAliases() != null) {
 90        // TODO In-memory evaluation of subqueries isn't fully implemented yet, so remove this when it is
 91        NucleusLogger.QUERY.warn("In-memory evaluator doesn't currently handle subqueries completely so evaluating in datastore");
 92        return false;
 93      }
 94
 95      // Return true unless the user has explicitly said no
 96      Object val = getExtension(EXTENSION_EVALUATE_IN_MEMORY);
 97      if (val == null) {
 98        return true;
 99      }
100      return Boolean.valueOf((String)val);
101    }
102    return super.evaluateInMemory();
103  }
104
105  /**
106   * {@inheritDoc}
107   */
108  @Override
109  protected Object performExecute(Map parameters) {
110    if (type == org.datanucleus.store.query.Query.BULK_UPDATE) {
111      throw new NucleusFatalUserException("Bulk Update statements are not supported.");
112    }
113
114    long startTime = System.currentTimeMillis();
115    if (NucleusLogger.QUERY.isDebugEnabled()) {
116        NucleusLogger.QUERY.debug(LOCALISER.msg("021046", "JDOQL", getSingleStringQuery(), null));
117    }
118
119    if (candidateCollection == null &&
120        type == Query.SELECT && resultClass == null && result == null) {
121      // Check for cached query results
122      List<Object> cachedResults = getQueryManager().getDatastoreQueryResult(this, parameters);
123      if (cachedResults != null) {
124        // Query results are cached, so return those
125        return new CandidateIdsQueryResult(this, cachedResults);
126      }
127    }
128
129    Object results = null;
130    if (evaluateInMemory()) {
131        // Evaluating in-memory so build up list of candidates
132        List candidates = null;
133        if (candidateCollection != null) {
134          candidates = new ArrayList(candidateCollection);
135        } else {
136          Extent ext = getStoreManager().getExtent(ec, candidateClass, subclasses);
137          candidates = new ArrayList();
138          Iterator iter = ext.iterator();
139          while (iter.hasNext()) {
140            candidates.add(iter.next());
141          }
142        }
143
144        // Evaluate in-memory over the candidate instances
145        JavaQueryEvaluator resultMapper = new JDOQLEvaluator(this, candidates, compilation,
146            parameters, ec.getClassLoaderResolver());
147        results = resultMapper.execute(true, true, true, true, true);
148    }
149    else {
150      // Evaluate in-datastore
151      boolean inmemoryWhenUnsupported = getEvaluateInMemoryWhenUnsupported();
152      QueryData qd = datastoreQuery.compile(compilation, parameters, inmemoryWhenUnsupported);
153      if (NucleusLogger.QUERY.isDebugEnabled()) {
154        // Log the query
155        NucleusLogger.QUERY.debug("Query compiled as : " + qd.getDatastoreQueryAsString());
156      }
157
158      results = datastoreQuery.performExecute(qd);
159
160      boolean filterInMemory = false;
161      boolean orderInMemory = false;
162      boolean resultInMemory = (result != null || grouping != null || having != null || resultClass != null);
163      if (inmemoryWhenUnsupported) {
164        // Set filter/order flags according to what the query can manage in-datastore
165        filterInMemory = !datastoreQuery.isFilterComplete();
166
167        if (ordering != null) {
168          if (filterInMemory) {
169            orderInMemory = true;
170          } else {
171            if (!datastoreQuery.isOrderComplete()) {
172              orderInMemory = true;
173            }
174          }
175        }
176      }
177
178      // Evaluate any remaining parts in-memory
179      if (filterInMemory || resultInMemory || orderInMemory) {
180        JavaQueryEvaluator resultMapper = new JDOQLEvaluator(this, (List)results, compilation,
181            parameters, ec.getClassLoaderResolver());
182        results = resultMapper.execute(filterInMemory, orderInMemory, 
183            resultInMemory, resultClass != null, false);
184      }
185
186      if (results instanceof AbstractQueryResult) {
187        // Lazy loading results : add listener to the connection so we can get a callback when the connection is flushed.
188        final AbstractQueryResult qr = (AbstractQueryResult)results;
189        final ManagedConnection mconn = getStoreManager().getConnection(ec);
190        ManagedConnectionResourceListener listener = new ManagedConnectionResourceListener() {
191          public void managedConnectionPreClose() {
192            // Disconnect the query from this ManagedConnection (read in unread rows etc)
193            qr.disconnect();
194          }
195          public void managedConnectionPostClose() {}
196          public void resourcePostClose() {
197            mconn.removeListener(this);
198          }
199          public void transactionFlushed() {}
200          public void transactionPreClose() {
201            // Disconnect the query from this ManagedConnection (read in unread rows etc)
202            qr.disconnect();
203          }
204        };
205        mconn.addListener(listener);
206        qr.addConnectionListener(listener);
207      }
208    }
209
210    if (NucleusLogger.QUERY.isDebugEnabled()) {
211      NucleusLogger.QUERY.debug(LOCALISER.msg("021074", "JDOQL", "" + (System.currentTimeMillis() - startTime)));
212    }
213
214    return results;
215  }
216
217  boolean getEvaluateInMemoryWhenUnsupported() {
218    // Use StoreManager setting and allow override in query extensions
219    boolean inmemory = storeMgr.getBooleanProperty("datanucleus.appengine.query.inMemoryWhenUnsupported");
220    return getBooleanExtensionProperty(DatastoreManager.QUERYEXT_INMEMORY_WHEN_UNSUPPORTED, inmemory);
221  }
222
223  // Exposed for tests.
224  DatastoreQuery getDatastoreQuery() {
225    return datastoreQuery;
226  }
227
228  @Override
229  protected boolean supportsTimeout() {
230    return true; // GAE/J Datastore supports timeouts
231  }
232
233  @Override
234  protected void checkParameterTypesAgainstCompilation(Map parameterValues) {
235    // Disabled as part of our DataNuc 1.1.3 upgrade so that we can be
236    // continue to allow multi-value properties and implicit conversions.
237
238    // TODO(maxr) Re-enable the checks that don't break multi-value filters
239    // and implicit conversions.
240  }
241
242  @Override
243  public void setSubclasses(boolean subclasses) {
244    // TODO Enable this!
245    // We support only queries that also return subclasses if all subclasses belong to the same kind.
246    if (subclasses) {
247      DatastoreManager storeMgr = (DatastoreManager) ec.getStoreManager();
248      ClassLoaderResolver clr = ec.getClassLoaderResolver();
249      AbstractClassMetaData acmd = storeMgr.getMetaDataManager().getMetaDataForClass(getCandidateClass(), clr);
250      if (!MetaDataUtils.isNewOrSuperclassTableInheritanceStrategy(acmd)) {
251        throw new NucleusFatalUserException(
252            "The App Engine datastore only supports queries that return subclass entities with the " +
253            "superclass-table interitance mapping strategy.");
254      }
255    }
256    super.setSubclasses(subclasses);
257  }
258}