PageRenderTime 116ms CodeModel.GetById 18ms app.highlight 82ms RepoModel.GetById 1ms app.codeStats 1ms

/tests/com/google/appengine/datanucleus/query/JDOQLQueryTest.java

http://datanucleus-appengine.googlecode.com/
Java | 3513 lines | 3027 code | 379 blank | 107 comment | 69 complexity | 9f9753ac1411133e88541b3c78d14a72 MD5 | raw file

Large files files are truncated, but you can click here to view the full 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 static com.google.appengine.datanucleus.test.jdo.Flight.newFlightEntity;
  19
  20import com.google.appengine.api.datastore.DatastoreFailureException;
  21import com.google.appengine.api.datastore.DatastoreTimeoutException;
  22import com.google.appengine.api.datastore.Entity;
  23import com.google.appengine.api.datastore.Key;
  24import com.google.appengine.api.datastore.KeyFactory;
  25import com.google.appengine.api.datastore.Query.FilterOperator;
  26import com.google.appengine.api.datastore.Query.FilterPredicate;
  27import com.google.appengine.api.datastore.Query.SortDirection;
  28import com.google.appengine.api.datastore.Query.SortPredicate;
  29import com.google.appengine.api.datastore.ShortBlob;
  30import com.google.appengine.api.datastore.dev.LocalDatastoreService;
  31import com.google.appengine.api.users.User;
  32import com.google.appengine.datanucleus.DatastoreManager;
  33import com.google.appengine.datanucleus.DatastoreServiceFactoryInternal;
  34import com.google.appengine.datanucleus.DatastoreServiceInterceptor;
  35import com.google.appengine.datanucleus.ExceptionThrowingDatastoreDelegate;
  36import com.google.appengine.datanucleus.PrimitiveArrays;
  37import com.google.appengine.datanucleus.TestUtils;
  38import com.google.appengine.datanucleus.Utils;
  39import com.google.appengine.datanucleus.WriteBlocker;
  40import com.google.appengine.datanucleus.jdo.JDOTestCase;
  41import com.google.appengine.datanucleus.test.jdo.AbstractBaseClassesJDO.Base1;
  42import com.google.appengine.datanucleus.test.jdo.BidirectionalChildListJDO;
  43import com.google.appengine.datanucleus.test.jdo.BidirectionalChildLongPkListJDO;
  44import com.google.appengine.datanucleus.test.jdo.BidirectionalGrandchildListJDO;
  45import com.google.appengine.datanucleus.test.jdo.Flight;
  46import com.google.appengine.datanucleus.test.jdo.HasBytesJDO;
  47import com.google.appengine.datanucleus.test.jdo.HasEmbeddedJDO;
  48import com.google.appengine.datanucleus.test.jdo.HasEncodedStringPkJDO;
  49import com.google.appengine.datanucleus.test.jdo.HasEncodedStringPkSeparateIdFieldJDO;
  50import com.google.appengine.datanucleus.test.jdo.HasEncodedStringPkSeparateNameFieldJDO;
  51import com.google.appengine.datanucleus.test.jdo.HasEnumJDO;
  52import com.google.appengine.datanucleus.test.jdo.HasKeyAncestorKeyPkJDO;
  53import com.google.appengine.datanucleus.test.jdo.HasKeyAncestorStringPkJDO;
  54import com.google.appengine.datanucleus.test.jdo.HasKeyPkJDO;
  55import com.google.appengine.datanucleus.test.jdo.HasLongPkJDO;
  56import com.google.appengine.datanucleus.test.jdo.HasMultiValuePropsJDO;
  57import com.google.appengine.datanucleus.test.jdo.HasOneToManyKeyPkListJDO;
  58import com.google.appengine.datanucleus.test.jdo.HasOneToManyKeyPkSetJDO;
  59import com.google.appengine.datanucleus.test.jdo.HasOneToManyListJDO;
  60import com.google.appengine.datanucleus.test.jdo.HasOneToManyLongPkListJDO;
  61import com.google.appengine.datanucleus.test.jdo.HasOneToManyLongPkSetJDO;
  62import com.google.appengine.datanucleus.test.jdo.HasOneToManyUnencodedStringPkListJDO;
  63import com.google.appengine.datanucleus.test.jdo.HasOneToManyUnencodedStringPkSetJDO;
  64import com.google.appengine.datanucleus.test.jdo.HasOneToOneJDO;
  65import com.google.appengine.datanucleus.test.jdo.HasOneToOneParentJDO;
  66import com.google.appengine.datanucleus.test.jdo.HasStringAncestorStringPkJDO;
  67import com.google.appengine.datanucleus.test.jdo.HasUnencodedStringPkJDO;
  68import com.google.appengine.datanucleus.test.jdo.KitchenSink;
  69import com.google.appengine.datanucleus.test.jdo.NullDataJDO;
  70import com.google.appengine.datanucleus.test.jdo.Person;
  71import com.google.appengine.datanucleus.test.jdo.UnidirectionalSuperclassTableChildJDO.UnidirTop;
  72import com.google.appengine.datanucleus.test.jpa.Book;
  73import com.google.apphosting.api.ApiProxy;
  74import com.google.apphosting.api.DatastorePb;
  75
  76import junit.framework.Assert;
  77
  78import org.datanucleus.api.jdo.JDOPersistenceManagerFactory;
  79import org.datanucleus.api.jdo.JDOQuery;
  80import org.datanucleus.exceptions.NucleusUserException;
  81import org.datanucleus.query.expression.Expression;
  82import org.datanucleus.store.query.cache.QueryResultsCache;
  83import org.easymock.EasyMock;
  84
  85import java.io.ByteArrayOutputStream;
  86import java.io.IOException;
  87import java.io.ObjectOutputStream;
  88import java.math.BigDecimal;
  89import java.util.Arrays;
  90import java.util.Collection;
  91import java.util.Collections;
  92import java.util.HashMap;
  93import java.util.HashSet;
  94import java.util.Iterator;
  95import java.util.LinkedList;
  96import java.util.List;
  97import java.util.Map;
  98import java.util.Set;
  99import java.util.concurrent.Future;
 100
 101import javax.jdo.Extent;
 102import javax.jdo.JDODataStoreException;
 103import javax.jdo.JDOException;
 104import javax.jdo.JDOFatalUserException;
 105import javax.jdo.JDOUserException;
 106import javax.jdo.Query;
 107import javax.jdo.listener.InstanceLifecycleEvent;
 108import javax.jdo.listener.LoadLifecycleListener;
 109
 110/**
 111 * @author Max Ross <maxr@google.com>
 112 */
 113public class JDOQLQueryTest extends JDOTestCase {
 114
 115  private static final List<SortPredicate> NO_SORTS = Collections.emptyList();
 116  private static final List<FilterPredicate> NO_FILTERS = Collections.emptyList();
 117
 118  private static final FilterPredicate ORIGIN_EQ_2 =
 119      new FilterPredicate("origin", FilterOperator.EQUAL, 2);
 120  private static final FilterPredicate ORIGIN_EQ_2_LITERAL =
 121      new FilterPredicate("origin", FilterOperator.EQUAL, 2L);
 122  private static final FilterPredicate ORIGIN_NEQ_NULL_LITERAL =
 123      new FilterPredicate("origin", FilterOperator.NOT_EQUAL, null);
 124  private static final FilterPredicate ORIGIN_EQ_2STR =
 125      new FilterPredicate("origin", FilterOperator.EQUAL, "2");
 126  private static final FilterPredicate ORIGIN_NEQ_2_LITERAL =
 127      new FilterPredicate("origin", FilterOperator.NOT_EQUAL, 2L);
 128  private static final FilterPredicate DEST_EQ_4_LITERAL =
 129      new FilterPredicate("dest", FilterOperator.EQUAL, 4L);
 130  private static final FilterPredicate ORIG_GT_2_LITERAL =
 131      new FilterPredicate("origin", FilterOperator.GREATER_THAN, 2L);
 132  private static final FilterPredicate ORIG_GTE_2_LITERAL =
 133      new FilterPredicate("origin", FilterOperator.GREATER_THAN_OR_EQUAL, 2L);
 134  private static final FilterPredicate DEST_LT_4_LITERAL =
 135      new FilterPredicate("dest", FilterOperator.LESS_THAN, 4L);
 136  private static final FilterPredicate DEST_LTE_4_LITERAL =
 137      new FilterPredicate("dest", FilterOperator.LESS_THAN_OR_EQUAL, 4L);
 138  private static final SortPredicate ORIG_ASC =
 139      new SortPredicate("origin", SortDirection.ASCENDING);
 140  private static final SortPredicate DESC_DESC =
 141      new SortPredicate("dest", SortDirection.DESCENDING);
 142  private static final FilterPredicate ORIGIN_IN_2_ARGS =
 143      new FilterPredicate("origin", FilterOperator.IN, Arrays.asList("2", 2L));
 144  private static final FilterPredicate ORIGIN_IN_3_ARGS =
 145      new FilterPredicate("origin", FilterOperator.IN, Arrays.asList("2", 2L, false));
 146
 147  @Override
 148  protected void setUp() throws Exception {
 149    super.setUp();
 150    DatastoreServiceInterceptor.install(getStoreManager(), new WriteBlocker());
 151    beginTxn();
 152  }
 153
 154  @Override
 155  protected void tearDown() throws Exception {
 156    if (!pm.isClosed() && pm.currentTransaction().isActive()) {
 157      commitTxn();
 158    }
 159    try {
 160      super.tearDown();
 161    } finally {
 162      DatastoreServiceInterceptor.uninstall();
 163    }
 164  }
 165
 166  public void testUnsupportedFilters() {
 167
 168    Set<Expression.Operator> unsupportedOps = Utils.newHashSet(DatastoreQuery.UNSUPPORTED_OPERATORS);
 169    assertQueryUnsupportedByOrm(Flight.class, "!origin", Expression.OP_NOT, unsupportedOps);
 170    assertQueryUnsupportedByOrm(Flight.class, "(origin + dest) == 4", Expression.OP_ADD, unsupportedOps);
 171    assertQueryUnsupportedByOrm(Flight.class, "origin + dest == 4", Expression.OP_ADD, unsupportedOps);
 172    assertQueryUnsupportedByOrm(Flight.class, "(origin - dest) == 4", Expression.OP_SUB, unsupportedOps);
 173    assertQueryUnsupportedByOrm(Flight.class, "origin - dest == 4", Expression.OP_SUB, unsupportedOps);
 174    assertQueryUnsupportedByOrm(Flight.class, "(origin / dest) == 4", Expression.OP_DIV, unsupportedOps);
 175    assertQueryUnsupportedByOrm(Flight.class, "origin / dest == 4", Expression.OP_DIV, unsupportedOps);
 176    assertQueryUnsupportedByOrm(Flight.class, "(origin * dest) == 4", Expression.OP_MUL, unsupportedOps);
 177    assertQueryUnsupportedByOrm(Flight.class, "origin * dest == 4", Expression.OP_MUL, unsupportedOps);
 178    assertQueryUnsupportedByOrm(Flight.class, "(origin % dest) == 4", Expression.OP_MOD, unsupportedOps);
 179    assertQueryUnsupportedByOrm(Flight.class, "origin % dest == 4", Expression.OP_MOD, unsupportedOps);
 180    assertQueryUnsupportedByOrm(Flight.class, "~origin == 4", Expression.OP_COM, unsupportedOps);
 181    assertQueryUnsupportedByOrm(Flight.class, "!origin == 4", Expression.OP_NOT, unsupportedOps);
 182    assertQueryUnsupportedByOrm(Flight.class, "-origin == 4", Expression.OP_NEG, unsupportedOps);
 183    assertQueryUnsupportedByOrm(Flight.class, "origin instanceof " + Flight.class.getName(),
 184        Expression.OP_IS, unsupportedOps);
 185    assertEquals(Utils.<Expression.Operator>newHashSet(Expression.OP_CONCAT, Expression.OP_LIKE,
 186        Expression.OP_ISNOT), unsupportedOps);
 187    String baseQuery = "select from " + Flight.class.getName() + " where ";
 188    // multiple inequality filters
 189    // TODO(maxr) Make this pass against the real datastore.
 190    // We need to have it return BadRequest instead of NeedIndex for that to
 191    // happen.
 192    assertQueryUnsupportedByDatastore(baseQuery + "(origin > 2 && dest < 4)");
 193    // inequality filter prop is not the same as the first order by prop
 194    assertQueryUnsupportedByDatastore(baseQuery + "origin > 2 order by dest");
 195    // gets split into multiple inequality filters
 196    assertQueryUnsupportedByDatastore(baseQuery + "origin != 2 && dest != 4");
 197
 198    // can't have 'or' on multiple properties
 199    assertQueryRequiresUnsupportedDatastoreFeature(baseQuery + "origin == 'yar' || dest == null");
 200    assertQueryRequiresUnsupportedDatastoreFeature(baseQuery + "origin == 4 && (dest == 'yar' || name == 'yam')");
 201    // TODO This query is flawed - defines a parameter but doesn't provide it (now an error in DN 3.x)
 202//    assertQueryRequiresUnsupportedDatastoreFeature(baseQuery + ":p1.contains(origin) || name == 'yam'");
 203    // can only check equality
 204    assertQueryRequiresUnsupportedDatastoreFeature(baseQuery + "origin > 5 || origin < 2");
 205  }
 206
 207  private void assertQueryRequiresUnsupportedDatastoreFeature(String query) {
 208    Query q = pm.newQuery(query);
 209    q.addExtension(DatastoreManager.QUERYEXT_INMEMORY_WHEN_UNSUPPORTED, "false");
 210    try {
 211      q.execute();
 212      fail("expected JDOUserException->UnsupportedDatastoreFeatureException for query <" + query + ">");
 213    } catch (JDOUserException jdoe) {
 214        if (jdoe.getCause() instanceof DatastoreQuery.UnsupportedDatastoreFeatureException) {
 215          // good
 216        }
 217        else {
 218          throw jdoe;
 219        }
 220    }
 221  }
 222
 223  public void testEvaluateInMemory() {
 224    ds.put(null, newFlightEntity("1", "yar", "bam", 1, 2));
 225    ds.put(null, newFlightEntity("1", "yam", null, 1, 2));
 226
 227    // This is impossible in the datastore, so run totally in-memory
 228    String query = "SELECT FROM " + Flight.class.getName() + " WHERE origin == 'yar' || dest == null";
 229    Query q = pm.newQuery(query);
 230    q.addExtension("datanucleus.query.evaluateInMemory", "true");
 231    try {
 232      List<Flight> results = (List<Flight>) q.execute();
 233      Assert.assertEquals("Number of results was wrong", 2, results.size());
 234    } catch (JDOException jdoe) {
 235      fail("Threw exception when evaluating query in-memory, but should have run");
 236    }
 237  }
 238
 239  public void testCacheQueryResults() {
 240    ds.put(null, newFlightEntity("1", "yar", "bam", 1, 2));
 241    ds.put(null, newFlightEntity("1", "yam", null, 1, 2));
 242
 243    QueryResultsCache cache = null;
 244    try {
 245      String query = "SELECT FROM " + Flight.class.getName();
 246      Query q = pm.newQuery(query);
 247      q.addExtension("datanucleus.query.results.cached", "true");
 248      try {
 249        List<Flight> results = (List<Flight>) q.execute();
 250        Assert.assertEquals("Number of results was wrong", 2, results.size());
 251      } catch (JDOException jdoe) {
 252        fail("Threw exception when evaluating query and caching results : " + jdoe.getMessage());
 253      }
 254      q.closeAll();
 255      if (pm.currentTransaction().isActive()) {
 256        pm.currentTransaction().rollback();
 257      }
 258      pm.close();
 259      cache = 
 260        ((JDOPersistenceManagerFactory)pmf).getNucleusContext().getStoreManager().getQueryManager().getQueryResultsCache();
 261      assertEquals("Number of entries in the query results cache is wrong", 1, cache.size());
 262
 263      pm = pmf.getPersistenceManager();
 264      Query q2 = pm.newQuery(query);
 265      try {
 266        List<Flight> results = (List<Flight>) q2.execute();
 267        Assert.assertEquals("Number of results was wrong", 2, results.size());
 268      } catch (JDOException jdoe) {
 269        fail("Threw exception when evaluating query with cached results : " + jdoe.getMessage());
 270      }
 271      q2.closeAll();
 272    } finally {
 273      // Evict the cached results
 274      cache.evictAll();
 275    }
 276  }
 277
 278  public void testCandidateCollectionInMemory() {
 279    ds.put(null, newFlightEntity("1", "yar", "bam", 1, 2));
 280    ds.put(null, newFlightEntity("1", "yam", null, 1, 2));
 281
 282    Collection<Flight> coll = new HashSet<Flight>();
 283    Iterator<Flight> iter = pm.getExtent(Flight.class).iterator();
 284    while (iter.hasNext()) {
 285      coll.add(iter.next());
 286    }
 287
 288    // Query is impossible in-datastore, and run against candidates so has to be in-memory
 289    String query = "SELECT FROM " + Flight.class.getName() + " WHERE origin == 'yar' || dest == null";
 290    Query q = pm.newQuery(query);
 291    q.setCandidates(coll);
 292    try {
 293       List<Flight> results = (List<Flight>) q.execute();
 294       Assert.assertEquals("Number of results was wrong", 2, results.size());
 295    } catch (JDOException jdoe) {
 296      fail("Threw exception when evaluating query in-memory, but should have run");
 297    }
 298  }
 299
 300  public void testSupportedFilters() {
 301    assertQuerySupported(Flight.class, "", NO_FILTERS, NO_SORTS);
 302    assertQuerySupported(Flight.class, "origin == 2", Utils.newArrayList(ORIGIN_EQ_2_LITERAL), NO_SORTS);
 303    assertQuerySupported(
 304        Flight.class, "origin == \"2\"", Utils.newArrayList(ORIGIN_EQ_2STR), NO_SORTS);
 305    assertQuerySupported(Flight.class, "(origin == 2)", Utils.newArrayList(ORIGIN_EQ_2_LITERAL), NO_SORTS);
 306    assertQuerySupported(Flight.class, "origin == 2 && dest == 4", Utils.newArrayList(ORIGIN_EQ_2_LITERAL,
 307        DEST_EQ_4_LITERAL), NO_SORTS);
 308    assertQuerySupported(Flight.class, "(origin == 2 && dest == 4)", Utils.newArrayList(ORIGIN_EQ_2_LITERAL,
 309        DEST_EQ_4_LITERAL), NO_SORTS);
 310    assertQuerySupported(Flight.class, "(origin == 2) && (dest == 4)", Utils.newArrayList(
 311        ORIGIN_EQ_2_LITERAL, DEST_EQ_4_LITERAL), NO_SORTS);
 312
 313    assertQuerySupported(Flight.class, "origin > 2", Utils.newArrayList(ORIG_GT_2_LITERAL), NO_SORTS);
 314    assertQuerySupported(Flight.class, "origin >= 2", Utils.newArrayList(ORIG_GTE_2_LITERAL), NO_SORTS);
 315    assertQuerySupported(Flight.class, "dest < 4", Utils.newArrayList(DEST_LT_4_LITERAL), NO_SORTS);
 316    assertQuerySupported(Flight.class, "dest <= 4", Utils.newArrayList(DEST_LTE_4_LITERAL), NO_SORTS);
 317
 318    assertQuerySupported("select from " + Flight.class.getName() + " order by origin asc",
 319        NO_FILTERS, Utils.newArrayList(ORIG_ASC));
 320    assertQuerySupported("select from " + Flight.class.getName() + " order by dest desc",
 321        NO_FILTERS, Utils.newArrayList(DESC_DESC));
 322    assertQuerySupported("select from " + Flight.class.getName()
 323        + " order by origin asc, dest desc", NO_FILTERS, Utils.newArrayList(ORIG_ASC, DESC_DESC));
 324
 325    assertQuerySupported("select from " + Flight.class.getName()
 326        + " where origin == 2 && dest == 4 order by origin asc, dest desc",
 327        Utils.newArrayList(ORIGIN_EQ_2_LITERAL, DEST_EQ_4_LITERAL), Utils.newArrayList(ORIG_ASC, DESC_DESC));
 328    assertQuerySupported(Flight.class, "origin != 2", Utils.newArrayList(ORIGIN_NEQ_2_LITERAL), NO_SORTS);
 329    assertQuerySupported("select from " + Flight.class.getName() + " where origin != null",
 330        Utils.newArrayList(ORIGIN_NEQ_NULL_LITERAL), NO_SORTS);
 331    assertQuerySupported(Flight.class, "origin == '2' || origin == 2",
 332                         Utils.newArrayList(ORIGIN_IN_2_ARGS), NO_SORTS);
 333    assertQuerySupported(Flight.class, "origin == '2' || origin == 2 || origin == false",
 334                         Utils.newArrayList(ORIGIN_IN_3_ARGS), NO_SORTS);
 335    assertQuerySupported(Flight.class, ":p1.contains(origin)",
 336                         Utils.newArrayList(ORIGIN_IN_2_ARGS), NO_SORTS, Arrays.asList("2", 2L));
 337    assertQuerySupported(Flight.class, ":p1.contains(origin)",
 338                         Utils.newArrayList(ORIGIN_IN_3_ARGS), NO_SORTS, Arrays.asList("2", 2L, false));
 339    assertQuerySupported(Flight.class, "(origin == '2' || origin == 2) && dest == 4",
 340                         Utils.newArrayList(DEST_EQ_4_LITERAL, ORIGIN_IN_2_ARGS), NO_SORTS);
 341    assertQuerySupported(Flight.class, ":p1.contains(origin) && dest == 4",
 342                         Utils.newArrayList(ORIGIN_IN_2_ARGS, DEST_EQ_4_LITERAL), NO_SORTS, Arrays.asList("2", 2L));
 343    assertQuerySupported(Flight.class, "(origin == '2' || origin == 2 || origin == false) && dest == 4",
 344                         Utils.newArrayList(DEST_EQ_4_LITERAL, ORIGIN_IN_3_ARGS), NO_SORTS);
 345    assertQuerySupported(Flight.class, ":p1.contains(origin) && dest == 4",
 346                         Utils.newArrayList(ORIGIN_IN_3_ARGS, DEST_EQ_4_LITERAL), NO_SORTS, Arrays.asList("2", 2L, false));
 347  }
 348
 349  public void testBindVariables() {
 350    String queryStr = "select from " + Flight.class.getName() + " where origin == two ";
 351    assertQuerySupported(queryStr + " parameters String two",
 352        Utils.newArrayList(ORIGIN_EQ_2STR), NO_SORTS, "2");
 353    assertQuerySupportedWithExplicitParams(queryStr,
 354        Utils.newArrayList(ORIGIN_EQ_2STR), NO_SORTS, "String two", "2");
 355
 356    queryStr = "select from " + Flight.class.getName() + " where origin == two && dest == four ";
 357    assertQuerySupported(queryStr + "parameters int two, int four",
 358        Utils.newArrayList(ORIGIN_EQ_2, DEST_EQ_4_LITERAL), NO_SORTS, 2, 4L);
 359    assertQuerySupportedWithExplicitParams(queryStr,
 360        Utils.newArrayList(ORIGIN_EQ_2, DEST_EQ_4_LITERAL), NO_SORTS, "int two, int four", 2, 4L);
 361
 362    queryStr = "select from " + Flight.class.getName() + " where origin == two && dest == four ";
 363    String orderStr = "order by origin asc, dest desc";
 364    assertQuerySupported(queryStr + "parameters int two, int four " + orderStr,
 365        Utils.newArrayList(ORIGIN_EQ_2, DEST_EQ_4_LITERAL),
 366        Utils.newArrayList(ORIG_ASC, DESC_DESC), 2, 4L);
 367    assertQuerySupportedWithExplicitParams(queryStr + orderStr,
 368        Utils.newArrayList(ORIGIN_EQ_2, DEST_EQ_4_LITERAL),
 369        Utils.newArrayList(ORIG_ASC, DESC_DESC), "int two, int four", 2, 4L);
 370  }
 371
 372  public void test2Equals2OrderBy() {
 373    ds.put(null, newFlightEntity("1", "yam", "bam", 1, 2));
 374    ds.put(null, newFlightEntity("2", "yam", "bam", 1, 1));
 375    ds.put(null, newFlightEntity("3", "yam", "bam", 2, 1));
 376    ds.put(null ,newFlightEntity("4", "yam", "bam", 2, 2));
 377    ds.put(null, newFlightEntity("5", "notyam", "bam", 2, 2));
 378    ds.put(null, newFlightEntity("5", "yam", "notbam", 2, 2));
 379    Query q = pm.newQuery(
 380        "select from " + Flight.class.getName()
 381            + " where origin == \"yam\" && dest == \"bam\""
 382            + " order by you asc, me desc");
 383    @SuppressWarnings("unchecked")
 384    List<Flight> result = (List<Flight>) q.execute();
 385    assertEquals(4, result.size());
 386
 387    assertEquals("1", result.get(0).getName());
 388    assertEquals("2", result.get(1).getName());
 389    assertEquals("4", result.get(2).getName());
 390    assertEquals("3", result.get(3).getName());
 391  }
 392
 393  public void testSetFilter() {
 394    ds.put(null, newFlightEntity("1", "yam", "bam", 1, 1));
 395    ds.put(null, newFlightEntity("2", "yam", "bam", 2, 2));
 396    Query q = pm.newQuery(
 397        "select from " + Flight.class.getName());
 398    q.setFilter("origin == \"yam\" && you == 2");
 399    @SuppressWarnings("unchecked")
 400    List<Flight> result = (List<Flight>) q.execute();
 401    assertEquals(1, result.size());
 402  }
 403
 404  public void testSetInvalidFilter() {
 405    Query q = pm.newQuery(
 406        "select from " + Flight.class.getName());
 407    q.setFilter("origin == \"yam\" AND you == 2");
 408    try {
 409      q.execute();
 410      fail("expected exception");
 411    } catch (JDOUserException e) {
 412      // good
 413    }
 414  }
 415
 416  public void testDefaultOrderingIsAsc() {
 417    ds.put(null, newFlightEntity("1", "yam", "bam", 1, 2));
 418    ds.put(null, newFlightEntity("2", "yam", "bam", 1, 1));
 419    ds.put(null, newFlightEntity("3", "yam", "bam", 2, 1));
 420    ds.put(null, newFlightEntity("4", "yam", "bam", 2, 2));
 421    ds.put(null, newFlightEntity("5", "notyam", "bam", 2, 2));
 422    ds.put(null, newFlightEntity("5", "yam", "notbam", 2, 2));
 423    Query q = pm.newQuery(
 424        "select from " + Flight.class.getName()
 425            + " where origin == \"yam\" && dest == \"bam\""
 426            + " order by you");
 427    @SuppressWarnings("unchecked")
 428    List<Flight> result = (List<Flight>) q.execute();
 429    assertEquals(4, result.size());
 430
 431    assertEquals("1", result.get(0).getName());
 432    assertEquals("2", result.get(1).getName());
 433    assertEquals("3", result.get(2).getName());
 434    assertEquals("4", result.get(3).getName());
 435  }
 436
 437  public void testLimitQuery() {
 438    ds.put(null, newFlightEntity("1", "yam", "bam", 1, 2));
 439    ds.put(null, newFlightEntity("2", "yam", "bam", 1, 1));
 440    ds.put(null, newFlightEntity("3", "yam", "bam", 2, 1));
 441    ds.put(null, newFlightEntity("4", "yam", "bam", 2, 2));
 442    ds.put(null, newFlightEntity("5", "notyam", "bam", 2, 2));
 443    ds.put(null, newFlightEntity("5", "yam", "notbam", 2, 2));
 444    Query q = pm.newQuery(
 445        "select from " + Flight.class.getName()
 446            + " where origin == \"yam\" && dest == \"bam\""
 447            + " order by you asc, me desc");
 448
 449    q.setRange(0, 1);
 450    @SuppressWarnings("unchecked")
 451    List<Flight> result1 = (List<Flight>) q.execute();
 452    assertEquals(1, result1.size());
 453    assertEquals("1", result1.get(0).getName());
 454
 455    q.setRange(0, Long.MAX_VALUE);
 456    @SuppressWarnings("unchecked")
 457    List<Flight> result2 = (List<Flight>) q.execute();
 458    assertEquals(4, result2.size());
 459    assertEquals("1", result2.get(0).getName());
 460
 461    q.setRange(0, 0);
 462    @SuppressWarnings("unchecked")
 463    List<Flight> result3 = (List<Flight>) q.execute();
 464    assertEquals(0, result3.size());
 465  }
 466
 467  public void testOffsetQuery() {
 468    ds.put(null, newFlightEntity("1", "yam", "bam", 1, 2));
 469    ds.put(null, newFlightEntity("2", "yam", "bam", 1, 1));
 470    ds.put(null, newFlightEntity("3", "yam", "bam", 2, 1));
 471    ds.put(null, newFlightEntity("4", "yam", "bam", 2, 2));
 472    ds.put(null, newFlightEntity("5", "notyam", "bam", 2, 2));
 473    ds.put(null, newFlightEntity("5", "yam", "notbam", 2, 2));
 474    Query q = pm.newQuery(
 475        "select from " + Flight.class.getName()
 476            + " where origin == \"yam\" && dest == \"bam\""
 477            + " order by you asc, me desc");
 478
 479    q.setRange(0, Long.MAX_VALUE);
 480    @SuppressWarnings("unchecked")
 481    List<Flight> result1 = (List<Flight>) q.execute();
 482    assertEquals(4, result1.size());
 483    assertEquals("1", result1.get(0).getName());
 484
 485    q.setRange(1, Long.MAX_VALUE);
 486    @SuppressWarnings("unchecked")
 487    List<Flight> result2 = (List<Flight>) q.execute();
 488    assertEquals(3, result2.size());
 489    assertEquals("2", result2.get(0).getName());
 490
 491    q.setRange(0, Long.MAX_VALUE);
 492    @SuppressWarnings("unchecked")
 493    List<Flight> result3 = (List<Flight>) q.execute();
 494    assertEquals(4, result3.size());
 495    assertEquals("1", result3.get(0).getName());
 496  }
 497
 498  public void testOffsetLimitQuery() {
 499    ds.put(null, newFlightEntity("1", "yam", "bam", 1, 2));
 500    ds.put(null, newFlightEntity("2", "yam", "bam", 1, 1));
 501    ds.put(null, newFlightEntity("3", "yam", "bam", 2, 1));
 502    ds.put(null, newFlightEntity("4", "yam", "bam", 2, 2));
 503    ds.put(null, newFlightEntity("5", "notyam", "bam", 2, 2));
 504    ds.put(null, newFlightEntity("5", "yam", "notbam", 2, 2));
 505    Query q = pm.newQuery(
 506        "select from " + Flight.class.getName()
 507            + " where origin == \"yam\" && dest == \"bam\""
 508            + " order by you asc, me desc");
 509
 510    q.setRange(0, 0);
 511    @SuppressWarnings("unchecked")
 512    List<Flight> result1 = (List<Flight>) q.execute();
 513    assertEquals(0, result1.size());
 514
 515    q.setRange(1, 0);
 516    @SuppressWarnings("unchecked")
 517    List<Flight> result2 = (List<Flight>) q.execute();
 518    assertEquals(0, result2.size());
 519
 520    q.setRange(0, 1);
 521    @SuppressWarnings("unchecked")
 522    List<Flight> result3 = (List<Flight>) q.execute();
 523    assertEquals(1, result3.size());
 524    assertEquals("1", result3.get(0).getName());
 525
 526    q.setRange(0, 2);
 527    @SuppressWarnings("unchecked")
 528    List<Flight> result4 = (List<Flight>) q.execute();
 529    assertEquals(2, result4.size());
 530    assertEquals("1", result4.get(0).getName());
 531
 532    q.setRange(1, 2);
 533    @SuppressWarnings("unchecked")
 534    List<Flight> result5 = (List<Flight>) q.execute();
 535    assertEquals(1, result5.size());
 536    assertEquals("2", result5.get(0).getName());
 537
 538    q.setRange(2, 5);
 539    @SuppressWarnings("unchecked")
 540    List<Flight> result6 = (List<Flight>) q.execute();
 541    assertEquals(2, result6.size());
 542    assertEquals("4", result6.get(0).getName());
 543
 544    q.setRange(2, 2);
 545    @SuppressWarnings("unchecked")
 546    List<Flight> result7 = (List<Flight>) q.execute();
 547    assertEquals(0, result7.size());
 548
 549    q.setRange(2, 1);
 550    @SuppressWarnings("unchecked")
 551    List<Flight> result8 = (List<Flight>) q.execute();
 552    assertEquals(0, result8.size());
 553  }
 554
 555  public void testOffsetLimitSingleStringQuery() {
 556    ds.put(null, newFlightEntity("1", "yam", "bam", 1, 2));
 557    ds.put(null, newFlightEntity("2", "yam", "bam", 1, 1));
 558    ds.put(null, newFlightEntity("3", "yam", "bam", 2, 1));
 559    ds.put(null, newFlightEntity("4", "yam", "bam", 2, 2));
 560    ds.put(null, newFlightEntity("5", "notyam", "bam", 2, 2));
 561    ds.put(null, newFlightEntity("5", "yam", "notbam", 2, 2));
 562    String queryFormat =
 563        "select from " + Flight.class.getName()
 564            + " where origin == \"yam\" && dest == \"bam\""
 565            + " order by you asc, me desc range %d,%d";
 566    Query q = pm.newQuery(String.format(queryFormat, 0, 0));
 567    @SuppressWarnings("unchecked")
 568    List<Flight> result1 = (List<Flight>) q.execute();
 569    assertEquals(0, result1.size());
 570
 571    q = pm.newQuery(String.format(queryFormat, 1, 0));
 572    @SuppressWarnings("unchecked")
 573    List<Flight> result2 = (List<Flight>) q.execute();
 574    assertEquals(0, result2.size());
 575
 576    q = pm.newQuery(String.format(queryFormat, 0, 1));
 577    @SuppressWarnings("unchecked")
 578    List<Flight> result3 = (List<Flight>) q.execute();
 579    assertEquals(1, result3.size());
 580
 581    q = pm.newQuery(String.format(queryFormat, 0, 2));
 582    @SuppressWarnings("unchecked")
 583    List<Flight> result4 = (List<Flight>) q.execute();
 584    assertEquals(2, result4.size());
 585    assertEquals("1", result4.get(0).getName());
 586
 587    q = pm.newQuery(String.format(queryFormat, 1, 2));
 588    @SuppressWarnings("unchecked")
 589    List<Flight> result5 = (List<Flight>) q.execute();
 590    assertEquals(1, result5.size());
 591    assertEquals("2", result5.get(0).getName());
 592
 593    q = pm.newQuery(String.format(queryFormat, 2, 5));
 594    @SuppressWarnings("unchecked")
 595    List<Flight> result6 = (List<Flight>) q.execute();
 596    assertEquals(2, result6.size());
 597    assertEquals("4", result6.get(0).getName());
 598
 599    q = pm.newQuery(String.format(queryFormat, 2, 2));
 600    @SuppressWarnings("unchecked")
 601    List<Flight> result7 = (List<Flight>) q.execute();
 602    assertEquals(0, result7.size());
 603
 604    q = pm.newQuery(String.format(queryFormat, 2, 1));
 605    @SuppressWarnings("unchecked")
 606    List<Flight> result8 = (List<Flight>) q.execute();
 607    assertEquals(0, result8.size());
 608  }
 609
 610  public void testSerialization() throws IOException {
 611    Query q = pm.newQuery("select from " + Flight.class.getName());
 612    q.execute();
 613
 614    JDOQLQuery innerQuery = (JDOQLQuery)((JDOQuery)q).getInternalQuery();
 615    ByteArrayOutputStream baos = new ByteArrayOutputStream();
 616    ObjectOutputStream oos = new ObjectOutputStream(baos);
 617    // the fact that this doesn't blow up is the test
 618    oos.writeObject(innerQuery);
 619  }
 620
 621  public void testKeyQuery_StringPk() {
 622    Entity flightEntity = newFlightEntity("1", "yam", "bam", 1, 2);
 623    ds.put(null, flightEntity);
 624
 625    Query q = pm.newQuery(
 626        "select from " + Flight.class.getName() + " where id == key parameters String key");
 627    @SuppressWarnings("unchecked")
 628    List<Flight> flights = (List<Flight>) q.execute(KeyFactory.keyToString(flightEntity.getKey()));
 629    assertEquals(1, flights.size());
 630    assertEquals(flightEntity.getKey(), KeyFactory.stringToKey(flights.get(0).getId()));
 631  }
 632
 633  public void testKeyQuery_KeyPk() {
 634    Entity entityWithName = new Entity(HasKeyPkJDO.class.getSimpleName(), "blarg");
 635    Entity entityWithId = new Entity(HasKeyPkJDO.class.getSimpleName());
 636    ds.put(null, entityWithName);
 637    ds.put(null, entityWithId);
 638
 639    Query q = pm.newQuery(
 640        "select from " + HasKeyPkJDO.class.getName() +
 641        " where key == mykey parameters " + Key.class.getName() + " mykey");
 642    @SuppressWarnings("unchecked")
 643    List<HasKeyPkJDO> result = (List<HasKeyPkJDO>) q.execute(entityWithName.getKey());
 644    assertEquals(1, result.size());
 645    assertEquals(entityWithName.getKey(), result.get(0).getKey());
 646
 647    q = pm.newQuery(
 648        "select from " + HasKeyPkJDO.class.getName() +
 649        " where key == mykey parameters " + Key.class.getName() + " mykey");
 650    @SuppressWarnings("unchecked")
 651    List<HasKeyPkJDO> result2 = (List<HasKeyPkJDO>) q.execute(entityWithId.getKey());
 652    assertEquals(1, result2.size());
 653    assertEquals(entityWithId.getKey(), result2.get(0).getKey());
 654
 655    q = pm.newQuery(
 656        "select from " + HasKeyPkJDO.class.getName() +
 657        " where key == mykeyname parameters " + String.class.getName() + " mykeyname");
 658    @SuppressWarnings("unchecked")
 659    List<HasKeyPkJDO> result3 = (List<HasKeyPkJDO>) q.execute(entityWithName.getKey().getName());
 660    assertEquals(1, result3.size());
 661    assertEquals(entityWithName.getKey(), result3.get(0).getKey());
 662
 663    q = pm.newQuery(
 664        "select from " + HasKeyPkJDO.class.getName() +
 665        " where key == mykeyid parameters " + String.class.getName() + " mykeyid");
 666    @SuppressWarnings("unchecked")
 667    List<HasKeyPkJDO> result4 = (List<HasKeyPkJDO>) q.execute(entityWithId.getKey().getId());
 668    assertEquals(1, result4.size());
 669    assertEquals(entityWithId.getKey(), result4.get(0).getKey());
 670  }
 671
 672  public void testKeyQueryWithSorts() {
 673    Entity flightEntity = newFlightEntity("1", "yam", "bam", 1, 2);
 674    ds.put(null, flightEntity);
 675
 676    Query q = pm.newQuery(
 677        "select from " + Flight.class.getName()
 678            + " where id == key parameters String key order by id asc");
 679    @SuppressWarnings("unchecked")
 680    List<Flight> flights = (List<Flight>) q.execute(KeyFactory.keyToString(flightEntity.getKey()));
 681    assertEquals(1, flights.size());
 682    assertEquals(flightEntity.getKey(), KeyFactory.stringToKey(flights.get(0).getId()));
 683  }
 684
 685  public void testKeyQuery_MultipleFilters() {
 686    Entity flightEntity = newFlightEntity("1", "yam", "bam", 1, 2);
 687    ds.put(null, flightEntity);
 688
 689    Query q = pm.newQuery(
 690        "select from " + Flight.class.getName()
 691            + " where id == key && origin == \"yam\" parameters String key");
 692    @SuppressWarnings("unchecked")
 693    List<Flight> flights = (List<Flight>) q.execute(KeyFactory.keyToString(flightEntity.getKey()));
 694    assertEquals(1, flights.size());
 695    assertEquals(flightEntity.getKey(), KeyFactory.stringToKey(flights.get(0).getId()));
 696  }
 697
 698  public void testKeyQuery_NonEqualityFilter() {
 699    Entity flightEntity1 = newFlightEntity("1", "yam", "bam", 1, 2);
 700    ds.put(null, flightEntity1);
 701    Entity flightEntity2 = newFlightEntity("1", "yam", "bam", 1, 2);
 702    ds.put(null, flightEntity2);
 703
 704    Query q = pm.newQuery(
 705        "select from " + Flight.class.getName() + " where id > key parameters String key");
 706    @SuppressWarnings("unchecked")
 707    List<Flight> flights = (List<Flight>) q.execute(KeyFactory.keyToString(flightEntity1.getKey()));
 708    assertEquals(1, flights.size());
 709    assertEquals(flightEntity2.getKey(), KeyFactory.stringToKey(flights.get(0).getId()));
 710  }
 711
 712  public void testKeyQuery_SortByKey() {
 713    Entity flightEntity1 = newFlightEntity("1", "yam", "bam", 1, 2);
 714    ds.put(null, flightEntity1);
 715
 716    Entity flightEntity2 = newFlightEntity("1", "yam", "bam", 1, 2);
 717    ds.put(null, flightEntity2);
 718
 719    Query q = pm.newQuery(
 720        "select from " + Flight.class.getName() + " where origin == 'yam' order by id DESC");
 721    @SuppressWarnings("unchecked")
 722    List<Flight> flights = (List<Flight>) q.execute();
 723    assertEquals(2, flights.size());
 724    assertEquals(flightEntity2.getKey(), KeyFactory.stringToKey(flights.get(0).getId()));
 725    assertEquals(flightEntity1.getKey(), KeyFactory.stringToKey(flights.get(1).getId()));
 726  }
 727
 728  public void testKeyQuery_FilterAndSortByKeyComponent() {
 729    // filter by pk-id
 730    assertQueryUnsupportedByDatastore(
 731        "select from " + HasEncodedStringPkSeparateIdFieldJDO.class.getName() + " where id == 4");
 732    // sort by pk-id
 733    assertQueryUnsupportedByDatastore(
 734        "select from " + HasEncodedStringPkSeparateIdFieldJDO.class.getName() + " order by id");
 735    // filter by pk-id
 736    assertQueryUnsupportedByDatastore(
 737        "select from " + HasEncodedStringPkSeparateNameFieldJDO.class.getName() + " where name == 4");
 738    // sort by pk-id
 739    assertQueryUnsupportedByDatastore(
 740        "select from " + HasEncodedStringPkSeparateNameFieldJDO.class.getName() + " order by name");
 741  }
 742
 743  public void testAncestorQueryWithStringAncestor() {
 744    Entity flightEntity = newFlightEntity("1", "yam", "bam", 1, 2);
 745    ds.put(null, flightEntity);
 746    Entity hasAncestorEntity = new Entity(HasStringAncestorStringPkJDO.class.getSimpleName(), flightEntity.getKey());
 747    ds.put(null, hasAncestorEntity);
 748
 749    Query q = pm.newQuery(
 750        "select from " + HasStringAncestorStringPkJDO.class.getName()
 751            + " where ancestorId == ancId parameters String ancId");
 752    @SuppressWarnings("unchecked")
 753    List<HasStringAncestorStringPkJDO> haList =
 754        (List<HasStringAncestorStringPkJDO>) q.execute(KeyFactory.keyToString(flightEntity.getKey()));
 755    assertEquals(1, haList.size());
 756    assertEquals(flightEntity.getKey(), KeyFactory.stringToKey(haList.get(0).getAncestorId()));
 757
 758    assertEquals(
 759        flightEntity.getKey(), getDatastoreQuery(q).getLatestDatastoreQuery().getAncestor());
 760    assertEquals(NO_FILTERS, getFilterPredicates(q));
 761    assertEquals(NO_SORTS, getSortPredicates(q));
 762  }
 763
 764  public void testAncestorQueryWithKeyAncestor() {
 765    Entity e = new Entity("parent");
 766    ds.put(null, e);
 767    Entity childEntity = new Entity(HasKeyAncestorStringPkJDO.class.getSimpleName(), e.getKey());
 768    ds.put(null, childEntity);
 769
 770    Query q = pm.newQuery(
 771        "select from " + HasKeyAncestorStringPkJDO.class.getName()
 772            + " where ancestorKey == ancId parameters " + Key.class.getName() + " ancId");
 773    @SuppressWarnings("unchecked")
 774    List<HasKeyAncestorStringPkJDO> result =
 775        (List<HasKeyAncestorStringPkJDO>) q.execute(e.getKey());
 776    assertEquals(1, result.size());
 777    assertEquals(e.getKey(), result.get(0).getAncestorKey());
 778  }
 779
 780  public void testIllegalAncestorQuery_BadOperator() {
 781    Entity flightEntity = newFlightEntity("1", "yam", "bam", 1, 2);
 782    ds.put(null, flightEntity);
 783    Entity hasAncestorEntity = new Entity(HasStringAncestorStringPkJDO.class.getName(), flightEntity.getKey());
 784    ds.put(null, hasAncestorEntity);
 785
 786    Query q = pm.newQuery(
 787        "select from " + HasStringAncestorStringPkJDO.class.getName()
 788            + " where ancestorId > ancId parameters String ancId");
 789    q.addExtension(DatastoreManager.QUERYEXT_INMEMORY_WHEN_UNSUPPORTED, "false");
 790    try {
 791      q.execute(KeyFactory.keyToString(flightEntity.getKey()));
 792      fail ("expected udfe");
 793    } catch (JDOUserException jdoe) {
 794        if (jdoe.getCause() instanceof DatastoreQuery.UnsupportedDatastoreFeatureException) {
 795          // good
 796        }
 797        else {
 798          throw jdoe;
 799        }
 800    }
 801  }
 802
 803  public void testSortByFieldWithCustomColumn() {
 804    ds.put(null, newFlightEntity("1", "yam", "bam", 1, 2, 400));
 805    ds.put(null, newFlightEntity("2", "yam", "bam", 1, 1, 300));
 806    ds.put(null, newFlightEntity("3", "yam", "bam", 2, 1, 200));
 807    Query q = pm.newQuery(
 808        "select from " + Flight.class.getName()
 809            + " where origin == \"yam\" && dest == \"bam\""
 810            + " order by flightNumber asc");
 811    @SuppressWarnings("unchecked")
 812    List<Flight> result = (List<Flight>) q.execute();
 813    assertEquals(3, result.size());
 814
 815    assertEquals("3", result.get(0).getName());
 816    assertEquals("2", result.get(1).getName());
 817    assertEquals("1", result.get(2).getName());
 818  }
 819
 820  public void testIllegalAncestorQuery_SortByAncestor() {
 821    Entity flightEntity = newFlightEntity("1", "yam", "bam", 1, 2);
 822    ds.put(null, flightEntity);
 823    Entity hasAncestorEntity = new Entity(HasStringAncestorStringPkJDO.class.getName(), flightEntity.getKey());
 824    ds.put(null, hasAncestorEntity);
 825
 826    Query q = pm.newQuery(
 827        "select from " + HasStringAncestorStringPkJDO.class.getName()
 828            + " where ancestorId == ancId parameters String ancId order by ancestorId ASC");
 829    q.addExtension(DatastoreManager.QUERYEXT_INMEMORY_WHEN_UNSUPPORTED, "false");
 830    try {
 831      q.execute(KeyFactory.keyToString(flightEntity.getKey()));
 832      fail ("expected udfe");
 833    } catch (JDOUserException jdoe) {
 834        if (jdoe.getCause() instanceof DatastoreQuery.UnsupportedDatastoreFeatureException) {
 835          // good
 836        }
 837        else {
 838          throw jdoe;
 839        }
 840    }
 841  }
 842
 843  private interface FlightProvider {
 844    Flight getFlight(Key key);
 845  }
 846
 847  private class AttachedFlightProvider implements FlightProvider {
 848    public Flight getFlight(Key key) {
 849      return pm.getObjectById(Flight.class, key);
 850    }
 851  }
 852
 853  private class TransientFlightProvider implements FlightProvider {
 854    public Flight getFlight(Key key) {
 855      Flight f = new Flight();
 856      f.setId(KeyFactory.keyToString(key));
 857      return f;
 858    }
 859  }
 860
 861  private void testFilterByChildObject(FlightProvider fp) {
 862    Entity parentEntity = new Entity(HasOneToOneJDO.class.getSimpleName());
 863    ds.put(null, parentEntity);
 864    Entity flightEntity = newFlightEntity(parentEntity.getKey(), null, "f", "bos", "mia", 2, 4, 33);
 865    ds.put(null, flightEntity);
 866
 867    Flight flight = fp.getFlight(flightEntity.getKey());
 868    Query q = pm.newQuery(
 869        "select from " + HasOneToOneJDO.class.getName()
 870        + " where flight == f parameters " + Flight.class.getName() + " f");
 871    List<HasOneToOneJDO> result = (List<HasOneToOneJDO>) q.execute(flight);
 872    assertEquals(1, result.size());
 873    assertEquals(parentEntity.getKey(), KeyFactory.stringToKey(result.get(0).getId()));
 874  }
 875
 876  public void testFilterByChildObject() {
 877    testFilterByChildObject(new AttachedFlightProvider());
 878    commitTxn();
 879    beginTxn();
 880    testFilterByChildObject(new TransientFlightProvider());
 881  }
 882
 883  public void testFilterByNullChildObject() {
 884    Entity parentEntity = new Entity(HasOneToOneJDO.class.getSimpleName());
 885    ds.put(null, parentEntity);
 886    Entity flightEntity = newFlightEntity(parentEntity.getKey(), null, "f", "bos", "mia", 2, 4, 33);
 887    ds.put(null, flightEntity);
 888
 889    Query q = pm.newQuery(
 890        "select from " + HasOneToOneJDO.class.getName()
 891        + " where flight == f parameters " + Flight.class.getName() + " f");
 892    q.addExtension(DatastoreManager.QUERYEXT_INMEMORY_WHEN_UNSUPPORTED, "false");
 893    try {
 894      q.execute(null);
 895      fail("expected exception");
 896    } catch (JDOFatalUserException e) {
 897      // good
 898    }
 899  }
 900
 901  public void testContains() {
 902    Entity e = Flight.newFlightEntity("name1", "bos1", "mia1", 23, 24);
 903    Entity e2 = Flight.newFlightEntity("name2", "bos2", null, 25, 26);
 904    Entity e3 = Flight.newFlightEntity("name3", "bos3", "mia2", 27, 28);
 905    ds.put(null, Arrays.asList(e, e2, e3));
 906
 907    Query q = pm.newQuery("select from " + Flight.class.getName() + " where :p1.contains(name)");
 908    List<Flight> flights = (List<Flight>) q.execute(Arrays.asList("name1", "name3"));
 909    assertEquals(2, flights.size());
 910    assertEquals(KeyFactory.keyToString(e.getKey()), flights.get(0).getId());
 911    assertEquals(KeyFactory.keyToString(e3.getKey()), flights.get(1).getId());
 912
 913    // Same but using executeWithMap
 914    Query q2 = pm.newQuery("select from " + Flight.class.getName() + " where :p1.contains(name)");
 915    Map params = new HashMap();
 916    params.put("p1", Arrays.asList("name1", "name3"));
 917    List<Flight> flights2 = (List<Flight>) q2.executeWithMap(params);
 918    assertEquals(2, flights2.size());
 919    assertEquals(KeyFactory.keyToString(e.getKey()), flights2.get(0).getId());
 920    assertEquals(KeyFactory.keyToString(e3.getKey()), flights2.get(1).getId());
 921
 922    q = pm.newQuery("select from " + Flight.class.getName() + " where :p1.contains(dest)");
 923    flights = (List<Flight>) q.execute(Arrays.asList(null, "mia1"));
 924    assertEquals(2, flights.size());
 925    assertEquals(KeyFactory.keyToString(e2.getKey()), flights.get(0).getId());
 926    assertEquals(KeyFactory.keyToString(e.getKey()), flights.get(1).getId());
 927
 928    q = pm.newQuery("select from " + Flight.class.getName() + " where :p1.contains(dest) || :p2.contains(dest)");
 929    flights = (List<Flight>) q.execute(Arrays.asList(null, "mia1"), Arrays.asList("mia2"));
 930    assertEquals(3, flights.size());
 931    assertEquals(KeyFactory.keyToString(e2.getKey()), flights.get(0).getId());
 932    assertEquals(KeyFactory.keyToString(e.getKey()), flights.get(1).getId());
 933    assertEquals(KeyFactory.keyToString(e3.getKey()), flights.get(2).getId());
 934  }
 935
 936  public void testContainsOnlyForCollection() {
 937    Entity e = Flight.newFlightEntity("name1", "bos1", "mia1", 23, 24);
 938    Entity e2 = Flight.newFlightEntity("name2", "bos2", null, 25, 26);
 939    Entity e3 = Flight.newFlightEntity("name3", "bos3", "mia2", 27, 28);
 940    ds.put(null, Arrays.asList(e, e2, e3));
 941
 942    try {
 943      Query q = pm.newQuery("select from " + Flight.class.getName() + " where name.contains(:param)");
 944      q.addExtension(DatastoreManager.QUERYEXT_INMEMORY_WHEN_UNSUPPORTED, "false");
 945      q.execute("na");
 946      fail("Should have thrown an exception when invoking 'contains' on a String");
 947    } catch (JDOUserException ue) {
 948      // Expected
 949    }
 950  }
 951
 952  public void testMultipleIn_Params() {
 953    Entity e = Flight.newFlightEntity("name1", "mia1", "bos1", 23, 24);
 954    Entity e2 = Flight.newFlightEntity("name2", "mia2", "bos2", 25, 26);
 955    Entity e3 = Flight.newFlightEntity("name3", "mia3", "bos3", 27, 28);
 956    ds.put(null, Arrays.asList(e, e2, e3));
 957    Query q = pm.newQuery("select from " + Flight.class.getName() + " where :p1.contains(name) && :p2.contains(origin)");
 958    List<Flight> flights =
 959        (List<Flight>) q.execute(Utils.newArrayList("name1", "name3"), Utils.newArrayList("mia3", "mia2"));
 960    assertEquals(1, flights.size());
 961    assertEquals(KeyFactory.keyToString(e3.getKey()), flights.get(0).getId());
 962
 963    q = pm.newQuery("select from " + Flight.class.getName() + " where :p1.contains(name) || :p2.contains(name)");
 964    flights =
 965        (List<Flight>) q.execute(Utils.newArrayList("name1", "name3"), Utils.newArrayList("name4", "name5"));
 966
 967    assertEquals(2, flights.size());
 968    assertEquals(KeyFactory.keyToString(e.getKey()), flights.get(0).getId());
 969    assertEquals(KeyFactory.keyToString(e3.getKey()), flights.get(1).getId());
 970  }
 971
 972  public void testMultipleIn_Params_KeyFilter() {
 973    Entity e = Flight.newFlightEntity("name1", "mia1", "bos1", 23, 24);
 974    Entity e2 = Flight.newFlightEntity("name2", "mia2", "bos2", 25, 26);
 975    Entity e3 = Flight.newFlightEntity("name3", "mia3", "bos3", 27, 28);
 976    ds.put(null, Arrays.asList(e, e2, e3));
 977    Query q = pm.newQuery(
 978        "select from " + Flight.class.getName() + " where :p1.contains(id) && :p2.contains(origin)");
 979    @SuppressWarnings("unchecked")
 980    List<Flight> flights = (List<Flight>) q.execute(
 981        Utils.newArrayList(KeyFactory.keyToString(e2.getKey())), Utils.newArrayList("mia3", "mia2"));
 982    assertEquals(1, flights.size());
 983    assertEquals(KeyFactory.keyToString(e2.getKey()), flights.get(0).getId());
 984
 985    q = pm.newQuery(
 986      "select from " + Flight.class.getName() + " where (id == :p1 || id ==:p2) && :p3.contains(origin)");
 987    @SuppressWarnings("unchecked")
 988    List<Flight> flights2 = (List<Flight>) q.execute(
 989        e2.getKey(), e3.getKey(), Utils.newArrayList("mia3", "dne"));
 990    assertEquals(1, flights2.size());
 991  }
 992
 993  public void testOr_Literals() {
 994    Entity e = Flight.newFlightEntity("name1", "bos1", "mia1", 23, 24);
 995    Entity e2 = Flight.newFlightEntity("name2", "bos2", null, 25, 26);
 996    Entity e3 = Flight.newFlightEntity("name3", "bos3", "mia2", 27, 28);
 997    ds.put(null, Arrays.asList(e, e2, e3));
 998    Query q = pm.newQuery("select from " + Flight.class.getName() +
 999                             " where name == 'name1' || name == 'name3'");
1000    List<Flight> flights = (List<Flight>) q.execute();
1001    assertEquals(2, flights.size());
1002    assertEquals(KeyFactory.keyToString(e.getKey()), flights.get(0).getId());
1003    assertEquals(KeyFactory.keyToString(e3.getKey()), flights.get(1).getId());
1004
1005    q = pm.newQuery("select from " + Flight.class.getName() +
1006                       " where dest == null || dest == 'mia1'");
1007    flights = (List<Flight>) q.execute();
1008    assertEquals(2, flights.size());
1009    assertEquals(KeyFactory.keyToString(e2.getKey()), flights.get(0).getId());
1010    assertEquals(KeyFactory.keyToString(e.getKey()), flights.get(1).getId());
1011  }
1012
1013  public void testOr_Params() {
1014    Entity e = Flight.newFlightEntity("name1", "bos1", "mia1", 23, 24);
1015    Entity e2 = Flight.newFlightEntity("name2", "bos2", "mia2", 25, 26);
1016    Entity e3 = Flight.newFlightEntity("name3", "bos3", "mia3", 27, 28);
1017    ds.put(null, Arrays.asList(e, e2, e3));
1018    Query q = pm.newQuery("select from " + Flight.class.getName() +
1019                             " where name == :p1 || name == :p2");
1020    List<Flight> flights = (List<Flight>) q.execute("name1", "name3");
1021    assertEquals(2, flights.size());
1022    assertEquals(KeyFactory.keyToString(e.getKey()), flights.get(0).getId());
1023    assertEquals(KeyFactory.keyToString(e3.getKey()), flights.get(1).getId());
1024  }
1025
1026  public void testMultipleOr_Literals() {
1027    Entity e = Flight.newFlightEntity("name1", "bos1", "mia1", 23, 24);
1028    Entity e2 = Flight.newFlightEntity("name2", "bos2", "mia2", 25, 26);
1029    Entity e3 = Flight.newFlightEntity("name3", "bos3", "mia3", 27, 28);
1030    ds.put(null, Arrays.asList(e, e2, e3));
1031    Query q = pm.newQuery("select from " + Flight.class.getName() + " where "
1032                             + "(name  == 'name1' || name == 'name3') && "
1033                             + "(origin == 'bos3' || origin == 'bos2')");
1034    List<Flight> flights = (List<Flight>) q.execute();
1035    assertEquals(1, flights.size());
1036    assertEquals(KeyFactory.keyToString(e3.getKey()), flights.get(0).getId());
1037  }
1038
1039  public void testMultipleOr_Params() {
1040    Entity e = Flight.newFlightEntity("name1", "bos1", "mia1", 23, 24);
1041    Entity e2 = Flight.newFlightEntity("name2", "bos2", "mia2", 25, 26);
1042    Entity e3 = Flight.newFlightEntity("name3", "bos3", "mia3", 27, 28);
1043    ds.put(null, Arrays.asList(e, e2, e3));
1044    Query q = pm.newQuery("select from " + Flight.class.getName() + " where "
1045                             + "(name == :p1 || name == :p2) && "
1046                             + "(origin == :p3 || origin == :p4)");
1047    Map<String, String> paramMap = Utils.newHashMap();
1048    paramMap.put("p1", "name1");
1049    paramMap.put("p2", "name3");
1050    paramMap.put("p3", "bos3");
1051    paramMap.put("p4", "bos2");
1052    List<Flight> flights = (List<Flight>) q.executeWithMap(paramMap);
1053    assertEquals(1, flights.size());
1054    assertEquals(KeyFactory.keyToString(e3.getKey()), flights.get(0).getId());
1055  }
1056
1057  public void testExecuteWithArray() {
1058    Entity e = Flight.newFlightEntity("name1", "bos1", "mia1", 23, 24);
1059    Entity e2 = Flight.newFlightEntity("name2", "bos2", "mia2", 25, 26);
1060    Entity e3 = Flight.newFlightEntity("name3", "bos3", "mia3", 27, 28);
1061    ds.put(null, Arrays.asList(e, e2, e3));
1062    Query q = pm.newQuery("select from " + Flight.class.getName() + " where "
1063                             + "(name == :p1 || name == :p2) && "
1064                             + "(origin == :p3 || origin == :p4)");
1065    Map<String, String> paramMap = Utils.newHashMap();
1066    paramMap.put("p1", "name1");
1067    paramMap.put("p2", "name3");
1068    paramMap.put("p3", "bos3");
1069    paramMap.put("p4", "bos2");
1070    List<Flight> flights = (List<Flight>) q.executeWithArray("name1", "name3", "bos3", "bos2");
1071    assertEquals(1, flights.size());
1072 

Large files files are truncated, but you can click here to view the full file