PageRenderTime 320ms CodeModel.GetById 21ms app.highlight 273ms RepoModel.GetById 2ms app.codeStats 1ms

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

http://datanucleus-appengine.googlecode.com/
Java | 3434 lines | 2943 code | 393 blank | 98 comment | 47 complexity | 3aab801a7e71a928c5a79dcec9a57053 MD5 | raw file
   1/**********************************************************************
   2 Copyright (c) 2009 Google Inc.
   3
   4 Licensed under the Apache License, Version 2.0 (the "License");
   5 you may not use this file except in compliance with the License.
   6 You may obtain a copy of the License at
   7
   8 http://www.apache.org/licenses/LICENSE-2.0
   9
  10 Unless required by applicable law or agreed to in writing, software
  11 distributed under the License is distributed on an "AS IS" BASIS,
  12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 See the License for the specific language governing permissions and
  14 limitations under the License.
  15 **********************************************************************/
  16package com.google.appengine.datanucleus.query;
  17
  18import com.google.appengine.api.datastore.DatastoreFailureException;
  19import com.google.appengine.api.datastore.DatastoreTimeoutException;
  20import com.google.appengine.api.datastore.Entity;
  21import com.google.appengine.api.datastore.Key;
  22import com.google.appengine.api.datastore.KeyFactory;
  23import com.google.appengine.api.datastore.Query.FilterOperator;
  24import com.google.appengine.api.datastore.Query.FilterPredicate;
  25import com.google.appengine.api.datastore.Query.SortDirection;
  26import com.google.appengine.api.datastore.Query.SortPredicate;
  27import com.google.appengine.api.datastore.ShortBlob;
  28import com.google.appengine.api.datastore.dev.LocalDatastoreService;
  29import com.google.appengine.datanucleus.DatastoreManager;
  30import com.google.appengine.datanucleus.DatastoreServiceFactoryInternal;
  31import com.google.appengine.datanucleus.DatastoreServiceInterceptor;
  32import com.google.appengine.datanucleus.ExceptionThrowingDatastoreDelegate;
  33import com.google.appengine.datanucleus.PrimitiveArrays;
  34import com.google.appengine.datanucleus.TestUtils;
  35import com.google.appengine.datanucleus.Utils;
  36import com.google.appengine.datanucleus.WriteBlocker;
  37import com.google.appengine.datanucleus.jpa.JPATestCase;
  38import com.google.appengine.datanucleus.test.jdo.Flight;
  39import com.google.appengine.datanucleus.test.jdo.KitchenSink;
  40import com.google.appengine.datanucleus.test.jdo.Person;
  41import com.google.appengine.datanucleus.test.jpa.BidirectionalChildListJPA;
  42import com.google.appengine.datanucleus.test.jpa.BidirectionalChildLongPkListJPA;
  43import com.google.appengine.datanucleus.test.jpa.BidirectionalGrandchildListJPA;
  44import com.google.appengine.datanucleus.test.jpa.Book;
  45import com.google.appengine.datanucleus.test.jpa.DetachableJPA;
  46import com.google.appengine.datanucleus.test.jpa.HasBytesJPA;
  47import com.google.appengine.datanucleus.test.jpa.HasDoubleJPA;
  48import com.google.appengine.datanucleus.test.jpa.HasEncodedStringPkJPA;
  49import com.google.appengine.datanucleus.test.jpa.HasEncodedStringPkSeparateIdFieldJPA;
  50import com.google.appengine.datanucleus.test.jpa.HasEncodedStringPkSeparateNameFieldJPA;
  51import com.google.appengine.datanucleus.test.jpa.HasEnumJPA;
  52import com.google.appengine.datanucleus.test.jpa.HasFloatJPA;
  53import com.google.appengine.datanucleus.test.jpa.HasKeyAncestorStringPkJPA;
  54import com.google.appengine.datanucleus.test.jpa.HasKeyPkJPA;
  55import com.google.appengine.datanucleus.test.jpa.HasLongPkJPA;
  56import com.google.appengine.datanucleus.test.jpa.HasMultiValuePropsJPA;
  57import com.google.appengine.datanucleus.test.jpa.HasOneToManyKeyPkListJPA;
  58import com.google.appengine.datanucleus.test.jpa.HasOneToManyKeyPkSetJPA;
  59import com.google.appengine.datanucleus.test.jpa.HasOneToManyListJPA;
  60import com.google.appengine.datanucleus.test.jpa.HasOneToManyLongPkListJPA;
  61import com.google.appengine.datanucleus.test.jpa.HasOneToManyLongPkSetJPA;
  62import com.google.appengine.datanucleus.test.jpa.HasOneToManyUnencodedStringPkListJPA;
  63import com.google.appengine.datanucleus.test.jpa.HasOneToManyUnencodedStringPkSetJPA;
  64import com.google.appengine.datanucleus.test.jpa.HasOneToOneJPA;
  65import com.google.appengine.datanucleus.test.jpa.HasOneToOneParentJPA;
  66import com.google.appengine.datanucleus.test.jpa.HasStringAncestorStringPkJPA;
  67import com.google.appengine.datanucleus.test.jpa.HasUnencodedStringPkJPA;
  68import com.google.appengine.datanucleus.test.jpa.InTheHouseJPA;
  69import com.google.appengine.datanucleus.test.jpa.NullDataJPA;
  70import com.google.appengine.datanucleus.test.jpa.AbstractBaseClassesJPA.Base1;
  71import com.google.appengine.datanucleus.test.jpa.UnidirectionalSingeTableChildJPA.UnidirTop;
  72import com.google.apphosting.api.ApiProxy;
  73import com.google.apphosting.api.DatastorePb;
  74
  75import org.datanucleus.api.jpa.JPAQuery;
  76import org.datanucleus.exceptions.NucleusFatalUserException;
  77import org.datanucleus.exceptions.NucleusUserException;
  78import org.datanucleus.query.expression.Expression;
  79import org.easymock.EasyMock;
  80
  81import java.io.ByteArrayOutputStream;
  82import java.io.IOException;
  83import java.io.ObjectOutputStream;
  84import java.math.BigDecimal;
  85import java.util.Arrays;
  86import java.util.Collections;
  87import java.util.Date;
  88import java.util.HashMap;
  89import java.util.HashSet;
  90import java.util.Iterator;
  91import java.util.List;
  92import java.util.Map;
  93import java.util.Set;
  94import java.util.concurrent.ExecutionException;
  95import java.util.concurrent.Future;
  96
  97import javax.persistence.NoResultException;
  98import javax.persistence.NonUniqueResultException;
  99import javax.persistence.PersistenceException;
 100import javax.persistence.Query;
 101import javax.persistence.QueryTimeoutException;
 102import javax.persistence.TypedQuery;
 103
 104import static com.google.appengine.datanucleus.test.jdo.Flight.newFlightEntity;
 105
 106/**
 107 * @author Max Ross <maxr@google.com>
 108 */
 109public class JPQLQueryTest extends JPATestCase {
 110
 111  private static final List<SortPredicate> NO_SORTS = Collections.emptyList();
 112  private static final List<FilterPredicate> NO_FILTERS = Collections.emptyList();
 113
 114  private static final FilterPredicate TITLE_EQ_2 =
 115      new FilterPredicate("title", FilterOperator.EQUAL, 2L);
 116  private static final FilterPredicate TITLE_EQ_2STR =
 117      new FilterPredicate("title", FilterOperator.EQUAL, "2");
 118  private static final FilterPredicate ISBN_EQ_4 =
 119      new FilterPredicate("isbn", FilterOperator.EQUAL, 4L);
 120  private static final FilterPredicate TITLE_GT_2 =
 121      new FilterPredicate("title", FilterOperator.GREATER_THAN, 2L);
 122  private static final FilterPredicate TITLE_GTE_2 =
 123      new FilterPredicate("title", FilterOperator.GREATER_THAN_OR_EQUAL, 2L);
 124  private static final FilterPredicate ISBN_LT_4 =
 125      new FilterPredicate("isbn", FilterOperator.LESS_THAN, 4L);
 126  private static final FilterPredicate ISBN_LTE_4 =
 127      new FilterPredicate("isbn", FilterOperator.LESS_THAN_OR_EQUAL, 4L);
 128  private static final FilterPredicate TITLE_NEQ_NULL_LITERAL =
 129      new FilterPredicate("title", FilterOperator.NOT_EQUAL, null);
 130  private static final FilterPredicate TITLE_NEQ_2_LITERAL =
 131      new FilterPredicate("title", FilterOperator.NOT_EQUAL, 2L);
 132  private static final SortPredicate TITLE_ASC =
 133      new SortPredicate("title", SortDirection.ASCENDING);
 134  private static final SortPredicate ISBN_DESC =
 135      new SortPredicate("isbn", SortDirection.DESCENDING);
 136  private static final FilterPredicate TITLE_IN_2_ARGS =
 137      new FilterPredicate("title", FilterOperator.IN, Arrays.asList("2", 2L));
 138  private static final FilterPredicate TITLE_IN_3_ARGS =
 139      new FilterPredicate("title", FilterOperator.IN, Arrays.asList("2", 2L, false));
 140
 141  @Override
 142  protected void setUp() throws Exception {
 143    super.setUp();
 144    DatastoreServiceInterceptor.install(getStoreManager(), new WriteBlocker());
 145  }
 146
 147  @Override
 148  protected void tearDown() throws Exception {
 149    try {
 150      super.tearDown();
 151    } finally {
 152      DatastoreServiceInterceptor.uninstall();
 153    }
 154  }
 155
 156  @Override
 157  protected EntityManagerFactoryName getEntityManagerFactoryName() {
 158    return EntityManagerFactoryName.nontransactional_ds_non_transactional_ops_allowed;
 159  }
 160
 161  public void testUnsupportedFilters_NoResultExpr() {
 162    String baseQuery = "SELECT FROM " + Book.class.getName() + " b ";
 163    testUnsupportedFilters(baseQuery);
 164  }
 165
 166  public void testUnsupportedFilters_PrimaryResultExpr() {
 167    String baseQuery = "SELECT b FROM " + Book.class.getName() + " b ";
 168    testUnsupportedFilters(baseQuery);
 169  }
 170
 171  private void testUnsupportedFilters(String baseQuery) {
 172    Set<Expression.Operator> unsupportedOps =
 173        new HashSet<Expression.Operator>(DatastoreQuery.UNSUPPORTED_OPERATORS);
 174    baseQuery += "WHERE ";
 175    assertQueryUnsupportedByOrm(baseQuery + "NOT title = 'foo'", Expression.OP_NOT, unsupportedOps);
 176    assertQueryUnsupportedByOrm(baseQuery + "(title + author) = 'foo'", Expression.OP_ADD,
 177                                unsupportedOps);
 178    assertQueryUnsupportedByOrm(baseQuery + "title + author = 'foo'", Expression.OP_ADD,
 179                                unsupportedOps);
 180    assertQueryUnsupportedByOrm(baseQuery + "(title - author) = 'foo'", Expression.OP_SUB,
 181                                unsupportedOps);
 182    assertQueryUnsupportedByOrm(baseQuery + "title - author = 'foo'", Expression.OP_SUB,
 183                                unsupportedOps);
 184    assertQueryUnsupportedByOrm(baseQuery + "(title / author) = 'foo'", Expression.OP_DIV,
 185                                unsupportedOps);
 186    assertQueryUnsupportedByOrm(baseQuery + "title / author = 'foo'", Expression.OP_DIV,
 187                                unsupportedOps);
 188    assertQueryUnsupportedByOrm(baseQuery + "(title * author) = 'foo'", Expression.OP_MUL,
 189                                unsupportedOps);
 190    assertQueryUnsupportedByOrm(baseQuery + "title * author = 'foo'", Expression.OP_MUL,
 191                                unsupportedOps);
 192    assertQueryUnsupportedByOrm(baseQuery + "(title % author) = 'foo'", Expression.OP_MOD,
 193                                unsupportedOps);
 194    assertQueryUnsupportedByOrm(baseQuery + "title % author = 'foo'", Expression.OP_MOD,
 195                                unsupportedOps);
 196    assertQueryRequiresUnsupportedDatastoreFeature(baseQuery + "title LIKE '%foo'");
 197    // can't have 'or' on multiple properties
 198    assertQueryRequiresUnsupportedDatastoreFeature(baseQuery + "title = 'yar' or author = null");
 199    assertQueryRequiresUnsupportedDatastoreFeature(baseQuery + "isbn = 4 and (title = 'yar' or author = 'yam')");
 200    assertQueryRequiresUnsupportedDatastoreFeature(baseQuery + "title IN('yar') or author = 'yam'");
 201    // can only check equality
 202    assertQueryRequiresUnsupportedDatastoreFeature(baseQuery + "title > 5 or title < 2");
 203
 204    // multiple inequality filters
 205    // TODO(maxr) Make this pass against the real datastore.
 206    // We need to have it return BadRequest instead of NeedIndex for that to happen.
 207    assertQueryUnsupportedByDatastore(baseQuery + "(title > 2 AND isbn < 4)", IllegalArgumentException.class);
 208    // inequality filter prop is not the same as the first order by prop
 209    assertQueryUnsupportedByDatastore(baseQuery + "(title > 2) order by isbn", IllegalArgumentException.class);
 210    // gets split into multiple inequality props
 211    assertQueryUnsupportedByDatastore(baseQuery + "title <> 2 AND isbn <> 4", IllegalArgumentException.class);
 212    assertEquals(
 213        new HashSet<Expression.Operator>(Arrays.asList(Expression.OP_CONCAT, Expression.OP_COM,
 214                                                       Expression.OP_NEG, Expression.OP_IS,
 215                                                       Expression.OP_LIKE,
 216                                                       Expression.OP_ISNOT)), unsupportedOps);
 217  }
 218
 219  public void testEvaluateInMemory() {
 220    ds.put(null, newFlightEntity("1", "yar", "bam", 1, 2));
 221    ds.put(null, newFlightEntity("1", "yam", null, 1, 2));
 222
 223    // This is impossible in the datastore, so run totally in-memory
 224    String query = "SELECT f FROM " + Flight.class.getName() + " f WHERE origin = 'yar' OR dest IS null";
 225    Query q = em.createQuery(query);
 226    q.setHint("datanucleus.query.evaluateInMemory", "true");
 227    try {
 228       List<Flight> results = (List<Flight>) q.getResultList();
 229       assertEquals("Number of results was wrong", 2, results.size());
 230    } catch (RuntimeException e) {
 231      fail("Threw exception when evaluating query in-memory, but should have run");
 232    }
 233  }
 234
 235  public void testSupportedFilters_NoResultExpr() {
 236    String baseQuery = "SELECT FROM " + Book.class.getName() + " b ";
 237    testSupportedFilters(baseQuery);
 238  }
 239
 240  public void testSupportedFilters_PrimaryResultExpr() {
 241    String baseQuery = "SELECT b FROM " + Book.class.getName() + " b ";
 242    testSupportedFilters(baseQuery);
 243  }
 244
 245  private void testSupportedFilters(String baseQuery) {
 246
 247    assertQuerySupported(baseQuery, NO_FILTERS, NO_SORTS);
 248
 249    baseQuery += "WHERE ";
 250    assertQuerySupported(baseQuery + "title = 2", Utils.newArrayList(TITLE_EQ_2), NO_SORTS);
 251    assertQuerySupported(baseQuery + "title = \"2\"", Utils.newArrayList(TITLE_EQ_2STR), NO_SORTS);
 252    assertQuerySupported(baseQuery + "(title = 2)", Utils.newArrayList(TITLE_EQ_2), NO_SORTS);
 253    assertQuerySupported(baseQuery + "title = 2 AND isbn = 4",
 254                         Utils.newArrayList(TITLE_EQ_2,ISBN_EQ_4),
 255                         NO_SORTS);
 256    assertQuerySupported(baseQuery + "(title = 2 AND isbn = 4)",
 257                         Utils.newArrayList(TITLE_EQ_2, ISBN_EQ_4),
 258                         NO_SORTS);
 259    assertQuerySupported(baseQuery + "(title = 2) AND (isbn = 4)", Utils.newArrayList(
 260        TITLE_EQ_2, ISBN_EQ_4), NO_SORTS);
 261    assertQuerySupported(baseQuery + "title > 2", Utils.newArrayList(TITLE_GT_2), NO_SORTS);
 262    assertQuerySupported(baseQuery + "title >= 2", Utils.newArrayList(TITLE_GTE_2), NO_SORTS);
 263    assertQuerySupported(baseQuery + "isbn < 4", Utils.newArrayList(ISBN_LT_4), NO_SORTS);
 264    assertQuerySupported(baseQuery + "isbn <= 4", Utils.newArrayList(ISBN_LTE_4), NO_SORTS);
 265
 266    baseQuery = "SELECT FROM " + Book.class.getName() + " b ";
 267    assertQuerySupported(baseQuery + "ORDER BY title ASC", NO_FILTERS,
 268                         Utils.newArrayList(TITLE_ASC));
 269    assertQuerySupported(baseQuery + "ORDER BY isbn DESC", NO_FILTERS,
 270                         Utils.newArrayList(ISBN_DESC));
 271    assertQuerySupported(baseQuery + "ORDER BY title ASC, isbn DESC", NO_FILTERS,
 272                         Utils.newArrayList(TITLE_ASC, ISBN_DESC));
 273
 274    assertQuerySupported(baseQuery + "WHERE title = 2 AND isbn = 4 ORDER BY title ASC, isbn DESC",
 275                         Utils.newArrayList(TITLE_EQ_2, ISBN_EQ_4),
 276                         Utils.newArrayList(TITLE_ASC, ISBN_DESC));
 277    assertQuerySupported(baseQuery + "WHERE title <> null",
 278                         Utils.newArrayList(TITLE_NEQ_NULL_LITERAL), NO_SORTS);
 279    assertQuerySupported(baseQuery + "WHERE title <> 2",
 280                         Utils.newArrayList(TITLE_NEQ_2_LITERAL), NO_SORTS);
 281    assertQuerySupported(baseQuery + "WHERE title = '2' OR title = 2",
 282                         Utils.newArrayList(TITLE_IN_2_ARGS), NO_SORTS);
 283    assertQuerySupported(baseQuery + "WHERE title = '2' OR title = 2 OR title = false",
 284                         Utils.newArrayList(TITLE_IN_3_ARGS), NO_SORTS);
 285    assertQuerySupported(baseQuery + "WHERE title IN ('2', 2)",
 286                         Utils.newArrayList(TITLE_IN_2_ARGS), NO_SORTS);
 287    assertQuerySupported(baseQuery + "WHERE title IN ('2', 2, false)",
 288                         Utils.newArrayList(TITLE_IN_3_ARGS), NO_SORTS);
 289    assertQuerySupported(baseQuery + "WHERE (title = '2' OR title = 2) AND isbn = 4",
 290                         Utils.newArrayList(ISBN_EQ_4, TITLE_IN_2_ARGS), NO_SORTS);
 291    assertQuerySupported(baseQuery + "WHERE title IN ('2', 2) AND isbn = 4",
 292                         Utils.newArrayList(ISBN_EQ_4, TITLE_IN_2_ARGS), NO_SORTS);
 293    assertQuerySupported(baseQuery + "WHERE (title = '2' OR title = 2 OR title = false) AND isbn = 4",
 294                         Utils.newArrayList(ISBN_EQ_4, TITLE_IN_3_ARGS), NO_SORTS);
 295    assertQuerySupported(baseQuery + "WHERE title IN ('2', 2, false) AND isbn = 4",
 296                         Utils.newArrayList(ISBN_EQ_4, TITLE_IN_3_ARGS), NO_SORTS);
 297  }
 298
 299  public void test2Equals2OrderBy() {
 300    ds.put(Book.newBookEntity("Joe Blow", "67890", "Bar Book"));
 301    ds.put(Book.newBookEntity("Joe Blow", "11111", "Bar Book"));
 302    ds.put(Book.newBookEntity("Joe Blow", "12345", "Foo Book"));
 303    ds.put(Book.newBookEntity("Joe Blow", "54321", "A Book"));
 304    ds.put(Book.newBookEntity("Jane Blow", "13579", "Baz Book"));
 305
 306    Query q = em.createQuery("SELECT FROM " +
 307                             Book.class.getName() + " b" +
 308                             " WHERE author = 'Joe Blow'" +
 309                             " ORDER BY title DESC, isbn ASC");
 310
 311    @SuppressWarnings("unchecked")
 312    List<Book> result = (List<Book>) q.getResultList();
 313
 314    assertEquals(4, result.size());
 315    assertEquals("12345", result.get(0).getIsbn());
 316    assertEquals("11111", result.get(1).getIsbn());
 317    assertEquals("67890", result.get(2).getIsbn());
 318    assertEquals("54321", result.get(3).getIsbn());
 319  }
 320
 321  public void testDefaultOrderingIsAsc() {
 322    ds.put(Book.newBookEntity("Joe Blow", "67890", "Bar Book"));
 323    ds.put(Book.newBookEntity("Joe Blow", "11111", "Bar Book"));
 324    ds.put(Book.newBookEntity("Joe Blow", "12345", "Foo Book"));
 325    ds.put(Book.newBookEntity("Joe Blow", "54321", "A Book"));
 326    ds.put(Book.newBookEntity("Jane Blow", "13579", "Baz Book"));
 327
 328    Query q = em.createQuery("SELECT FROM " +
 329                             Book.class.getName() + " b" +
 330                             " WHERE author = 'Joe Blow'" +
 331                             " ORDER BY title");
 332
 333    @SuppressWarnings("unchecked")
 334    List<Book> result = (List<Book>) q.getResultList();
 335
 336    assertEquals(4, result.size());
 337    assertEquals("54321", result.get(0).getIsbn());
 338    assertEquals("67890", result.get(1).getIsbn());
 339    assertEquals("11111", result.get(2).getIsbn());
 340    assertEquals("12345", result.get(3).getIsbn());
 341  }
 342
 343  public void testLimitQuery() {
 344    ds.put(Book.newBookEntity("Joe Blow", "67890", "Bar Book"));
 345    ds.put(Book.newBookEntity("Joe Blow", "11111", "Bar Book"));
 346    ds.put(Book.newBookEntity("Joe Blow", "12345", "Foo Book"));
 347    ds.put(Book.newBookEntity("Joe Blow", "54321", "A Book"));
 348    ds.put(Book.newBookEntity("Jane Blow", "13579", "Baz Book"));
 349
 350    Query q = em.createQuery("SELECT FROM " +
 351                             Book.class.getName() + " b" + 
 352                             " WHERE author = 'Joe Blow'" +
 353                             " ORDER BY title DESC, isbn ASC");
 354
 355    q.setMaxResults(1);
 356    @SuppressWarnings("unchecked")
 357    List<Book> result1 = (List<Book>) q.getResultList();
 358    assertEquals(1, result1.size());
 359    assertEquals("12345", result1.get(0).getIsbn());
 360
 361    q.setMaxResults(0);
 362    @SuppressWarnings("unchecked")
 363    List<Book> result2 = (List<Book>) q.getResultList();
 364    assertEquals(0, result2.size());
 365
 366    try {
 367      q.setMaxResults(-1);
 368      fail("expected iae");
 369    } catch (IllegalArgumentException iae) {
 370      // good
 371    }
 372  }
 373
 374  public void testOffsetQuery() {
 375    ds.put(Book.newBookEntity("Joe Blow", "67890", "Bar Book"));
 376    ds.put(Book.newBookEntity("Joe Blow", "11111", "Bar Book"));
 377    ds.put(Book.newBookEntity("Joe Blow", "12345", "Foo Book"));
 378    ds.put(Book.newBookEntity("Joe Blow", "54321", "A Book"));
 379    ds.put(Book.newBookEntity("Jane Blow", "13579", "Baz Book"));
 380    Query q = em.createQuery("SELECT FROM " +
 381                             Book.class.getName() + " b" + 
 382                             " WHERE author = 'Joe Blow'" +
 383                             " ORDER BY title DESC, isbn ASC");
 384
 385    q.setFirstResult(0);
 386    @SuppressWarnings("unchecked")
 387    List<Book> result1 = (List<Book>) q.getResultList();
 388    assertEquals(4, result1.size());
 389    assertEquals("12345", result1.get(0).getIsbn());
 390
 391    q.setFirstResult(1);
 392    @SuppressWarnings("unchecked")
 393    List<Book> result2 = (List<Book>) q.getResultList();
 394    assertEquals(3, result2.size());
 395    assertEquals("11111", result2.get(0).getIsbn());
 396
 397    try {
 398      q.setFirstResult(-1);
 399      fail("expected iae");
 400    } catch (IllegalArgumentException iae) {
 401      // good
 402    }
 403  }
 404
 405  public void testOffsetLimitQuery() {
 406    ds.put(Book.newBookEntity("Joe Blow", "67890", "Bar Book"));
 407    ds.put(Book.newBookEntity("Joe Blow", "11111", "Bar Book"));
 408    ds.put(Book.newBookEntity("Joe Blow", "12345", "Foo Book"));
 409    ds.put(Book.newBookEntity("Joe Blow", "54321", "A Book"));
 410    ds.put(Book.newBookEntity("Jane Blow", "13579", "Baz Book"));
 411    Query q = em.createQuery("SELECT FROM " +
 412                             Book.class.getName() + " b" + 
 413                             " WHERE author = 'Joe Blow'" +
 414                             " ORDER BY title DESC, isbn ASC");
 415
 416    q.setFirstResult(0);
 417    q.setMaxResults(0);
 418    @SuppressWarnings("unchecked")
 419    List<Book> result1 = (List<Book>) q.getResultList();
 420    assertEquals(0, result1.size());
 421
 422    q.setFirstResult(1);
 423    q.setMaxResults(0);
 424    @SuppressWarnings("unchecked")
 425    List<Book> result2 = (List<Book>) q.getResultList();
 426    assertEquals(0, result2.size());
 427
 428    q.setFirstResult(0);
 429    q.setMaxResults(1);
 430    @SuppressWarnings("unchecked")
 431    List<Book> result3 = (List<Book>) q.getResultList();
 432    assertEquals(1, result3.size());
 433
 434    q.setFirstResult(0);
 435    q.setMaxResults(2);
 436    @SuppressWarnings("unchecked")
 437    List<Book> result4 = (List<Book>) q.getResultList();
 438    assertEquals(2, result4.size());
 439    assertEquals("12345", result4.get(0).getIsbn());
 440
 441    q.setFirstResult(1);
 442    q.setMaxResults(1);
 443    @SuppressWarnings("unchecked")
 444    List<Book> result5 = (List<Book>) q.getResultList();
 445    assertEquals(1, result5.size());
 446    assertEquals("11111", result5.get(0).getIsbn());
 447
 448    q.setFirstResult(2);
 449    q.setMaxResults(5);
 450    @SuppressWarnings("unchecked")
 451    List<Book> result6 = (List<Book>) q.getResultList();
 452    assertEquals(2, result6.size());
 453    assertEquals("67890", result6.get(0).getIsbn());
 454  }
 455
 456  public void testSerialization() throws IOException {
 457    Query q = em.createQuery("select from " + Book.class.getName() + " b ");
 458    q.getResultList();
 459
 460    JPQLQuery innerQuery = (JPQLQuery) ((JPAQuery) q).getInternalQuery();
 461    ByteArrayOutputStream baos = new ByteArrayOutputStream();
 462    ObjectOutputStream oos = new ObjectOutputStream(baos);
 463    // the fact that this doesn't blow up is the test
 464    oos.writeObject(innerQuery);
 465  }
 466
 467  public void testBindVariables() {
 468
 469    assertQuerySupported("select from " + Book.class.getName() + " b where title = :titleParam",
 470                         Utils.newArrayList(TITLE_EQ_2), NO_SORTS, "titleParam", 2L);
 471
 472    assertQuerySupported("select from " + Book.class.getName() + " b" + 
 473                         " where title = :titleParam AND isbn = :isbnParam",
 474                         Utils.newArrayList(TITLE_EQ_2, ISBN_EQ_4), NO_SORTS, "titleParam", 2L, "isbnParam", 4L);
 475
 476    assertQuerySupported("select from " + Book.class.getName() + " b" + 
 477                         " where title = :titleParam AND isbn = :isbnParam order by title asc, isbn desc",
 478                         Utils.newArrayList(TITLE_EQ_2, ISBN_EQ_4),
 479                         Utils.newArrayList(TITLE_ASC, ISBN_DESC), "titleParam", 2L, "isbnParam", 4L);
 480  }
 481
 482  public void testKeyQuery() {
 483    Entity bookEntity = Book.newBookEntity("Joe Blow", "67890", "Bar Book");
 484    ds.put(bookEntity);
 485
 486    javax.persistence.Query q = em.createQuery(
 487        "select from " + Book.class.getName() + " b where id = :key");
 488    q.setParameter("key", KeyFactory.keyToString(bookEntity.getKey()));
 489    @SuppressWarnings("unchecked")
 490    List<Book> books = (List<Book>) q.getResultList();
 491    assertEquals(1, books.size());
 492    assertEquals(bookEntity.getKey(), KeyFactory.stringToKey(books.get(0).getId()));
 493
 494    // now issue the same query, but instead of providing a String version of
 495    // the key, provide the Key itself.
 496    q.setParameter("key", bookEntity.getKey());
 497    @SuppressWarnings("unchecked")
 498    List<Book> books2 = (List<Book>) q.getResultList();
 499    assertEquals(1, books2.size());
 500    assertEquals(bookEntity.getKey(), KeyFactory.stringToKey(books2.get(0).getId()));
 501  }
 502
 503  public void testKeyQuery_KeyPk() {
 504    Entity entityWithName = new Entity(HasKeyPkJPA.class.getSimpleName(), "blarg");
 505    Entity entityWithId = new Entity(HasKeyPkJPA.class.getSimpleName());
 506    ds.put(entityWithName);
 507    ds.put(entityWithId);
 508
 509    Query q = em.createQuery(
 510        "select from " + HasKeyPkJPA.class.getName() + " b where id = :key");
 511    q.setParameter("key", entityWithName.getKey());
 512    HasKeyPkJPA result = (HasKeyPkJPA) q.getSingleResult();
 513    assertEquals(entityWithName.getKey(), result.getId());
 514
 515    q = em.createQuery("select from " + HasKeyPkJPA.class.getName() + " b where id = :mykey");
 516    q.setParameter("mykey", entityWithId.getKey());
 517    result = (HasKeyPkJPA) q.getSingleResult();
 518    assertEquals(entityWithId.getKey(), result.getId());
 519
 520    q = em.createQuery("select from " + HasKeyPkJPA.class.getName() + " b where id = :mykeyname");
 521    q.setParameter("mykeyname", entityWithName.getKey().getName());
 522    result = (HasKeyPkJPA) q.getSingleResult();
 523    assertEquals(entityWithName.getKey(), result.getId());
 524
 525    q = em.createQuery("select from " + HasKeyPkJPA.class.getName() + " b where id = :mykeyid");
 526    q.setParameter("mykeyid", entityWithId.getKey().getId());
 527    result = (HasKeyPkJPA) q.getSingleResult();
 528    assertEquals(entityWithId.getKey(), result.getId());
 529  }
 530
 531  public void testKeyQueryWithSorts() {
 532    Entity bookEntity = Book.newBookEntity("Joe Blow", "67890", "Bar Book");
 533    ds.put(bookEntity);
 534
 535    javax.persistence.Query q = em.createQuery(
 536        "select from " + Book.class.getName() + " c where id = :key order by isbn ASC");
 537    q.setParameter("key", KeyFactory.keyToString(bookEntity.getKey()));
 538    @SuppressWarnings("unchecked")
 539    List<Book> books = (List<Book>) q.getResultList();
 540    assertEquals(1, books.size());
 541    assertEquals(bookEntity.getKey(), KeyFactory.stringToKey(books.get(0).getId()));
 542  }
 543
 544  public void testKeyQuery_MultipleFilters() {
 545    Entity bookEntity = Book.newBookEntity("Joe Blow", "67890", "Bar Book");
 546    ds.put(bookEntity);
 547
 548    javax.persistence.Query q = em.createQuery(
 549        "select from " + Book.class.getName() + " c where id = :key and isbn = \"67890\"");
 550    q.setParameter("key", KeyFactory.keyToString(bookEntity.getKey()));
 551    @SuppressWarnings("unchecked")
 552    List<Book> books = (List<Book>) q.getResultList();
 553    assertEquals(1, books.size());
 554    assertEquals(bookEntity.getKey(), KeyFactory.stringToKey(books.get(0).getId()));
 555  }
 556
 557  public void testKeyQuery_NonEqualityFilter() {
 558    Entity bookEntity1 = Book.newBookEntity("Joe Blow", "67890", "Bar Book");
 559    ds.put(bookEntity1);
 560
 561    Entity bookEntity2 = Book.newBookEntity("Joe Blow", "67890", "Bar Book");
 562    ds.put(bookEntity2);
 563
 564    javax.persistence.Query q = em.createQuery(
 565        "select from " + Book.class.getName()
 566        + " b where id > :key");
 567    q.setParameter("key", KeyFactory.keyToString(bookEntity1.getKey()));
 568    @SuppressWarnings("unchecked")
 569    List<Book> books = (List<Book>) q.getResultList();
 570    assertEquals(1, books.size());
 571    assertEquals(bookEntity2.getKey(), KeyFactory.stringToKey(books.get(0).getId()));
 572  }
 573
 574  public void testKeyQuery_SortByKey() {
 575    Entity bookEntity1 = Book.newBookEntity("Joe Blow", "67890", "Bar Book");
 576    ds.put(bookEntity1);
 577
 578    Entity bookEntity2 = Book.newBookEntity("Joe Blow", "67890", "Bar Book");
 579    ds.put(bookEntity2);
 580
 581    javax.persistence.Query q = em.createQuery(
 582        "select from " + Book.class.getName()
 583        + " b order by id DESC");
 584    @SuppressWarnings("unchecked")
 585    List<Book> books = (List<Book>) q.getResultList();
 586    assertEquals(2, books.size());
 587    assertEquals(bookEntity2.getKey(), KeyFactory.stringToKey(books.get(0).getId()));
 588  }
 589
 590  public void testKeyQuery_FilterAndSortByKeyComponent() {
 591    // filter by pk-id
 592    assertQueryUnsupportedByDatastore(
 593        "select from " + HasEncodedStringPkSeparateIdFieldJPA.class.getName() + " b where id = 4",
 594        NucleusFatalUserException.class);
 595    // sort by pk-id
 596    assertQueryUnsupportedByDatastore(
 597        "select from " + HasEncodedStringPkSeparateIdFieldJPA.class.getName() + " b order by id",
 598        NucleusFatalUserException.class);
 599    // filter by pk-id
 600    assertQueryUnsupportedByDatastore(
 601        "select from " + HasEncodedStringPkSeparateNameFieldJPA.class.getName() + " b where name = 4",
 602        NucleusFatalUserException.class);
 603    // sort by pk-id
 604    assertQueryUnsupportedByDatastore(
 605        "select from " + HasEncodedStringPkSeparateNameFieldJPA.class.getName() + " b order by name",
 606        NucleusFatalUserException.class);
 607  }
 608
 609  public void testAncestorQuery() {
 610    Entity bookEntity = Book.newBookEntity("Joe Blow", "67890", "Bar Book");
 611    ds.put(bookEntity);
 612    Entity
 613        hasAncestorEntity =
 614        new Entity(HasStringAncestorStringPkJPA.class.getSimpleName(), bookEntity.getKey());
 615    ds.put(hasAncestorEntity);
 616
 617    javax.persistence.Query q = em.createQuery(
 618        "select from " + HasStringAncestorStringPkJPA.class.getName()
 619        + " b where ancestorId = :ancId");
 620    q.setParameter("ancId", KeyFactory.keyToString(bookEntity.getKey()));
 621
 622    @SuppressWarnings("unchecked")
 623    List<HasStringAncestorStringPkJPA>
 624        haList =
 625        (List<HasStringAncestorStringPkJPA>) q.getResultList();
 626    assertEquals(1, haList.size());
 627    assertEquals(bookEntity.getKey(), KeyFactory.stringToKey(haList.get(0).getAncestorId()));
 628
 629    assertEquals(
 630        bookEntity.getKey(), getDatastoreQuery(q).getLatestDatastoreQuery().getAncestor());
 631    assertEquals(NO_FILTERS, getFilterPredicates(q));
 632    assertEquals(NO_SORTS, getSortPredicates(q));
 633  }
 634
 635  public void testIllegalAncestorQuery() {
 636    Entity bookEntity = Book.newBookEntity("Joe Blow", "67890", "Bar Book");
 637    ds.put(bookEntity);
 638    Entity
 639        hasAncestorEntity =
 640        new Entity(HasStringAncestorStringPkJPA.class.getName(), bookEntity.getKey());
 641    ds.put(hasAncestorEntity);
 642
 643    javax.persistence.Query q = em.createQuery(
 644        "select from " + HasStringAncestorStringPkJPA.class.getName() + " b"
 645        + " where ancestorId > :ancId");
 646    q.setParameter("ancId", KeyFactory.keyToString(bookEntity.getKey()));
 647    q.setHint(DatastoreManager.QUERYEXT_INMEMORY_WHEN_UNSUPPORTED, "false");
 648    try {
 649      q.getResultList();
 650      fail("expected udfe");
 651    } catch (PersistenceException pe) {
 652        if (pe.getCause() instanceof DatastoreQuery.UnsupportedDatastoreFeatureException) {
 653            // good
 654        }
 655        else {
 656          throw pe;
 657        }
 658    }
 659  }
 660
 661  public void testSortByFieldWithCustomColumn() {
 662    ds.put(Book.newBookEntity("Joe Blow", "67890", "Bar Book", 2003));
 663    ds.put(Book.newBookEntity("Joe Blow", "11111", "Bar Book", 2002));
 664    ds.put(Book.newBookEntity("Joe Blow", "12345", "Foo Book", 2001));
 665
 666    Query q = em.createQuery("SELECT FROM " +
 667                             Book.class.getName() + " b" +
 668                             " WHERE b.author = 'Joe Blow'" +
 669                             " ORDER BY firstPublished ASC");
 670
 671    @SuppressWarnings("unchecked")
 672    List<Book> result = (List<Book>) q.getResultList();
 673
 674    assertEquals(3, result.size());
 675    assertEquals("12345", result.get(0).getIsbn());
 676    assertEquals("11111", result.get(1).getIsbn());
 677    assertEquals("67890", result.get(2).getIsbn());
 678  }
 679
 680  private interface BookProvider {
 681
 682    Book getBook(Key key);
 683  }
 684
 685  private class AttachedBookProvider implements BookProvider {
 686
 687    public Book getBook(Key key) {
 688      return em.find(Book.class, key);
 689    }
 690  }
 691
 692  private class TransientBookProvider implements BookProvider {
 693
 694    public Book getBook(Key key) {
 695      Book b = new Book();
 696      b.setId(KeyFactory.keyToString(key));
 697      return b;
 698    }
 699  }
 700
 701  private void testFilterByChildObject(BookProvider bp) {
 702    Entity parentEntity = new Entity(HasOneToOneJPA.class.getSimpleName());
 703    ds.put(parentEntity);
 704    Entity bookEntity = Book.newBookEntity(parentEntity.getKey(), "Joe Blow", "11111", "Bar Book", 1929);
 705    ds.put(bookEntity);
 706
 707    Book book = bp.getBook(bookEntity.getKey());
 708    Query q = em.createQuery(
 709        "select from " + HasOneToOneJPA.class.getName() + " c where book = :b");
 710    q.setParameter("b", book);
 711    List<HasOneToOneJPA> result = (List<HasOneToOneJPA>) q.getResultList();
 712    assertEquals(1, result.size());
 713    assertEquals(parentEntity.getKey(), KeyFactory.stringToKey(result.get(0).getId()));
 714  }
 715
 716  public void testFilterByChildObject() {
 717    testFilterByChildObject(new AttachedBookProvider());
 718    testFilterByChildObject(new TransientBookProvider());
 719  }
 720
 721  private void testFilterByChildObject_AdditionalFilterOnParent(BookProvider bp) {
 722    Entity parentEntity = new Entity(HasOneToOneJPA.class.getSimpleName());
 723    ds.put(parentEntity);
 724    Entity bookEntity = Book.newBookEntity(parentEntity.getKey(), "Joe Blow", "11111", "Bar Book", 1929);
 725    ds.put(bookEntity);
 726
 727    Book book = bp.getBook(bookEntity.getKey());
 728    Query q = em.createQuery(
 729        "select from " + HasOneToOneJPA.class.getName() + " c where id = :parentId and book = :b");
 730    q.setParameter("parentId", KeyFactory.keyToString(bookEntity.getKey()));
 731    q.setParameter("b", book);
 732    List<HasOneToOneJPA> result = (List<HasOneToOneJPA>) q.getResultList();
 733    assertTrue(result.isEmpty());
 734
 735    q.setParameter("parentId", KeyFactory.keyToString(parentEntity.getKey()));
 736    q.setParameter("b", book);
 737    result = (List<HasOneToOneJPA>) q.getResultList();
 738    assertEquals(1, result.size());
 739    assertEquals(parentEntity.getKey(), KeyFactory.stringToKey(result.get(0).getId()));
 740  }
 741
 742  public void testFilterByChildObject_AdditionalFilterOnParent() {
 743    testFilterByChildObject_AdditionalFilterOnParent(new AttachedBookProvider());
 744    testFilterByChildObject_AdditionalFilterOnParent(new TransientBookProvider());
 745  }
 746
 747
 748  private void testFilterByChildObject_UnsupportedOperator(BookProvider bp) {
 749    Entity parentEntity = new Entity(HasOneToOneJPA.class.getSimpleName());
 750    ds.put(parentEntity);
 751    Entity bookEntity = Book.newBookEntity(parentEntity.getKey(), "Joe Blow", "11111", "Bar Book", 1929);
 752    ds.put(bookEntity);
 753
 754    Book book = bp.getBook(bookEntity.getKey());
 755    Query q = em.createQuery(
 756        "select from " + HasOneToOneJPA.class.getName() + " c where book > :b");
 757    q.setHint(DatastoreManager.QUERYEXT_INMEMORY_WHEN_UNSUPPORTED, "false");
 758    q.setParameter("b", book);
 759    try {
 760      q.getResultList();
 761      fail("expected udfe");
 762    } catch (PersistenceException pe) {
 763        if (pe.getCause() instanceof DatastoreQuery.UnsupportedDatastoreFeatureException) {
 764            // good
 765        }
 766        else {
 767          throw pe;
 768        }
 769    }
 770  }
 771
 772  public void testFilterByChildObject_UnsupportedOperator() {
 773    testFilterByChildObject_UnsupportedOperator(new AttachedBookProvider());
 774    testFilterByChildObject_UnsupportedOperator(new TransientBookProvider());
 775  }
 776
 777  private void testFilterByChildObject_ValueWithoutAncestor(BookProvider bp) {
 778    Entity parentEntity = new Entity(HasOneToOneJPA.class.getSimpleName());
 779    ds.put(parentEntity);
 780    Entity bookEntity = Book.newBookEntity("Joe Blow", "11111", "Bar Book", 1929);
 781    ds.put(bookEntity);
 782
 783    Book book = bp.getBook(bookEntity.getKey());
 784    Query q = em.createQuery(
 785        "select from " + HasOneToOneJPA.class.getName() + " c where book = :b");
 786    q.setParameter("b", book);
 787    q.setHint(DatastoreManager.QUERYEXT_INMEMORY_WHEN_UNSUPPORTED, "false");
 788    try {
 789      q.getResultList();
 790      fail("expected JPAException");
 791    } catch (PersistenceException e) {
 792      // good
 793    }
 794  }
 795
 796  public void testFilterByChildObject_ValueWithoutAncestor() {
 797    testFilterByChildObject_ValueWithoutAncestor(new AttachedBookProvider());
 798    testFilterByChildObject_ValueWithoutAncestor(new TransientBookProvider());
 799  }
 800
 801  public void testFilterByChildObject_KeyIsWrongType() {
 802    Entity parentEntity = new Entity(HasOneToOneJPA.class.getSimpleName());
 803    ds.put(parentEntity);
 804
 805    Query q = em.createQuery(
 806        "select from " + HasOneToOneJPA.class.getName() + " c where book = :b");
 807    q.setParameter("b", parentEntity.getKey());
 808    q.setHint(DatastoreManager.QUERYEXT_INMEMORY_WHEN_UNSUPPORTED, "false");
 809    try {
 810      q.getResultList();
 811      fail("expected JPAException");
 812    } catch (PersistenceException e) {
 813      // good
 814    }
 815  }
 816
 817  public void testFilterByChildObject_KeyParentIsWrongType() {
 818    Key parent = KeyFactory.createKey("yar", 44);
 819    Entity bookEntity = new Entity(Book.class.getSimpleName(), parent);
 820
 821    Query q = em.createQuery(
 822        "select from " + HasOneToOneJPA.class.getName() + " c where book = :b");
 823    q.setParameter("b", bookEntity.getKey());
 824
 825    assertTrue(q.getResultList().isEmpty());
 826  }
 827
 828  public void testFilterByChildObject_ValueWithoutId() {
 829    Entity parentEntity = new Entity(HasOneToOneJPA.class.getSimpleName());
 830    ds.put(parentEntity);
 831    Entity bookEntity = Book.newBookEntity("Joe Blow", "11111", "Bar Book", 1929);
 832    ds.put(bookEntity);
 833
 834    Book book = new Book();
 835    Query q = em.createQuery(
 836        "select from " + HasOneToOneJPA.class.getName() + " c where book = :b");
 837    q.setParameter("b", book);
 838    q.setHint(DatastoreManager.QUERYEXT_INMEMORY_WHEN_UNSUPPORTED, "false");
 839    try {
 840      q.getResultList();
 841      fail("expected JPAException");
 842    } catch (PersistenceException e) {
 843      // good
 844    }
 845  }
 846
 847  public void testFilterByParentObject() {
 848    Entity parentEntity = new Entity(HasOneToManyListJPA.class.getSimpleName());
 849    ds.put(parentEntity);
 850    Entity bidirEntity =
 851        new Entity(BidirectionalChildListJPA.class.getSimpleName(), parentEntity.getKey());
 852    ds.put(bidirEntity);
 853    Entity bidirEntity2 =
 854        new Entity(BidirectionalChildListJPA.class.getSimpleName(), parentEntity.getKey());
 855    ds.put(bidirEntity2);
 856
 857    HasOneToManyListJPA parent =
 858        em.find(HasOneToManyListJPA.class, KeyFactory.keyToString(parentEntity.getKey()));
 859    Query q = em.createQuery("SELECT FROM " +
 860                             BidirectionalChildListJPA.class.getName() +
 861                             " c WHERE parent = :p");
 862
 863    q.setParameter("p", parent);
 864    @SuppressWarnings("unchecked")
 865    List<BidirectionalChildListJPA> result = (List<BidirectionalChildListJPA>) q.getResultList();
 866    assertEquals(2, result.size());
 867    assertEquals(bidirEntity.getKey(), KeyFactory.stringToKey(result.get(0).getId()));
 868    assertEquals(bidirEntity2.getKey(), KeyFactory.stringToKey(result.get(1).getId()));
 869  }
 870
 871  public void testFilterByParentLongObjectId() {
 872    Entity parentEntity = new Entity(HasOneToManyLongPkListJPA.class.getSimpleName());
 873    ds.put(parentEntity);
 874    Entity bidirEntity =
 875        new Entity(BidirectionalChildLongPkListJPA.class.getSimpleName(), parentEntity.getKey());
 876    ds.put(bidirEntity);
 877    Entity bidirEntity2 =
 878        new Entity(BidirectionalChildLongPkListJPA.class.getSimpleName(), parentEntity.getKey());
 879    ds.put(bidirEntity2);
 880
 881    HasOneToManyLongPkListJPA parent =
 882        em.find(HasOneToManyLongPkListJPA.class, KeyFactory.keyToString(parentEntity.getKey()));
 883    Query q = em.createQuery("SELECT FROM " +
 884                             BidirectionalChildLongPkListJPA.class.getName() + " c WHERE parent = :p");
 885
 886    q.setParameter("p", parent.getId());
 887    @SuppressWarnings("unchecked")
 888    List<BidirectionalChildLongPkListJPA> result = (List<BidirectionalChildLongPkListJPA>) q.getResultList();
 889    assertEquals(2, result.size());
 890    assertEquals(bidirEntity.getKey(), KeyFactory.stringToKey(result.get(0).getId()));
 891    assertEquals(bidirEntity2.getKey(), KeyFactory.stringToKey(result.get(1).getId()));
 892  }
 893
 894  public void testFilterByParentIntObjectId() {
 895    Entity parentEntity = new Entity(HasOneToManyLongPkListJPA.class.getSimpleName());
 896    ds.put(parentEntity);
 897    Entity bidirEntity =
 898        new Entity(BidirectionalChildLongPkListJPA.class.getSimpleName(), parentEntity.getKey());
 899    ds.put(bidirEntity);
 900    Entity bidirEntity2 =
 901        new Entity(BidirectionalChildLongPkListJPA.class.getSimpleName(), parentEntity.getKey());
 902    ds.put(bidirEntity2);
 903
 904    HasOneToManyLongPkListJPA parent =
 905        em.find(HasOneToManyLongPkListJPA.class, KeyFactory.keyToString(parentEntity.getKey()));
 906    Query q = em.createQuery("SELECT FROM " +
 907                             BidirectionalChildLongPkListJPA.class.getName() + " c WHERE parent = :p");
 908
 909    q.setParameter("p", parent.getId().intValue());
 910    @SuppressWarnings("unchecked")
 911    List<BidirectionalChildLongPkListJPA> result = (List<BidirectionalChildLongPkListJPA>) q.getResultList();
 912    assertEquals(2, result.size());
 913    assertEquals(bidirEntity.getKey(), KeyFactory.stringToKey(result.get(0).getId()));
 914    assertEquals(bidirEntity2.getKey(), KeyFactory.stringToKey(result.get(1).getId()));
 915  }
 916
 917  public void testFilterByParentObjectWhereParentIsAChild() {
 918    Entity parentEntity = new Entity(HasOneToManyListJPA.class.getSimpleName());
 919    ds.put(parentEntity);
 920    Entity childEntity = new Entity(BidirectionalChildListJPA.class.getSimpleName(), parentEntity.getKey());
 921    ds.put(childEntity);
 922    Entity grandChildEntity1 =
 923        new Entity(BidirectionalGrandchildListJPA.class.getSimpleName(), childEntity.getKey());
 924    ds.put(grandChildEntity1);
 925    Entity grandChildEntity2 =
 926        new Entity(BidirectionalGrandchildListJPA.class.getSimpleName(), childEntity.getKey());
 927    ds.put(grandChildEntity2);
 928
 929    BidirectionalChildListJPA child =
 930        em.find(BidirectionalChildListJPA.class, KeyFactory.keyToString(childEntity.getKey()));
 931    Query q = em.createQuery(
 932        "select from " + BidirectionalGrandchildListJPA.class.getName() + " c where parent = :p");
 933    q.setParameter("p", child);
 934    @SuppressWarnings("unchecked")
 935    List<BidirectionalGrandchildListJPA> result = (List<BidirectionalGrandchildListJPA>) q.getResultList();
 936    assertEquals(2, result.size());
 937    assertEquals(grandChildEntity1.getKey(), KeyFactory.stringToKey(result.get(0).getId()));
 938    assertEquals(grandChildEntity2.getKey(), KeyFactory.stringToKey(result.get(1).getId()));
 939  }
 940  public void testFilterByParentId() {
 941    Entity parentEntity = new Entity(HasOneToManyListJPA.class.getSimpleName());
 942    ds.put(parentEntity);
 943    Entity
 944        bidirEntity =
 945        new Entity(BidirectionalChildListJPA.class.getSimpleName(), parentEntity.getKey());
 946    ds.put(bidirEntity);
 947    Entity
 948        bidirEntity2 =
 949        new Entity(BidirectionalChildListJPA.class.getSimpleName(), parentEntity.getKey());
 950    ds.put(bidirEntity2);
 951
 952    HasOneToManyListJPA parent =
 953        em.find(HasOneToManyListJPA.class, KeyFactory.keyToString(parentEntity.getKey()));
 954    Query q = em.createQuery("SELECT FROM " +
 955                             BidirectionalChildListJPA.class.getName() +
 956                             " c WHERE parent = :p");
 957
 958    q.setParameter("p", parent.getId());
 959    @SuppressWarnings("unchecked")
 960    List<BidirectionalChildListJPA> result = (List<BidirectionalChildListJPA>) q.getResultList();
 961    assertEquals(2, result.size());
 962    assertEquals(bidirEntity.getKey(), KeyFactory.stringToKey(result.get(0).getId()));
 963    assertEquals(bidirEntity2.getKey(), KeyFactory.stringToKey(result.get(1).getId()));
 964  }
 965
 966  public void testFilterByParentKey() {
 967    Entity parentEntity = new Entity(HasOneToManyListJPA.class.getSimpleName());
 968    ds.put(parentEntity);
 969    Entity
 970        bidirEntity =
 971        new Entity(BidirectionalChildListJPA.class.getSimpleName(), parentEntity.getKey());
 972    ds.put(bidirEntity);
 973    Entity
 974        bidirEntity2 =
 975        new Entity(BidirectionalChildListJPA.class.getSimpleName(), parentEntity.getKey());
 976    ds.put(bidirEntity2);
 977
 978    Query q = em.createQuery("SELECT FROM " +
 979                             BidirectionalChildListJPA.class.getName() +
 980                             " c WHERE parent = :p");
 981
 982    q.setParameter("p", parentEntity.getKey());
 983    @SuppressWarnings("unchecked")
 984    List<BidirectionalChildListJPA> result = (List<BidirectionalChildListJPA>) q.getResultList();
 985    assertEquals(2, result.size());
 986    assertEquals(bidirEntity.getKey(), KeyFactory.stringToKey(result.get(0).getId()));
 987    assertEquals(bidirEntity2.getKey(), KeyFactory.stringToKey(result.get(1).getId()));
 988  }
 989
 990  public void testFilterByMultiValueProperty() {
 991    Entity entity = new Entity(HasMultiValuePropsJPA.class.getSimpleName());
 992    entity.setProperty("strList", Utils.newArrayList("1", "2", "3"));
 993    entity.setProperty("keyList",
 994                       Utils.newArrayList(KeyFactory.createKey("be", "bo"),
 995                                          KeyFactory.createKey("bo", "be")));
 996    ds.put(entity);
 997
 998    Query q = em.createQuery(
 999        "select from " + HasMultiValuePropsJPA.class.getName()
1000        + " c where strList = :p1 AND strList = :p2");
1001    q.setParameter("p1", "1");
1002    q.setParameter("p2", "3");
1003    @SuppressWarnings("unchecked")
1004    List<HasMultiValuePropsJPA> result = (List<HasMultiValuePropsJPA>) q.getResultList();
1005    assertEquals(1, result.size());
1006    q.setParameter("p1", "1");
1007    q.setParameter("p2", "4");
1008    @SuppressWarnings("unchecked")
1009    List<HasMultiValuePropsJPA> result2 = (List<HasMultiValuePropsJPA>) q.getResultList();
1010    assertEquals(0, result2.size());
1011
1012    q = em.createQuery(
1013        "select from " + HasMultiValuePropsJPA.class.getName() + " c where keyList = :p1 AND keyList = :p2");
1014    q.setParameter("p1", KeyFactory.createKey("be", "bo"));
1015    q.setParameter("p2", KeyFactory.createKey("bo", "be"));
1016    assertEquals(1, result.size());
1017    q.setParameter("p1", KeyFactory.createKey("be", "bo"));
1018    q.setParameter("p2", KeyFactory.createKey("bo", "be2"));
1019    @SuppressWarnings("unchecked")
1020    List<HasMultiValuePropsJPA> result3 = (List<HasMultiValuePropsJPA>) q.getResultList();
1021    assertEquals(0, result3.size());
1022  }
1023
1024  public void testNoPutsAfterLoadingMultiValueProperty() throws NoSuchMethodException {
1025    switchDatasource(EntityManagerFactoryName.nontransactional_ds_non_transactional_ops_allowed);
1026    testFilterByMultiValueProperty();
1027    em.close();
1028  }
1029
1030  public void testFilterByMultiValueProperty_MemberOf_ArgsWrongOrder() {
1031    Entity entity = new Entity(HasMultiValuePropsJPA.class.getSimpleName());
1032    entity.setProperty("strList", Utils.newArrayList("1", "2", "3"));
1033    entity.setProperty("keyList",
1034                       Utils.newArrayList(KeyFactory.createKey("be", "bo"),
1035                                          KeyFactory.createKey("bo", "be")));
1036    ds.put(entity);
1037
1038    Query q = em.createQuery(
1039        "select from " + HasMultiValuePropsJPA.class.getName()
1040        + " c where strList MEMBER OF :p1 AND strList MEMBER OF :p2");
1041    q.setParameter("p1", "1");
1042    q.setParameter("p2", "3");
1043    @SuppressWarnings("unchecked")
1044    List<HasMultiValuePropsJPA> result = (List<HasMultiValuePropsJPA>) q.getResultList();
1045    assertEquals(1, result.size());
1046    q.setParameter("p1", "1");
1047    q.setParameter("p2", "4");
1048    @SuppressWarnings("unchecked")
1049    List<HasMultiValuePropsJPA> result2 = (List<HasMultiValuePropsJPA>) q.getResultList();
1050    assertEquals(0, result2.size());
1051
1052    q = em.createQuery(
1053        "select from " + HasMultiValuePropsJPA.class.getName()
1054        + " c where keyList MEMBER OF :p1 AND keyList MEMBER OF :p2");
1055    q.setParameter("p1", KeyFactory.createKey("be", "bo"));
1056    q.setParameter("p2", KeyFactory.createKey("bo", "be"));
1057    result = q.getResultList();
1058    assertEquals(1, result.size());
1059    q.setParameter("p1", KeyFactory.createKey("be", "bo"));
1060    q.setParameter("p2", KeyFactory.createKey("bo", "be2"));
1061    @SuppressWarnings("unchecked")
1062    List<HasMultiValuePropsJPA> result3 = (List<HasMultiValuePropsJPA>) q.getResultList();
1063    assertEquals(0, result3.size());
1064  }
1065
1066  public void testFilterByMultiValueProperty_MemberOf_ArgsCorrectOrder() {
1067    Entity entity = new Entity(HasMultiValuePropsJPA.class.getSimpleName());
1068    entity.setProperty("strList", Utils.newArrayList("1", "2", "3"));
1069    entity.setProperty("keyList",
1070                       Utils.newArrayList(KeyFactory.createKey("be", "bo"),
1071                                          KeyFactory.createKey("bo", "be")));
1072    ds.put(entity);
1073
1074    Query q = em.createQuery(
1075        "select from " + HasMultiValuePropsJPA.class.getName()
1076        + " c where :p1 MEMBER OF strList AND :p2 MEMBER OF strList");
1077    q.setParameter("p1", "1");
1078    q.setParameter("p2", "3");
1079    @SuppressWarnings("unchecked")
1080    List<HasMultiValuePropsJPA> result = (List<HasMultiValuePropsJPA>) q.getResultList();
1081    assertEquals(1, result.size());
1082    q.setParameter("p1", "1");
1083    q.setParameter("p2", "4");
1084    @SuppressWarnings("unchecked")
1085    List<HasMultiValuePropsJPA> result2 = (List<HasMultiValuePropsJPA>) q.getResultList();
1086    assertEquals(0, result2.size());
1087
1088    q = em.createQuery(
1089        "select from " + HasMultiValuePropsJPA.class.getName()
1090        + " c where :p1 MEMBER OF keyList AND :p2 MEMBER OF keyList");
1091    q.setParameter("p1", KeyFactory.createKey("be", "bo"));
1092    q.setParameter("p2", KeyFactory.createKey("bo", "be"));
1093    result = q.getResultList();
1094    assertEquals(1, result.size());
1095    q.setParameter("p1", KeyFactory.createKey("be", "bo"));
1096    q.setParameter("p2", KeyFactory.createKey("bo", "be2"));
1097    @SuppressWarnings("unchecked")
1098    List<HasMultiValuePropsJPA> result3 = (List<HasMultiValuePropsJPA>) q.getResultList();
1099    assertEquals(0, result3.size());
1100
1101    // try one with a literal value
1102    q = em.createQuery(
1103        "select from " + HasMultiValuePropsJPA.class.getName()
1104        + " c where '1' MEMBER OF strList");
1105    result = q.getResultList();
1106    assertEquals(1, result.size());
1107  }
1108
1109  public void testFilterByEmbeddedField() {
1110    Entity entity = new Entity(Person.class.getSimpleName());
1111    entity.setProperty("first", "max");
1112    entity.setProperty("last", "ross");
1113    entity.setProperty("anotherFirst", "notmax");
1114    entity.setProperty("anotherLast", "notross");
1115    ds.put(entity);
1116
1117    Query q = em.createQuery(
1118        "select from " + Person.class.getName() + " c where name.first = \"max\"");
1119    @SuppressWarnings("unchecked")
1120    List<Person> result = (List<Person>) q.getResultList();
1121    assertEquals(1, result.size());
1122  }
1123
1124  public void testFilterByEmbeddedField_OverriddenColumn() {
1125    Entity entity = new Entity(Person.class.getSimpleName());
1126    entity.setProperty("first", "max");
1127    entity.setProperty("last", "ross");
1128    entity.setProperty("anotherFirst", "notmax");
1129    entity.setProperty("anotherLast", "notross");
1130    ds.put(entity);
1131
1132    Query q = em.createQuery(
1133        "select from " + Person.class.getName()
1134        + " c where anotherName.last = \"notross\"");
1135    @SuppressWarnings("unchecked")
1136    List<Person> result = (List<Person>) q.getResultList();
1137    assertEquals(1, result.size());
1138  }
1139
1140  public void testFilterByEmbeddedField_MultipleFields() {
1141    Entity entity = new Entity(Person.class.getSimpleName());
1142    entity.setProperty("first", "max");
1143    entity.setProperty("last", "ross");
1144    entity.setProperty("anotherFirst", "max");
1145    entity.setProperty("anotherLast", "notross");
1146    ds.put(entity);
1147
1148    Query q = em.createQuery(
1149        "select from " + Person.class.getName()
1150        + " c where name.first = \"max\" AND anotherName.last = \"notross\"");
1151    @SuppressWarnings("unchecked")
1152    List<Person> result = (List<Person>) q.getResultList();
1153    assertEquals(1, result.size());
1154  }
1155
1156  public void testFilterBySubObject_UnknownField() {
1157    try {
1158      em.createQuery(
1159          "select from " + Flight.class.getName() + " c where origin.first = \"max\"")
1160          .getResultList();
1161      fail("expected exception");
1162    } catch (PersistenceException e) {
1163      // good
1164    }
1165  }
1166
1167  public void testFilterBySubObject_NotEmbeddable() {
1168    try {
1169      em.createQuery(
1170          "select from " + HasOneToOneJPA.class.getName() + " c where flight.origin = \"max\"")
1171          .getResultList();
1172      fail("expected exception");
1173    } catch (PersistenceException e) {
1174      // good
1175    }
1176  }
1177
1178  public void testSortByEmbeddedField() {
1179    Entity entity = new Entity(Person.class.getSimpleName());
1180    entity.setProperty("first", "max");
1181    entity.setProperty("last", "ross");
1182    entity.setProperty("anotherFirst", "notmax");
1183    entity.setProperty("anotherLast", "notross");
1184    ds.put(entity);
1185
1186    entity = new Entity(Person.class.getSimpleName());
1187    entity.setProperty("first", "max2");
1188    entity.setProperty("last", "ross2");
1189    entity.setProperty("anotherFirst", "notmax2");
1190    entity.setProperty("anotherLast", "notross2");
1191    ds.put(entity);
1192
1193    Query q = em.createQuery("select from " + Person.class.getName() + " c order by name.first desc");
1194    @SuppressWarnings("unchecked")
1195    List<Person> result = (List<Person>) q.getResultList();
1196    assertEquals(2, result.size());
1197    assertEquals("max2", result.get(0).getName().getFirst());
1198    assertEquals("max", result.get(1).getName().getFirst());
1199  }
1200
1201  public void testSortByEmbeddedField_OverriddenColumn() {
1202    Entity entity = new Entity(Person.class.getSimpleName());
1203    entity.setProperty("first", "max");
1204    entity.setProperty("last", "ross");
1205    entity.setProperty("anotherFirst", "notmax");
1206    entity.setProperty("anotherLast", "notross");
1207    ds.put(entity);
1208
1209    entity = new Entity(Person.class.getSimpleName());
1210    entity.setProperty("first", "max2");
1211    entity.setProperty("last", "ross2");
1212    entity.setProperty("anotherFirst", "notmax2");
1213    entity.setProperty("anotherLast", "notross2");
1214    ds.put(entity);
1215
1216    Query q =
1217        em.createQuery("select from " + Person.class.getName() + " c order by anotherName.last desc");
1218    @SuppressWarnings("unchecked")
1219    List<Person> result = (List<Person>) q.getResultList();
1220    assertEquals(2, result.size());
1221    assertEquals("notross2", result.get(0).getAnotherName().getLast());
1222    assertEquals("notross", result.get(1).getAnotherName().getLast());
1223  }
1224
1225  public void testSortByEmbeddedField_MultipleFields() {
1226    Entity entity0 = new Entity(Person.class.getSimpleName());
1227    entity0.setProperty("first", "max");
1228    entity0.setProperty("last", "ross");
1229    entity0.setProperty("anotherFirst", "notmax");
1230    entity0.setProperty("anotherLast", "z");
1231    ds.put(entity0);
1232
1233    Entity entity1 = new Entity(Person.class.getSimpleName());
1234    entity1.setProperty("first", "max");
1235    entity1.setProperty("last", "ross2");
1236    entity1.setProperty("anotherFirst", "notmax2");
1237    entity1.setProperty("anotherLast", "notross2");
1238    ds.put(entity1);
1239
1240    Entity entity2 = new Entity(Person.class.getSimpleName());
1241    entity2.setProperty("first", "a");
1242    entity2.setProperty("last", "b");
1243    entity2.setProperty("anotherFirst", "c");
1244    entity2.setProperty("anotherLast", "d");
1245    ds.put(entity2);
1246
1247    Query q = em.createQuery(
1248        "select from " + Person.class.getName()
1249        + " c order by name.first asc, anotherName.last desc");
1250    @SuppressWarnings("unchecked")
1251    List<Person> result = (List<Person>) q.getResultList();
1252    assertEquals(3, result.size());
1253    assertEquals(Long.valueOf(entity2.getKey().getId()), result.get(0).getId());
1254    assertEquals(Long.valueOf(entity0.getKey().getId()), result.get(1).getId());
1255    assertEquals(Long.valueOf(entity1.getKey().getId()), result.get(2).getId());
1256  }
1257
1258  public void testSortBySubObject_UnknownField() {
1259    try {
1260      Query q = em.createQuery("select from " + Book.class.getName() + " c order by author.first");
1261      q.setHint(DatastoreManager.QUERYEXT_INMEMORY_WHEN_UNSUPPORTED, "false");
1262      q.getResultList();
1263      fail("expected exception");
1264    } catch (PersistenceException e) {
1265      // good
1266    }
1267  }
1268
1269  public void testSortBySubObject_NotEmbeddable() {
1270    try {
1271      Query q = em.createQuery(
1272          "select from " + HasOneToOneJPA.class.getName() + " c order by book.author");
1273      q.setHint(DatastoreManager.QUERYEXT_INMEMORY_WHEN_UNSUPPORTED, "false");
1274      q.getResultList();
1275      fail("expected exception");
1276    } catch (PersistenceException e) {
1277      // good
1278    }
1279  }
1280
1281  public void testBigDecimalQuery() {
1282    Map<String, String> props = new HashMap<String, String>();
1283    props.put("datanucleus.appengine.BigDecimalsEncoding", "String");
1284    switchDatasource(EntityManagerFactoryName.originalStorageVersion, props);
1285    
1286    Entity e = KitchenSink.newKitchenSinkEntity("blarg", null);
1287    ds.put(e);
1288
1289    Query q = em.createQuery(
1290        "select from " + KitchenSink.class.getName() + " c where bigDecimal = :bd");
1291    q.setParameter("bd", new BigDecimal("2.444"));
1292    @SuppressWarnings("unchecked")
1293    List<KitchenSink> results = (List<KitchenSink>) q.getResultList();
1294    assertEquals(1, results.size());
1295  }
1296  
1297  public void testBigDecimalInequalityQuery() {
1298    Map<String, String> props = new HashMap<String, String>();
1299    props.put("datanucleus.appengine.BigDecimalsEncoding", "String");
1300    switchDatasource(EntityManagerFactoryName.originalStorageVersion, props);
1301    
1302    Entity e = KitchenSink.newKitchenSinkEntity("blarg", null);
1303    ds.put(e);
1304
1305    Query q = em.createQuery(
1306        "select from " + KitchenSink.class.getName() + " c where bigDecimal < :bd1 and bigDecimal > :bd2");
1307    q.setParameter("bd1", new BigDecimal("3"));
1308    q.setParameter("bd2", new BigDecimal("2"));
1309    @SuppressWarnings("unchecked")
1310    List<KitchenSink> results = (List<KitchenSink>) q.getResultList();
1311    assertEquals(1, results.size());
1312  }
1313  
1314  public void testQueryWithLiteralFloat() {
1315    Entity e = new Entity(HasFloatJPA.class.getSimpleName());
1316    e.setProperty("aFloat", -2.23f);
1317    ds.put(e);
1318
1319    Query q = em.createQuery(
1320        "select from " + HasFloatJPA.class.getName() + " c where aFloat > -2.25");
1321    @SuppressWarnings("unchecked")
1322    List<KitchenSink> results = (List<KitchenSink>) q.getResultList();
1323    assertEquals(1, results.size());
1324  }
1325
1326  public void testQueryWithNegativeLiteralLong() {
1327    ds.put(Book.newBookEntity("auth", "123432", "title", -40));
1328
1329    Query q = em.createQuery(
1330        "select from " + Book.class.getName() + " c where firstPublished = -40");
1331    @SuppressWarnings("unchecked")
1332    List<Book> results = (List<Book>) q.getResultList();
1333    assertEquals(1, results.size());
1334    q = em.createQuery(
1335        "select from " + Book.class.getName() + " c where firstPublished > -41");
1336    @SuppressWarnings("unchecked")
1337    List<Book> results2 = (List<Book>) q.getResultList();
1338    assertEquals(1, results2.size());
1339  }
1340
1341  public void testQueryWithNegativeLiteralDouble() {
1342    Entity e = new Entity(HasDoubleJPA.class.getSimpleName());
1343    e.setProperty("aDouble", -2.23d);
1344    ds.put(e);
1345
1346    Query q = em.createQuery(
1347        "select from " + HasDoubleJPA.class.getName() + " c where aDouble > -2.25");
1348    @SuppressWarnings("unchecked")
1349    List<KitchenSink> results = (List<KitchenSink>) q.getResultList();
1350    assertEquals(1, results.size());
1351  }
1352
1353  public void testQueryWithNegativeParam() {
1354    ds.put(Book.newBookEntity("auth", "123432", "title", -40));
1355
1356    Query q = em.createQuery(
1357        "select from " + Book.class.getName() + " c where firstPublished = :p");
1358    q.setParameter("p", -40);
1359    @SuppressWarnings("unchecked")
1360    List<Book> results = (List<Book>) q.getResultList();
1361    assertEquals(1, results.size());
1362  }
1363
1364  public void testKeyQueryWithUnencodedStringPk() {
1365    Entity e = new Entity(HasUnencodedStringPkJPA.class.getSimpleName(), "yar");
1366    ds.put(e);
1367    Query q = em.createQuery(
1368        "select from " + HasUnencodedStringPkJPA.class.getName() + " c where id = :p");
1369    q.setParameter("p", e.getKey().getName());
1370    @SuppressWarnings("unchecked")
1371    List<HasUnencodedStringPkJPA> results =
1372        (List<HasUnencodedStringPkJPA>) q.getResultList();
1373    assertEquals(1, results.size());
1374    assertEquals(e.getKey().getName(), results.get(0).getId());
1375
1376    q = em.createQuery(
1377        "select from " + HasUnencodedStringPkJPA.class.getName() + " c where id = :p");
1378    q.setParameter("p", e.getKey());
1379    @SuppressWarnings("unchecked")
1380    List<HasUnencodedStringPkJPA> results2 =
1381        (List<HasUnencodedStringPkJPA>) q.getResultList();
1382    assertEquals(1, results2.size());
1383    assertEquals(e.getKey().getName(), results2.get(0).getId());
1384  }
1385
1386  public void testKeyQueryWithLongPk() {
1387    Entity e = new Entity(HasLongPkJPA.class.getSimpleName());
1388    ds.put(e);
1389    Query q = em.createQuery(
1390        "select from " + HasLongPkJPA.class.getName() + " c where id = :p");
1391    q.setParameter("p", e.getKey().getId());
1392    @SuppressWarnings("unchecked")
1393    List<HasLongPkJPA> results = (List<HasLongPkJPA>) q.getResultList();
1394    assertEquals(1, results.size());
1395    assertEquals(Long.valueOf(e.getKey().getId()), results.get(0).getId());
1396
1397    q = em.createQuery(
1398        "select from " + HasLongPkJPA.class.getName() + " c where id = :p");
1399    q.setParameter("p", e.getKey().getId());
1400    @SuppressWarnings("unchecked")
1401    List<HasLongPkJPA> results2 = (List<HasLongPkJPA>) q.getResultList();
1402    assertEquals(1, results2.size());
1403    assertEquals(Long.valueOf(e.getKey().getId()), results2.get(0).getId());
1404  }
1405
1406  public void testKeyQueryWithEncodedStringPk() {
1407    Entity e = new Entity(HasEncodedStringPkJPA.class.getSimpleName(), "yar");
1408    ds.put(e);
1409    Query q = em.createQuery("select from " + HasEncodedStringPkJPA.class.getName() + " c where id = :p");
1410    q.setParameter("p", e.getKey().getName());
1411    HasEncodedStringPkJPA result = (HasEncodedStringPkJPA) q.getSingleResult();
1412    assertEquals(KeyFactory.keyToString(e.getKey()), result.getId());
1413
1414    q = em.createQuery("select from " + HasEncodedStringPkJPA.class.getName() + " c where id = :p");
1415    q.setParameter("p", e.getKey());
1416    result = (HasEncodedStringPkJPA) q.getSingleResult();
1417    assertEquals(KeyFactory.keyToString(e.getKey()), result.getId());
1418
1419    q = em.createQuery("select from " + HasEncodedStringPkJPA.class.getName() + " c where id = :p");
1420    q.setParameter("p", e.getKey().getName());
1421    result = (HasEncodedStringPkJPA) q.getSingleResult();
1422    assertEquals(KeyFactory.keyToString(e.getKey()), result.getId());
1423  }
1424
1425  public void testQuerySingleResult_OneResult() {
1426    Entity e = Book.newBookEntity("max", "12345", "t1");
1427    ds.put(e);
1428    Query q = em.createQuery(
1429        "select from " + Book.class.getName() + " c where title = :p");
1430    q.setParameter("p", "t1");
1431    Book pojo = (Book) q.getSingleResult();
1432    assertEquals(e.getKey(), KeyFactory.stringToKey(pojo.getId()));
1433  }
1434
1435  public void testQuerySingleResult_NoResult() {
1436    Entity e = Book.newBookEntity("max", "12345", "t1");
1437    ds.put(e);
1438    Query q = em.createQuery(
1439        "select from " + Book.class.getName() + " c where title = :p");
1440    q.setParameter("p", "not t1");
1441    try {
1442      q.getSingleResult();
1443      fail("expected exception");
1444    } catch (NoResultException ex) {
1445      // good
1446    }
1447  }
1448
1449  public void testQuerySingleResult_MultipleResults() {
1450    Entity e1 = Book.newBookEntity("max", "12345", "t1");
1451    Entity e2 = Book.newBookEntity("max", "12345", "t1");
1452    ds.put(e1);
1453    ds.put(e2);
1454    Query q = em.createQuery(
1455        "select from " + Book.class.getName() + " c where title = :p");
1456    q.setParameter("p", "t1");
1457    try {
1458      q.getSingleResult();
1459      fail("expected exception");
1460    } catch (NonUniqueResultException ex) {
1461      // good
1462    }
1463  }
1464
1465
1466  public void testSortByUnknownProperty() {
1467    try {
1468      Query q = em.createQuery("select from " + Book.class.getName() + " c order by dne");
1469      q.setHint(DatastoreManager.QUERYEXT_INMEMORY_WHEN_UNSUPPORTED, "false");
1470      q.getResultList();
1471      fail("expected exception");
1472    } catch (PersistenceException e) {
1473      // good
1474    }
1475  }
1476
1477  public void testDatastoreFailureWhileIterating() {
1478    // Need to have enough data to ensure a Next call
1479    for (int i = 0; i < 21; i++) {
1480      Entity bookEntity = Book.newBookEntity("Joe Blow", "67890", "Bar Book");
1481      ds.put(bookEntity);
1482    }
1483    ExceptionThrowingDatastoreDelegate.ExceptionPolicy policy =
1484        new ExceptionThrowingDatastoreDelegate.BaseExceptionPolicy() {
1485          boolean exploded = false;
1486          protected void doIntercept(String methodName) {
1487            if (!exploded && methodName.equals("Next")) {
1488              exploded = true;
1489              throw new DatastoreFailureException("boom: " + methodName);
1490            }
1491          }
1492        };
1493
1494    ApiProxy.Delegate original = getDelegateForThread();
1495    ExceptionThrowingDatastoreDelegate dd =
1496        new ExceptionThrowingDatastoreDelegate(getDelegateForThread(), policy);
1497    setDelegateForThread(dd);
1498
1499    try {
1500      javax.persistence.Query q = em.createQuery(
1501          "select from " + Book.class.getName() + " b");
1502      try {
1503        @SuppressWarnings("unchecked")
1504        List<Book> books = (List<Book>) q.getResultList();
1505        books.size();
1506        fail("expected exception");
1507      } catch (PersistenceException e) {
1508        // good
1509        assertTrue(e.getCause() instanceof DatastoreFailureException);
1510      }
1511    } finally {
1512      setDelegateForThread(original);
1513    }
1514  }
1515
1516  public void testBadRequest() {
1517    ExceptionThrowingDatastoreDelegate.ExceptionPolicy policy =
1518        new ExceptionThrowingDatastoreDelegate.BaseExceptionPolicy() {
1519          int count = 0;
1520
1521          protected void doIntercept(String methodName) {
1522            count++;
1523            if (count == 1) {
1524              throw new IllegalArgumentException("boom");
1525            }
1526          }
1527        };
1528    ApiProxy.Delegate original = getDelegateForThread();
1529
1530    ExceptionThrowingDatastoreDelegate dd =
1531        new ExceptionThrowingDatastoreDelegate(getDelegateForThread(), policy);
1532    setDelegateForThread(dd);
1533
1534    try {
1535      Query q = em.createQuery("select from " + Book.class.getName() + " b");
1536      try {
1537        q.getResultList();
1538        fail("expected exception");
1539      } catch (PersistenceException e) {
1540        // good
1541        assertTrue(e.getCause() instanceof IllegalArgumentException);
1542      }
1543    } finally {
1544      setDelegateForThread(original);
1545    }
1546  }
1547
1548  public void testCountQuery() {
1549    Query q = em.createQuery("select count(id) from " + Book.class.getName() + " c");
1550    assertEquals(0l, q.getSingleResult());
1551
1552    Entity e1 = Book.newBookEntity("jimmy", "12345", "the title", 2003);
1553    Entity e2 = Book.newBookEntity("jimmy", "12345", "the title", 2004);
1554    ds.put(e1);
1555    ds.put(e2);
1556    q = em.createQuery("select count(id) from " + Book.class.getName() + " c");
1557    assertEquals(2l, q.getSingleResult());
1558
1559    q = em.createQuery("select COUNT(id) from " + Book.class.getName() + " c");
1560    assertEquals(2l, q.getSingleResult());
1561
1562    q = em.createQuery("select Count(id) from " + Book.class.getName() + " c");
1563    assertEquals(2l, q.getSingleResult());
1564  }
1565
1566  public void testProjectionQueryTyped() {
1567    Entity e1 = Book.newBookEntity("jimmy", "12345", "the title", 2003);
1568    Entity e2 = Book.newBookEntity("jimmy", "12345", "the title", 2004);
1569    ds.put(e1);
1570    ds.put(e2);
1571    TypedQuery<Integer> q = em.createQuery(
1572        "select firstPublished from " + Book.class.getName() + " b ORDER BY firstPublished ASC", Integer.class);
1573    List<Integer> result = q.getResultList();
1574    Iterator<Integer> resultIter = result.iterator();
1575    Integer first = resultIter.next();
1576    Integer second = resultIter.next();
1577    assertEquals(2003, first.intValue());
1578    assertEquals(2004, second.intValue());
1579  }
1580
1581  public void testProjectionWithCreator() {
1582    Entity e1 = Book.newBookEntity("jimmy", "12345", "the title 1", 2003);
1583    Entity e2 = Book.newBookEntity("bobby", "12346", "the title 2", 2004);
1584    ds.put(e1);
1585    ds.put(e2);
1586
1587    TypedQuery<BookSummary> q = em.createQuery(
1588        "SELECT NEW " + BookSummary.class.getName() + "(author, title) FROM " + Book.class.getName() + " b" +
1589        " ORDER BY firstPublished ASC", BookSummary.class);
1590    List<BookSummary> result = q.getResultList();
1591    assertEquals("Number of results is wrong", 2, result.size());
1592    Iterator<BookSummary> resultIter = result.iterator();
1593    BookSummary first = resultIter.next();
1594    BookSummary second = resultIter.next();
1595    assertEquals("jimmy", first.getAuthor());
1596    assertEquals("bobby", second.getAuthor());
1597    assertEquals("the title 1", first.getTitle());
1598    assertEquals("the title 2", second.getTitle());
1599  }
1600
1601  public void testCountQueryWithFilter() {
1602    Entity e1 = Book.newBookEntity("jimmy", "12345", "the title", 2003);
1603    Entity e2 = Book.newBookEntity("jimmy", "12345", "the title", 2004);
1604    ds.put(e1);
1605    ds.put(e2);
1606    Query q = em.createQuery(
1607            "select count(id) from " + Book.class.getName() + " c" + " where firstPublished = 2003");
1608    assertEquals(1l, q.getSingleResult());
1609  }
1610
1611  public void testCountQueryWithUnknownCountProp() {
1612    Entity e1 = Book.newBookEntity("jimmy", "12345", "the title", 2003);
1613    Entity e2 = Book.newBookEntity("jimmy", "12345", "the title", 2004);
1614    ds.put(e1);
1615    ds.put(e2);
1616    Query q = em.createQuery("select count(doesnotexist) from " + Book.class.getName() + " c");
1617    try {
1618      q.getSingleResult();
1619      fail("expected exception");
1620    } catch (PersistenceException e) {
1621      // good
1622    }
1623  }
1624
1625  public void testCountQueryWithOffset() {
1626    Entity e1 = Book.newBookEntity("jimmy", "12345", "the title", 2003);
1627    Entity e2 = Book.newBookEntity("jimmy", "12345", "the title", 2004);
1628    ds.put(e1);
1629    ds.put(e2);
1630    Query q = em.createQuery("select count(id) from " + Book.class.getName() + " c");
1631    q.setFirstResult(1);
1632    assertEquals(1l, q.getSingleResult());
1633  }
1634
1635  public void testCountQueryWithLimit() {
1636    Entity e1 = Book.newBookEntity("jimmy", "12345", "the title", 2003);
1637    Entity e2 = Book.newBookEntity("jimmy", "12345", "the title", 2004);
1638    ds.put(e1);
1639    ds.put(e2);
1640    Query q = em.createQuery("select count(id) from " + Book.class.getName() + " c");
1641    q.setMaxResults(1);
1642    assertEquals(1l, q.getSingleResult());
1643  }
1644
1645  public void testCountQueryWithOffsetAndLimit() {
1646    Entity e1 = Book.newBookEntity("jimmy", "12345", "the title", 2003);
1647    Entity e2 = Book.newBookEntity("jimmy", "12345", "the title", 2004);
1648    Entity e3 = Book.newBookEntity("jimmy", "12345", "the title", 2005);
1649    ds.put(e1);
1650    ds.put(e2);
1651    ds.put(e3);
1652    Query q = em.createQuery("select count(id) from " + Book.class.getName() + " c");
1653    q.setFirstResult(1);
1654    q.setMaxResults(1);
1655    assertEquals(1l, q.getSingleResult());
1656  }
1657
1658  public void testFilterByEnum_ProvideStringExplicitly() {
1659    Entity e = new Entity(HasEnumJPA.class.getSimpleName());
1660    e.setProperty("myEnum", HasEnumJPA.MyEnum.V1.name());
1661    ds.put(e);
1662    Query q = em.createQuery("select from " + HasEnumJPA.class.getName() + " c" + " where myEnum = :p1");
1663    q.setParameter("p1", HasEnumJPA.MyEnum.V1.name());
1664    List<HasEnumJPA> result = (List<HasEnumJPA>) q.getResultList();
1665    assertEquals(1, result.size());
1666  }
1667
1668  public void testFilterByEnum_ProvideEnumExplicitly() {
1669    Entity e = new Entity(HasEnumJPA.class.getSimpleName());
1670    e.setProperty("myEnum", HasEnumJPA.MyEnum.V1.name());
1671    ds.put(e);
1672    Query q = em.createQuery("select from " + HasEnumJPA.class.getName() + " c" + " where myEnum = :p1");
1673    q.setParameter("p1", HasEnumJPA.MyEnum.V1);
1674    List<HasEnumJPA> result = (List<HasEnumJPA>) q.getResultList();
1675    assertEquals(1, result.size());
1676  }
1677
1678  public void testFilterByEnum_ProvideLiteral() {
1679    Entity e = new Entity(HasEnumJPA.class.getSimpleName());
1680    e.setProperty("myEnum", HasEnumJPA.MyEnum.V1.name());
1681    ds.put(e);
1682    Query q = em.createQuery(
1683        "select from " + HasEnumJPA.class.getName() + " c" + " where myEnum = '"
1684        + HasEnumJPA.MyEnum.V1.name() + "'");
1685    List<HasEnumJPA> result = (List<HasEnumJPA>) q.getResultList();
1686    assertEquals(1, result.size());
1687  }
1688
1689  public void testFilterByShortBlob() {
1690    Entity e = new Entity(HasBytesJPA.class.getSimpleName());
1691    e.setProperty("onePrimByte", 8L);
1692    e.setProperty("shortBlob", new ShortBlob("short blob".getBytes()));
1693    ds.put(e);
1694    Query
1695        q =
1696        em.createQuery("select from " + HasBytesJPA.class.getName() + " c" + " where shortBlob = :p1");
1697    q.setParameter("p1", new ShortBlob("short blob".getBytes()));
1698    List<HasBytesJPA> result = (List<HasBytesJPA>) q.getResultList();
1699    assertEquals(1, result.size());
1700  }
1701
1702  public void testFilterByPrimitiveByteArray() {
1703    Entity e = new Entity(HasBytesJPA.class.getSimpleName());
1704    e.setProperty("onePrimByte", 8L);
1705    e.setProperty("primBytes", new ShortBlob("short blob".getBytes()));
1706    ds.put(e);
1707    Query
1708        q =
1709        em.createQuery("select from " + HasBytesJPA.class.getName() + " c" + " where primBytes = :p1");
1710    q.setParameter("p1", "short blob".getBytes());
1711    List<HasBytesJPA> result = (List<HasBytesJPA>) q.getResultList();
1712    assertEquals(1, result.size());
1713  }
1714
1715  public void testFilterByByteArray() {
1716    Entity e = new Entity(HasBytesJPA.class.getSimpleName());
1717    e.setProperty("onePrimByte", 8L);
1718    e.setProperty("bytes", new ShortBlob("short blob".getBytes()));
1719    ds.put(e);
1720    Query q = em.createQuery("select from " + HasBytesJPA.class.getName() + " c" + " where bytes = :p1");
1721    q.setParameter("p1", PrimitiveArrays.asList("short blob".getBytes()).toArray(new Byte[0]));
1722    List<HasBytesJPA> result = (List<HasBytesJPA>) q.getResultList();
1723    assertEquals(1, result.size());
1724  }
1725
1726  public void testAliasedFilter() {
1727    Entity bookEntity = Book.newBookEntity("Joe Blow", "67890", "Bar Book");
1728    ds.put(bookEntity);
1729
1730    javax.persistence.Query q = em.createQuery(
1731        "select from " + Book.class.getName() + " b where b.id = :key");
1732    q.setParameter("key", KeyFactory.keyToString(bookEntity.getKey()));
1733    @SuppressWarnings("unchecked")
1734    List<Book> books = (List<Book>) q.getResultList();
1735    assertEquals(1, books.size());
1736    assertEquals(bookEntity.getKey(), KeyFactory.stringToKey(books.get(0).getId()));
1737  }
1738
1739  public void testAliasedSort() {
1740    Entity bookEntity1 = Book.newBookEntity("Joe Blow", "67891", "Bar Book");
1741    Entity bookEntity2 = Book.newBookEntity("Joe Blow", "67890", "Bar Book");
1742    ds.put(bookEntity1);
1743    ds.put(bookEntity2);
1744
1745    javax.persistence.Query q = em.createQuery(
1746        "select from " + Book.class.getName() + " b order by b.isbn");
1747    @SuppressWarnings("unchecked")
1748    List<Book> books = (List<Book>) q.getResultList();
1749    assertEquals(2, books.size());
1750    assertEquals(bookEntity2.getKey(), KeyFactory.stringToKey(books.get(0).getId()));
1751    assertEquals(bookEntity1.getKey(), KeyFactory.stringToKey(books.get(1).getId()));
1752  }
1753
1754  public void testAliasedEmbeddedFilter() {
1755    Entity entity = new Entity(Person.class.getSimpleName());
1756    entity.setProperty("first", "max");
1757    entity.setProperty("last", "ross");
1758    entity.setProperty("anotherFirst", "notmax");
1759    entity.setProperty("anotherLast", "notross");
1760    ds.put(entity);
1761
1762    Query q = em.createQuery(
1763        "select from " + Person.class.getName() + " p where p.name.first = \"max\"");
1764    @SuppressWarnings("unchecked")
1765    List<Person> result = (List<Person>) q.getResultList();
1766    assertEquals(1, result.size());
1767  }
1768
1769  public void testAliasedEmbeddedSort() {
1770    Entity entity1 = new Entity(Person.class.getSimpleName());
1771    entity1.setProperty("first", "max");
1772    entity1.setProperty("last", "ross");
1773    entity1.setProperty("anotherFirst", "notmax2");
1774    entity1.setProperty("anotherLast", "notross");
1775    ds.put(entity1);
1776    Entity entity2 = new Entity(Person.class.getSimpleName());
1777    entity2.setProperty("first", "max");
1778    entity2.setProperty("last", "ross");
1779    entity2.setProperty("anotherFirst", "notmax1");
1780    entity2.setProperty("anotherLast", "notross");
1781    ds.put(entity2);
1782
1783    Query q = em.createQuery(
1784        "select from " + Person.class.getName() + " p order by p.anotherName.first");
1785    @SuppressWarnings("unchecked")
1786    List<Person> result = (List<Person>) q.getResultList();
1787    assertEquals(2, result.size());
1788    assertEquals(entity2.getKey(), TestUtils.createKey(Person.class, result.get(0).getId()));
1789    assertEquals(entity1.getKey(), TestUtils.createKey(Person.class, result.get(1).getId()));
1790  }
1791
1792  public void testFilterByNullValue_Literal() {
1793    Entity e = new Entity(NullDataJPA.class.getSimpleName());
1794    e.setProperty("string", null);
1795    ds.put(e);
1796
1797    Query q = em.createQuery("select from " + NullDataJPA.class.getName() + " c where string = null");
1798    @SuppressWarnings("unchecked")
1799    List<NullDataJPA> results = (List<NullDataJPA>) q.getResultList();
1800    assertEquals(1, results.size());
1801  }
1802
1803  public void testFilterByNullValue_Param() {
1804    Entity e = new Entity(NullDataJPA.class.getSimpleName());
1805    e.setProperty("string", null);
1806    ds.put(e);
1807
1808    Query q = em.createQuery("select from " + NullDataJPA.class.getName() + " c where string = :p");
1809    q.setParameter("p", null);
1810    @SuppressWarnings("unchecked")
1811    List<NullDataJPA> results = (List<NullDataJPA>) q.getResultList();
1812    assertEquals(1, results.size());
1813  }
1814
1815  public void testQueryForOneToManySetWithKeyPk() {
1816    Entity e = new Entity(HasOneToManyKeyPkSetJPA.class.getSimpleName());
1817    ds.put(e);
1818
1819    beginTxn();
1820    Query q = em.createQuery("select from " + HasOneToManyKeyPkSetJPA.class.getName() + " c");
1821    @SuppressWarnings("unchecked")
1822    List<HasOneToManyKeyPkSetJPA> results = q.getResultList();
1823    assertEquals(1, results.size());
1824    assertEquals(0, results.get(0).getBooks().size());
1825    commitTxn();
1826  }
1827
1828  public void testQueryForOneToManyListWithKeyPk() {
1829    Entity e = new Entity(HasOneToManyKeyPkListJPA.class.getSimpleName());
1830    ds.put(e);
1831
1832    beginTxn();
1833    Query q = em.createQuery("select from " + HasOneToManyKeyPkListJPA.class.getName() + " c");
1834    @SuppressWarnings("unchecked")
1835    List<HasOneToManyKeyPkListJPA> results = q.getResultList();
1836    assertEquals(1, results.size());
1837    assertEquals(0, results.get(0).getBooks().size());
1838    commitTxn();
1839  }
1840
1841  public void testQueryForOneToManySetWithLongPk() {
1842    Entity e = new Entity(HasOneToManyLongPkSetJPA.class.getSimpleName());
1843    ds.put(e);
1844
1845    beginTxn();
1846    Query q = em.createQuery("select from " + HasOneToManyLongPkSetJPA.class.getName() + " c");
1847    @SuppressWarnings("unchecked")
1848    List<HasOneToManyLongPkSetJPA> results = q.getResultList();
1849    assertEquals(1, results.size());
1850    assertEquals(0, results.get(0).getBooks().size());
1851    commitTxn();
1852  }
1853
1854  public void testQueryForOneToManyListWithLongPk() {
1855    Entity e = new Entity(HasOneToManyLongPkListJPA.class.getSimpleName());
1856    ds.put(e);
1857
1858    beginTxn();
1859    Query q = em.createQuery("select from " + HasOneToManyLongPkListJPA.class.getName() + " c");
1860    @SuppressWarnings("unchecked")
1861    List<HasOneToManyLongPkListJPA> results = q.getResultList();
1862    assertEquals(1, results.size());
1863    assertEquals(0, results.get(0).getBooks().size());
1864    commitTxn();
1865  }
1866
1867  public void testQueryForOneToManySetWithUnencodedStringPk() {
1868    Entity e = new Entity(HasOneToManyUnencodedStringPkSetJPA.class.getSimpleName(), "yar");
1869    ds.put(e);
1870
1871    beginTxn();
1872    Query q = em.createQuery("select from " + HasOneToManyUnencodedStringPkSetJPA.class.getName() + " c");
1873    @SuppressWarnings("unchecked")
1874    List<HasOneToManyUnencodedStringPkSetJPA> results = q.getResultList();
1875    assertEquals(1, results.size());
1876    assertEquals(0, results.get(0).getBooks().size());
1877    commitTxn();
1878  }
1879
1880  public void testQueryForOneToManyListWithUnencodedStringPk() {
1881    Entity e = new Entity(HasOneToManyUnencodedStringPkListJPA.class.getSimpleName(), "yar");
1882    ds.put(e);
1883
1884    beginTxn();
1885    Query q = em.createQuery("select from " + HasOneToManyUnencodedStringPkListJPA.class.getName() + " c ");
1886    @SuppressWarnings("unchecked")
1887    List<HasOneToManyUnencodedStringPkListJPA> results = q.getResultList();
1888    assertEquals(1, results.size());
1889    assertEquals(0, results.get(0).getBooks().size());
1890    commitTxn();
1891  }
1892
1893  public void testBatchGet_NoTxn() {
1894    switchDatasource(EntityManagerFactoryName.nontransactional_ds_non_transactional_ops_allowed);
1895    Entity e1 = Book.newBookEntity("auth", "123432", "title", -40);
1896    ds.put(e1);
1897    Entity e2 = Book.newBookEntity("auth", "123432", "title", -40);
1898    ds.put(e2);
1899    Entity e3 = Book.newBookEntity("auth", "123432", "title", -40);
1900    ds.put(e3);
1901
1902    Key key = KeyFactory.createKey("yar", "does not exist");
1903    NoQueryDelegate nqd = new NoQueryDelegate().install();
1904    try {
1905      Query q = em.createQuery("select from " + Book.class.getName() + " c" + " where id = :ids");
1906      q.setParameter("ids", Utils.newArrayList(key, e1.getKey(), e2.getKey()));
1907      @SuppressWarnings("unchecked")
1908      List<Book> books = (List<Book>) q.getResultList();
1909      assertEquals(2, books.size());
1910      assertEquals(e1.getKey(), KeyFactory.stringToKey(books.get(0).getId()));
1911      assertEquals(e2.getKey(), KeyFactory.stringToKey(books.get(1).getId()));
1912    } finally {
1913      nqd.uninstall();
1914    }
1915  }
1916
1917  public void testBatchGet_NoTxn_EncodedStringParam() {
1918    switchDatasource(EntityManagerFactoryName.nontransactional_ds_non_transactional_ops_allowed);
1919    Entity e1 = Book.newBookEntity("auth", "123432", "title", -40);
1920    ds.put(e1);
1921    Entity e2 = Book.newBookEntity("auth", "123432", "title", -40);
1922    ds.put(e2);
1923    Entity e3 = Book.newBookEntity("auth", "123432", "title", -40);
1924    ds.put(e3);
1925
1926    Key key = KeyFactory.createKey("yar", "does not exist");
1927    NoQueryDelegate nqd = new NoQueryDelegate().install();
1928    try {
1929      Query q = em.createQuery("select from " + Book.class.getName() + " c" + " where id = :ids");
1930      q.setParameter("ids", Utils.newArrayList(
1931          KeyFactory.keyToString(key),
1932          KeyFactory.keyToString(e1.getKey()),
1933          KeyFactory.keyToString(e2.getKey())));
1934      @SuppressWarnings("unchecked")
1935      List<Book> books = (List<Book>) q.getResultList();
1936      assertEquals(2, books.size());
1937      assertEquals(e1.getKey(), KeyFactory.stringToKey(books.get(0).getId()));
1938      assertEquals(e2.getKey(), KeyFactory.stringToKey(books.get(1).getId()));
1939    } finally {
1940      nqd.uninstall();
1941    }
1942  }
1943
1944  public void testBatchGet_NoTxn_In() {
1945    switchDatasource(EntityManagerFactoryName.nontransactional_ds_non_transactional_ops_allowed);
1946    Entity e1 = Book.newBookEntity("auth", "123432", "title", -40);
1947    ds.put(e1);
1948    Entity e2 = Book.newBookEntity("auth", "123432", "title", -40);
1949    ds.put(e2);
1950    Entity e3 = Book.newBookEntity("auth", "123432", "title", -40);
1951    ds.put(e3);
1952
1953    NoQueryDelegate nqd = new NoQueryDelegate().install();
1954    try {
1955      Key key = KeyFactory.createKey("yar", "does not exist");
1956      Query q = em.createQuery("select from " + Book.class.getName() + " c" + " where id IN (:ids)");
1957      q.setParameter("ids", Utils.newArrayList(key, e1.getKey(), e2.getKey()));
1958      @SuppressWarnings("unchecked")
1959      List<Book> books = (List<Book>) q.getResultList();
1960      assertEquals(2, books.size());
1961      assertEquals(e1.getKey(), KeyFactory.stringToKey(books.get(0).getId()));
1962      assertEquals(e2.getKey(), KeyFactory.stringToKey(books.get(1).getId()));
1963
1964      q = em.createQuery("select from " + Book.class.getName() + " c" + " where id IN (:id1, :id2, :id3)");
1965      q.setParameter("id1", key);
1966      q.setParameter("id2", e1.getKey());
1967      q.setParameter("id3", e2.getKey());
1968      books = (List<Book>) q.getResultList();
1969      assertEquals(2, books.size());
1970      assertEquals(e1.getKey(), KeyFactory.stringToKey(books.get(0).getId()));
1971      assertEquals(e2.getKey(), KeyFactory.stringToKey(books.get(1).getId()));
1972
1973      q = em.createQuery("select from " + Book.class.getName() + " c" + " where "
1974                         + "id IN (:id1, :id3) OR id IN (:id2)");
1975      q.setParameter("id1", key);
1976      q.setParameter("id2", e1.getKey());
1977      q.setParameter("id3", e2.getKey());
1978      books = (List<Book>) q.getResultList();
1979      assertEquals(2, books.size());
1980      assertEquals(e1.getKey(), KeyFactory.stringToKey(books.get(1).getId()));
1981      assertEquals(e2.getKey(), KeyFactory.stringToKey(books.get(0).getId()));
1982    } finally {
1983      nqd.uninstall();
1984    }
1985  }
1986
1987  public void testBatchGet_Count_NoTxn() {
1988    switchDatasource(EntityManagerFactoryName.nontransactional_ds_non_transactional_ops_allowed);
1989    Entity e1 = Book.newBookEntity("auth", "123432", "title", -40);
1990    ds.put(e1);
1991    Entity e2 = Book.newBookEntity("auth", "123432", "title", -40);
1992    ds.put(e2);
1993    Entity e3 = Book.newBookEntity("auth", "123432", "title", -40);
1994    ds.put(e3);
1995
1996    NoQueryDelegate nqd = new NoQueryDelegate().install();
1997    try {
1998      Key key = KeyFactory.createKey("yar", "does not exist");
1999      Query q = em.createQuery("select count(id) from " + Book.class.getName() + " c" + " where id = :ids");
2000      q.setParameter("ids", Utils.newArrayList(key, e1.getKey(), e2.getKey()));
2001      long count = (Long) q.getSingleResult();
2002      assertEquals(2l, count);
2003    } finally {
2004      nqd.uninstall();
2005    }
2006  }
2007
2008  public void testBatchGet_Count_NoTxn_In() {
2009    switchDatasource(EntityManagerFactoryName.nontransactional_ds_non_transactional_ops_allowed);
2010    Entity e1 = Book.newBookEntity("auth", "123432", "title", -40);
2011    ds.put(e1);
2012    Entity e2 = Book.newBookEntity("auth", "123432", "title", -40);
2013    ds.put(e2);
2014    Entity e3 = Book.newBookEntity("auth", "123432", "title", -40);
2015    ds.put(e3);
2016
2017    NoQueryDelegate nqd = new NoQueryDelegate().install();
2018    try {
2019      Key key = KeyFactory.createKey("yar", "does not exist");
2020      Query q = em.createQuery("select count(id) from " + Book.class.getName() + " c" + " where id IN (:ids)");
2021      q.setParameter("ids", Utils.newArrayList(key, e1.getKey(), e2.getKey()));
2022      long count = (Long) q.getSingleResult();
2023      assertEquals(2l, count);
2024
2025      q = em.createQuery("select count(id) from " + Book.class.getName() + " c" + " where id IN (:id1, :id2, :id3)");
2026      q.setParameter("id1", key);
2027      q.setParameter("id2", e1.getKey());
2028      q.setParameter("id3", e2.getKey());
2029      count = (Long) q.getSingleResult();
2030      assertEquals(2l, count);
2031    } finally {
2032      nqd.uninstall();
2033    }
2034  }
2035
2036  public void testBatchGet_Txn() {
2037    Entity e1 = Book.newBookEntity("auth", "123432", "title", -40);
2038    ds.put(e1);
2039    Entity e2 = Book.newBookEntity(e1.getKey(), "auth", "123432", "title", -40);
2040    ds.put(e2);
2041    Entity e3 = Book.newBookEntity("auth", "123432", "title", -40);
2042    ds.put(e3);
2043
2044    NoQueryDelegate nqd = new NoQueryDelegate().install();
2045    try {
2046      Key key = KeyFactory.createKey(e1.getKey(), "yar", "does not exist");
2047      Query q = em.createQuery("select from " + Book.class.getName() + " c" + " where id = :ids");
2048      q.setParameter("ids", Utils.newArrayList(key, e1.getKey(), e2.getKey()));
2049      @SuppressWarnings("unchecked")
2050      List<Book> books = (List<Book>) q.getResultList();
2051      assertEquals(2, books.size());
2052      assertEquals(e1.getKey(), KeyFactory.stringToKey(books.get(0).getId()));
2053      assertEquals(e2.getKey(), KeyFactory.stringToKey(books.get(1).getId()));
2054    } finally {
2055      nqd.uninstall();
2056    }
2057  }
2058
2059  public void testBatchGet_Txn_In() {
2060    Entity e1 = Book.newBookEntity("auth", "123432", "title", -40);
2061    ds.put(e1);
2062    Entity e2 = Book.newBookEntity(e1.getKey(), "auth", "123432", "title", -40);
2063    ds.put(e2);
2064    Entity e3 = Book.newBookEntity("auth", "123432", "title", -40);
2065    ds.put(e3);
2066
2067    NoQueryDelegate nqd = new NoQueryDelegate().install();
2068    try {
2069      Key key = KeyFactory.createKey(e1.getKey(), "yar", "does not exist");
2070      Query q = em.createQuery("select from " + Book.class.getName() + " c" + " where id IN (:ids)");
2071      q.setParameter("ids", Utils.newArrayList(key, e1.getKey(), e2.getKey()));
2072      @SuppressWarnings("unchecked")
2073      List<Book> books = (List<Book>) q.getResultList();
2074      assertEquals(2, books.size());
2075      assertEquals(e1.getKey(), KeyFactory.stringToKey(books.get(0).getId()));
2076      assertEquals(e2.getKey(), KeyFactory.stringToKey(books.get(1).getId()));
2077
2078      q = em.createQuery("select from " + Book.class.getName() + " c" + " where id IN (:id1, :id2, :id3)");
2079      q.setParameter("id1", key);
2080      q.setParameter("id2", e1.getKey());
2081      q.setParameter("id3", e2.getKey());
2082      books = (List<Book>) q.getResultList();
2083      assertEquals(2, books.size());
2084      assertEquals(e1.getKey(), KeyFactory.stringToKey(books.get(0).getId()));
2085      assertEquals(e2.getKey(), KeyFactory.stringToKey(books.get(1).getId()));
2086    } finally {
2087      nqd.uninstall();
2088    }
2089  }
2090
2091  public void testBatchGet_Illegal() {
2092    switchDatasource(EntityManagerFactoryName.nontransactional_ds_non_transactional_ops_allowed);
2093    Query q = em.createQuery("select from " + Flight.class.getName() + " c" + " where origin = :ids");
2094    q.setParameter("ids", Utils.newArrayList());
2095    q.setHint(DatastoreManager.QUERYEXT_INMEMORY_WHEN_UNSUPPORTED, "false");
2096    try {
2097      q.getResultList();
2098      fail("expected exception");
2099    } catch (PersistenceException e) {
2100      // good
2101    }
2102    q = em.createQuery(
2103        "select from " + Flight.class.getName() + " c" + " where id = :ids and origin = :origin");
2104    q.setParameter("ids", Utils.newArrayList());
2105    q.setParameter("origin", "bos");
2106    q.setHint(DatastoreManager.QUERYEXT_INMEMORY_WHEN_UNSUPPORTED, "false");
2107    try {
2108      q.getResultList();
2109      fail("expected exception");
2110    } catch (PersistenceException e) {
2111      // good
2112    }
2113    q = em.createQuery(
2114        "select from " + Flight.class.getName() + " c" + " where origin = :origin and id = :ids");
2115    q.setParameter("origin", "bos");
2116    q.setParameter("ids", Utils.newArrayList());
2117    q.setHint(DatastoreManager.QUERYEXT_INMEMORY_WHEN_UNSUPPORTED, "false");
2118    try {
2119      q.getResultList();
2120      fail("expected exception");
2121    } catch (PersistenceException e) {
2122      // good
2123    }
2124    q = em.createQuery("select from " + Flight.class.getName() + " c" + " where id > :ids");
2125    q.setParameter("ids", Utils.newArrayList());
2126    q.setHint(DatastoreManager.QUERYEXT_INMEMORY_WHEN_UNSUPPORTED, "false");
2127    try {
2128      q.getResultList();
2129      fail("expected exception");
2130    } catch (PersistenceException e) {
2131      // good
2132    }
2133    q = em.createQuery("select from " + Flight.class.getName() + " c" + " where id = :ids order by id");
2134    q.setParameter("ids", Utils.newArrayList());
2135    q.setHint(DatastoreManager.QUERYEXT_INMEMORY_WHEN_UNSUPPORTED, "false");
2136    try {
2137      q.getResultList();
2138      fail("expected exception");
2139    } catch (PersistenceException e) {
2140      // good
2141    }
2142  }
2143
2144  public void testNamedQuery() {
2145    Query q = em.createNamedQuery("namedQuery");
2146    assertTrue(q.getResultList().isEmpty());
2147    Entity e = Book.newBookEntity("author", "12345", "yam");
2148    ds.put(e);
2149    Book b = (Book) q.getSingleResult();
2150    assertEquals(e.getKey(), KeyFactory.stringToKey(b.getId()));
2151  }
2152
2153  public void testRestrictFetchedFields_UnknownField() {
2154    Query q = em.createQuery("select dne from " + Book.class.getName());
2155    q.setHint(DatastoreManager.QUERYEXT_INMEMORY_WHEN_UNSUPPORTED, "false");
2156    try {
2157      q.getResultList();
2158      fail("expected exception");
2159    } catch (PersistenceException e) {
2160      // good
2161    }
2162  }
2163
2164  public void testRestrictFetchedFields_OneField() {
2165    Entity e1 = Book.newBookEntity("author", "12345", "the title");
2166    ds.put(e1);
2167    Query q = em.createQuery("select title from " + Book.class.getName() + " c");
2168    @SuppressWarnings("unchecked")
2169    List<String> titles = (List<String>) q.getResultList();
2170    assertEquals(1, titles.size());
2171    assertEquals("the title", titles.get(0));
2172
2173    Entity e2 = Book.newBookEntity("another author", "123456", "the other title");
2174    ds.put(e2);
2175
2176    @SuppressWarnings("unchecked")
2177    List<String> titles2 = (List<String>) q.getResultList();
2178    assertEquals(2, titles2.size());
2179    assertEquals("the title", titles2.get(0));
2180    assertEquals("the other title", titles2.get(1));
2181  }
2182
2183  public void testRestrictFetchedFields_OneIdField() {
2184    Entity e1 = Book.newBookEntity("author", "12345", "the title");
2185    ds.put(e1);
2186
2187    Entity e2 = Book.newBookEntity("another author", "123456", "the other title");
2188    ds.put(e2);
2189
2190    // Remove this blocker since the test needs to update!
2191    DatastoreServiceInterceptor.uninstall();
2192
2193    beginTxn();
2194    Query q = em.createQuery("select id from " + Book.class.getName() + " c");
2195    @SuppressWarnings("unchecked")
2196    List<String> ids = (List<String>) q.getResultList();
2197    assertEquals(2, ids.size());
2198    assertEquals(KeyFactory.keyToString(e1.getKey()), ids.get(0));
2199    assertEquals(KeyFactory.keyToString(e2.getKey()), ids.get(1));
2200
2201    Book b = em.find(Book.class, e1.getKey());
2202    assertEquals("author", b.getAuthor());
2203    b.setAuthor("not author");
2204    commitTxn();
2205    beginTxn();
2206    b = em.find(Book.class, e1.getKey());
2207    assertEquals("not author", b.getAuthor());
2208    commitTxn();
2209  }
2210
2211  public void testRestrictFetchedFields_TwoFields() {
2212    Entity e1 = Book.newBookEntity("author", "12345", "the title");
2213    ds.put(e1);
2214    Query q = em.createQuery("select author, isbn from " + Book.class.getName() + " c");
2215    @SuppressWarnings("unchecked")
2216    List<Object[]> results = (List<Object[]>) q.getResultList();
2217    assertEquals(1, results.size());
2218    assertEquals(2, results.get(0).length);
2219    assertEquals("author", results.get(0)[0]);
2220    assertEquals("12345", results.get(0)[1]);
2221
2222    Entity e2 = Book.newBookEntity("another author", null, "the other title");
2223    ds.put(e2);
2224
2225    @SuppressWarnings("unchecked")
2226    List<Object[]> results2 = (List<Object[]>) q.getResultList();
2227    assertEquals(2, results2.size());
2228    assertEquals(2, results2.get(0).length);
2229    assertEquals("author", results2.get(0)[0]);
2230    assertEquals("12345", results2.get(0)[1]);
2231    assertEquals(2, results2.get(0).length);
2232    assertEquals("another author", results2.get(1)[0]);
2233    assertNull(results2.get(1)[1]);
2234  }
2235
2236  public void testRestrictFetchedFields_TwoIdFields() {
2237    Entity e1 = Book.newBookEntity("author", "12345", "the title");
2238    ds.put(e1);
2239    Query q = em.createQuery("select id, id from " + Book.class.getName() + " c");
2240    @SuppressWarnings("unchecked")
2241    List<Object[]> results = (List<Object[]>) q.getResultList();
2242    assertEquals(1, results.size());
2243    assertEquals(2, results.get(0).length);
2244    assertEquals(KeyFactory.keyToString(e1.getKey()), results.get(0)[0]);
2245    assertEquals(KeyFactory.keyToString(e1.getKey()), results.get(0)[1]);
2246
2247    Entity e2 = Book.newBookEntity("another author", null, "the other title");
2248    ds.put(e2);
2249
2250    @SuppressWarnings("unchecked")
2251    List<Object[]> results2 = (List<Object[]>) q.getResultList();
2252    assertEquals(2, results2.size());
2253    assertEquals(2, results2.get(0).length);
2254    assertEquals(KeyFactory.keyToString(e1.getKey()), results2.get(0)[0]);
2255    assertEquals(KeyFactory.keyToString(e1.getKey()), results2.get(0)[1]);
2256    assertEquals(2, results2.get(0).length);
2257    assertEquals(KeyFactory.keyToString(e2.getKey()), results2.get(1)[0]);
2258    assertEquals(KeyFactory.keyToString(e2.getKey()), results2.get(1)[1]);
2259  }
2260
2261  public void testRestrictFetchedFields_TwoIdFields_IdIsFirst() {
2262    Entity e1 = Book.newBookEntity("author", "12345", "the title");
2263    ds.put(e1);
2264    Query q = em.createQuery("select id, author from " + Book.class.getName() + " c");
2265    @SuppressWarnings("unchecked")
2266    List<Object[]> results = (List<Object[]>) q.getResultList();
2267    assertEquals(1, results.size());
2268    assertEquals(2, results.get(0).length);
2269    assertEquals(KeyFactory.keyToString(e1.getKey()), results.get(0)[0]);
2270    assertEquals("author", results.get(0)[1]);
2271
2272    Entity e2 = Book.newBookEntity("another author", null, "the other title");
2273    ds.put(e2);
2274
2275    @SuppressWarnings("unchecked")
2276    List<Object[]> results2 = (List<Object[]>) q.getResultList();
2277    assertEquals(2, results2.size());
2278    assertEquals(2, results2.get(0).length);
2279    assertEquals(KeyFactory.keyToString(e1.getKey()), results2.get(0)[0]);
2280    assertEquals("author", results2.get(0)[1]);
2281    assertEquals(2, results2.get(0).length);
2282    assertEquals(KeyFactory.keyToString(e2.getKey()), results2.get(1)[0]);
2283    assertEquals("another author", results2.get(1)[1]);
2284  }
2285
2286  public void testRestrictFetchedFields_TwoIdFields_IdIsSecond() {
2287    Entity e1 = Book.newBookEntity("author", "12345", "the title");
2288    ds.put(e1);
2289    Query q = em.createQuery("select author, id from " + Book.class.getName() + " c");
2290    @SuppressWarnings("unchecked")
2291    List<Object[]> results = (List<Object[]>) q.getResultList();
2292    assertEquals(1, results.size());
2293    assertEquals(2, results.get(0).length);
2294    assertEquals("author", results.get(0)[0]);
2295    assertEquals(KeyFactory.keyToString(e1.getKey()), results.get(0)[1]);
2296
2297    Entity e2 = Book.newBookEntity("another author", null, "the other title");
2298    ds.put(e2);
2299
2300    @SuppressWarnings("unchecked")
2301    List<Object[]> results2 = (List<Object[]>) q.getResultList();
2302    assertEquals(2, results2.size());
2303    assertEquals(2, results2.get(0).length);
2304    assertEquals("author", results2.get(0)[0]);
2305    assertEquals(KeyFactory.keyToString(e1.getKey()), results2.get(0)[1]);
2306    assertEquals(2, results2.get(0).length);
2307    assertEquals("another author", results2.get(1)[0]);
2308    assertEquals(KeyFactory.keyToString(e2.getKey()), results2.get(1)[1]);
2309  }
2310
2311  public void testRestrictFetchedFields_OneToOne() {
2312    Entity e1 = new Entity(HasOneToOneJPA.class.getSimpleName());
2313    ds.put(e1);
2314    Entity e2 = Book.newBookEntity(e1.getKey(), "author", "12345", "the title");
2315    ds.put(e2);
2316    Query q = em.createQuery("select id, book from " + HasOneToOneJPA.class.getName() + " c");
2317    @SuppressWarnings("unchecked")
2318    List<Object[]> results = (List<Object[]>) q.getResultList();
2319    assertEquals(1, results.size());
2320    assertEquals(2, results.get(0).length);
2321    assertEquals(KeyFactory.keyToString(e1.getKey()), results.get(0)[0]);
2322    Book b = em.find(Book.class, e2.getKey());
2323    assertEquals(b, results.get(0)[1]);
2324  }
2325
2326  public void testRestrictFetchedFields_OneToMany() {
2327    Entity e1 = new Entity(HasOneToManyListJPA.class.getSimpleName());
2328    ds.put(e1);
2329
2330    Entity e2 = Book.newBookEntity(e1.getKey(), "author", "12345", "the title");
2331    ds.put(e2);
2332
2333    e1.setProperty("books", Utils.newArrayList(e2.getKey()));
2334    ds.put(e1);
2335
2336    Query q = em.createQuery("select id, books from " + HasOneToManyListJPA.class.getName() + " c");
2337    @SuppressWarnings("unchecked")
2338    List<Object[]> results = (List<Object[]>) q.getResultList();
2339    assertEquals(1, results.size());
2340    assertEquals(2, results.get(0).length);
2341    assertEquals(KeyFactory.keyToString(e1.getKey()), results.get(0)[0]);
2342    Book b = em.find(Book.class, e2.getKey());
2343    List<Book> books = (List<Book>) results.get(0)[1];
2344    assertEquals(1, books.size());
2345    assertEquals(b, books.get(0));
2346  }
2347
2348  public void testRestrictFetchedFields_AliasedField() {
2349    Entity e1 = Book.newBookEntity("author", "12345", "the title");
2350    ds.put(e1);
2351    Query q = em.createQuery("select b.isbn from " + Book.class.getName() + " b");
2352    @SuppressWarnings("unchecked")
2353    List<String> isbns = (List<String>) q.getResultList();
2354    assertEquals(1, isbns.size());
2355    assertEquals("12345", isbns.get(0));
2356  }
2357
2358  public void testRestrictFetchedFields_EmbeddedField() {
2359    Entity entity = new Entity(Person.class.getSimpleName());
2360    entity.setProperty("first", "max");
2361    entity.setProperty("last", "ross");
2362    entity.setProperty("anotherFirst", "notmax");
2363    entity.setProperty("anotherLast", "notross");
2364    ds.put(entity);
2365
2366    Query q = em.createQuery("select name.first, anotherName.last from " + Person.class.getName() + " c");
2367    @SuppressWarnings("unchecked")
2368    List<Object[]> result = (List<Object[]>) q.getResultList();
2369    assertEquals(1, result.size());
2370  }
2371
2372  public void testIsNull() {
2373    Entity e = Book.newBookEntity("author", null, "title");
2374    ds.put(e);
2375    Query q = em.createQuery("select from " + Book.class.getName() + " c" + " where isbn is NULL");
2376    @SuppressWarnings("unchecked")
2377    List<Book> books = q.getResultList();
2378    assertEquals(1, books.size());
2379  }
2380
2381  public void testIsNotNull() {
2382    Entity e = Book.newBookEntity("auth", "isbn", null);
2383    ds.put(e);
2384    Query q = em.createQuery("select from " + Book.class.getName() + " c" + " where title is not null");
2385    assertTrue(q.getResultList().isEmpty());
2386    Query q2 = em.createQuery("select from " + Book.class.getName() + " c" + " where title <> null");
2387    assertTrue(q2.getResultList().isEmpty());
2388    e = Book.newBookEntity("auth2", "isbn2", "not null");
2389    ds.put(e);
2390    Book b = (Book) q.getSingleResult();
2391    assertEquals("not null", b.getTitle());
2392    b = (Book) q2.getSingleResult();
2393    assertEquals("not null", b.getTitle());
2394  }
2395
2396  public void testIsNotNull_Param() {
2397    Entity e = Book.newBookEntity("auth", "isbn", null);
2398    ds.put(e);
2399    Query q = em.createQuery("select from " + Book.class.getName() + " c" + " where title <> :p");
2400    q.setParameter("p", null);
2401    assertTrue(q.getResultList().isEmpty());
2402    e = Book.newBookEntity("auth2", "isbn2", "not null");
2403    ds.put(e);
2404    Book b = (Book) q.getSingleResult();
2405    assertEquals("not null", b.getTitle());
2406  }
2407
2408  public void testNotEqual() {
2409    Entity e = Book.newBookEntity("auth", "isbn", "yar");
2410    ds.put(e);
2411    Query q = em.createQuery("select from " + Book.class.getName() + " c" + " where title <> 'yar'");
2412    assertTrue(q.getResultList().isEmpty());
2413    e = Book.newBookEntity("auth2", "isbn2", "not yar");
2414    ds.put(e);
2415    Book b = (Book) q.getSingleResult();
2416    assertEquals("not yar", b.getTitle());
2417  }
2418
2419  public void testNotEqual_Param() {
2420    Entity e = Book.newBookEntity("auth", "isbn", "yar");
2421    ds.put(e);
2422    Query q = em.createQuery("select from " + Book.class.getName() + " c" + " where title <> :p");
2423    q.setParameter("p", "yar");
2424    assertTrue(q.getResultList().isEmpty());
2425    e = Book.newBookEntity("auth2", "isbn2", "not yar");
2426    ds.put(e);
2427    Book b = (Book) q.getSingleResult();
2428    assertEquals("not yar", b.getTitle());
2429  }
2430
2431  public void testIn_Literals() {
2432    Entity e = Book.newBookEntity("auth1", "isbn1", "yar1");
2433    Entity e2 = Book.newBookEntity("auth2", "isbn2", null);
2434    Entity e3 = Book.newBookEntity("auth3", "isbn3", "yar3");
2435    ds.put(Arrays.asList(e, e2, e3));
2436    Query q = em.createQuery("select from " + Book.class.getName() + " c" + " where author IN ('auth1', 'auth3')");
2437    List<Book> books = q.getResultList();
2438    assertEquals(2, books.size());
2439    assertEquals(KeyFactory.keyToString(e.getKey()), books.get(0).getId());
2440    assertEquals(KeyFactory.keyToString(e3.getKey()), books.get(1).getId());
2441
2442    q = em.createQuery("select from " + Book.class.getName() + " c" + " where title IN (null, 'yar1')");
2443    books = q.getResultList();
2444    assertEquals(2, books.size());
2445    assertEquals(KeyFactory.keyToString(e2.getKey()), books.get(0).getId());
2446    assertEquals(KeyFactory.keyToString(e.getKey()), books.get(1).getId());
2447  }
2448
2449  public void testIn_Params() {
2450    Entity e = Book.newBookEntity("auth1", "isbn1", "yar1");
2451    Entity e2 = Book.newBookEntity("auth2", "isbn2", "yar2");
2452    Entity e3 = Book.newBookEntity("auth3", "isbn3", "yar3");
2453    ds.put(Arrays.asList(e, e2, e3));
2454    Query q = em.createQuery("select from " + Book.class.getName() + " c" + " where author IN (:p1, :p2)");
2455    q.setParameter("p1", "auth1");
2456    q.setParameter("p2", "auth3");
2457    List<Book> books = q.getResultList();
2458    assertEquals(2, books.size());
2459    assertEquals(KeyFactory.keyToString(e.getKey()), books.get(0).getId());
2460    assertEquals(KeyFactory.keyToString(e3.getKey()), books.get(1).getId());
2461
2462    q = em.createQuery("select from " + Book.class.getName() + " c" +
2463                       " where author IN (:p1) OR author IN (:p2)");
2464    q.setParameter("p1", "auth1");
2465    q.setParameter("p2", "auth3");
2466    books = q.getResultList();
2467    assertEquals(2, books.size());
2468    assertEquals(KeyFactory.keyToString(e.getKey()), books.get(0).getId());
2469    assertEquals(KeyFactory.keyToString(e3.getKey()), books.get(1).getId());
2470  }
2471
2472  public void testIn_CollectionParam() {
2473    Entity e = Book.newBookEntity("auth1", "isbn1", "yar1");
2474    Entity e2 = Book.newBookEntity("auth2", "isbn2", "yar2");
2475    Entity e3 = Book.newBookEntity("auth3", "isbn3", "yar3");
2476    ds.put(Arrays.asList(e, e2, e3));
2477    Query q = em.createQuery("select from " + Book.class.getName() + " c" + " where author IN (:p1)");
2478    q.setParameter("p1", Arrays.asList("auth1", "auth3"));
2479    List<Book> books = q.getResultList();
2480    assertEquals(2, books.size());
2481    assertEquals(KeyFactory.keyToString(e.getKey()), books.get(0).getId());
2482    assertEquals(KeyFactory.keyToString(e3.getKey()), books.get(1).getId());
2483
2484    q = em.createQuery("select from " + Book.class.getName() + " c" +
2485                       " where author IN (:p1) OR author IN (:p2)");
2486    q.setParameter("p1", Arrays.asList("auth1"));
2487    q.setParameter("p2", Arrays.asList("auth3"));
2488    books = q.getResultList();
2489    assertEquals(2, books.size());
2490    assertEquals(KeyFactory.keyToString(e.getKey()), books.get(0).getId());
2491    assertEquals(KeyFactory.keyToString(e3.getKey()), books.get(1).getId());
2492  }
2493
2494  public void testMultipleIn_Literals() {
2495    Entity e = Book.newBookEntity("auth1", "isbn1", "yar1");
2496    Entity e2 = Book.newBookEntity("auth2", "isbn2", "yar2");
2497    Entity e3 = Book.newBookEntity("auth3", "isbn3", "yar3");
2498    ds.put(Arrays.asList(e, e2, e3));
2499    Query q = em.createQuery("select from " + Book.class.getName() + " c" + " where "
2500                             + "author IN ('auth1', 'auth3') AND isbn IN ('isbn3', 'isbn2')");
2501    List<Book> books = q.getResultList();
2502    assertEquals(1, books.size());
2503    assertEquals(KeyFactory.keyToString(e3.getKey()), books.get(0).getId());
2504
2505    q = em.createQuery("select from " + Book.class.getName() + " c" + " where "
2506                             + "author IN ('auth1') OR author IN ('auth4', 'auth2')");
2507    books = q.getResultList();
2508    assertEquals(2, books.size());
2509    assertEquals(KeyFactory.keyToString(e.getKey()), books.get(0).getId());
2510    assertEquals(KeyFactory.keyToString(e2.getKey()), books.get(1).getId());
2511  }
2512
2513  public void testMultipleIn_Params() {
2514    Entity e = Book.newBookEntity("auth1", "isbn1", "yar1");
2515    Entity e2 = Book.newBookEntity("auth2", "isbn2", "yar2");
2516    Entity e3 = Book.newBookEntity("auth3", "isbn3", "yar3");
2517    ds.put(Arrays.asList(e, e2, e3));
2518    Query q = em.createQuery("select from " + Book.class.getName() + " c" + " where "
2519                             + "author IN (:p1, :p2) AND isbn IN (:p3, :p4)");
2520    q.setParameter("p1", "auth1");
2521    q.setParameter("p2", "auth3");
2522    q.setParameter("p3", "isbn3");
2523    q.setParameter("p4", "isbn2");
2524    List<Book> books = q.getResultList();
2525    assertEquals(1, books.size());
2526    assertEquals(KeyFactory.keyToString(e3.getKey()), books.get(0).getId());
2527
2528    q = em.createQuery("select from " + Book.class.getName() + " c" + " where "
2529                             + "author IN (:p1, :p2) OR author IN (:p3, :p4)");
2530    q.setParameter("p1", "auth1");
2531    q.setParameter("p2", "auth3");
2532    q.setParameter("p3", "auth4");
2533    q.setParameter("p4", "auth5");
2534    books = q.getResultList();
2535    assertEquals(2, books.size());
2536    assertEquals(KeyFactory.keyToString(e.getKey()), books.get(0).getId());
2537    assertEquals(KeyFactory.keyToString(e3.getKey()), books.get(1).getId());
2538  }
2539
2540  public void testMultipleIn_Params_KeyFilter() {
2541    Entity e = Book.newBookEntity("auth1", "isbn1", "yar1");
2542    Entity e2 = Book.newBookEntity("auth2", "isbn2", "yar2");
2543    Entity e3 = Book.newBookEntity("auth3", "isbn3", "yar3");
2544    ds.put(Arrays.asList(e, e2, e3));
2545    Query q = em.createQuery("select from " + Book.class.getName() + " c" + " where "
2546                             + "id IN (:p1) AND isbn IN (:p2, :p3)");
2547    q.setParameter("p1", e2.getKey());
2548    q.setParameter("p2", "isbn2");
2549    q.setParameter("p3", "isbn3");
2550    @SuppressWarnings("unchecked")
2551    List<Book> books = (List<Book>) q.getResultList();
2552    assertEquals(1, books.size());
2553    assertEquals(KeyFactory.keyToString(e2.getKey()), books.get(0).getId());
2554
2555    q = em.createQuery("select from " + Book.class.getName() + " c" + " where "
2556                             + "(id = :p1 or id = :p2) AND isbn IN (:p3, :p4)");
2557    q.setParameter("p1", e2.getKey());
2558    q.setParameter("p2", e3.getKey());
2559    q.setParameter("p3", "isbn2");
2560    q.setParameter("p4", "isbn3");
2561    @SuppressWarnings("unchecked")
2562    List<Book> books2 = (List<Book>) q.getResultList();
2563    assertEquals(2, books2.size());
2564  }
2565
2566  public void testOr_Literals() {
2567    Entity e = Book.newBookEntity("auth1", "isbn1", "yar1");
2568    Entity e2 = Book.newBookEntity("auth2", "isbn2", null);
2569    Entity e3 = Book.newBookEntity("auth3", "isbn3", "yar3");
2570    ds.put(Arrays.asList(e, e2, e3));
2571    Query q = em.createQuery("select from " + Book.class.getName() + " c" +
2572                             " where author = 'auth1' or author = 'auth3'");
2573    List<Book> books = q.getResultList();
2574    assertEquals(2, books.size());
2575    assertEquals(KeyFactory.keyToString(e.getKey()), books.get(0).getId());
2576    assertEquals(KeyFactory.keyToString(e3.getKey()), books.get(1).getId());
2577
2578    q = em.createQuery("select from " + Book.class.getName() + " c" +
2579                       " where title is null or title = 'yar1'");
2580    books = q.getResultList();
2581    assertEquals(2, books.size());
2582    assertEquals(KeyFactory.keyToString(e2.getKey()), books.get(0).getId());
2583    assertEquals(KeyFactory.keyToString(e.getKey()), books.get(1).getId());
2584  }
2585
2586  public void testOr_Params() {
2587    Entity e = Book.newBookEntity("auth1", "isbn1", "yar1");
2588    Entity e2 = Book.newBookEntity("auth2", "isbn2", "yar2");
2589    Entity e3 = Book.newBookEntity("auth3", "isbn3", "yar3");
2590    ds.put(Arrays.asList(e, e2, e3));
2591    Query q = em.createQuery("select from " + Book.class.getName() + " c" +
2592                             " where author = :p1 or author = :p2");
2593    q.setParameter("p1", "auth1");
2594    q.setParameter("p2", "auth3");
2595    List<Book> books = q.getResultList();
2596    assertEquals(2, books.size());
2597    assertEquals(KeyFactory.keyToString(e.getKey()), books.get(0).getId());
2598    assertEquals(KeyFactory.keyToString(e3.getKey()), books.get(1).getId());
2599  }
2600
2601  public void testMultipleOr_Literals() {
2602    Entity e = Book.newBookEntity("auth1", "isbn1", "yar1");
2603    Entity e2 = Book.newBookEntity("auth2", "isbn2", "yar2");
2604    Entity e3 = Book.newBookEntity("auth3", "isbn3", "yar3");
2605    ds.put(Arrays.asList(e, e2, e3));
2606    Query q = em.createQuery("select from " + Book.class.getName() + " c" + " where "
2607                             + "(author  = 'auth1' or author = 'auth3') AND "
2608                             + "(isbn = 'isbn3' or isbn = 'isbn2')");
2609    List<Book> books = q.getResultList();
2610    assertEquals(1, books.size());
2611    assertEquals(KeyFactory.keyToString(e3.getKey()), books.get(0).getId());
2612  }
2613
2614  public void testMultipleOr_Params() {
2615    Entity e = Book.newBookEntity("auth1", "isbn1", "yar1");
2616    Entity e2 = Book.newBookEntity("auth2", "isbn2", "yar2");
2617    Entity e3 = Book.newBookEntity("auth3", "isbn3", "yar3");
2618    ds.put(Arrays.asList(e, e2, e3));
2619    Query q = em.createQuery("select from " + Book.class.getName() + " c" + " where "
2620                             + "(author = :p1 or author = :p2) AND "
2621                             + "(isbn = :p3 or isbn = :p4)");
2622    q.setParameter("p1", "auth1");
2623    q.setParameter("p2", "auth3");
2624    q.setParameter("p3", "isbn3");
2625    q.setParameter("p4", "isbn2");
2626    List<Book> books = q.getResultList();
2627    assertEquals(1, books.size());
2628    assertEquals(KeyFactory.keyToString(e3.getKey()), books.get(0).getId());
2629  }
2630
2631  public void testIsNullChild() {
2632    Entity e = new Entity(HasOneToOneJPA.class.getSimpleName());
2633    ds.put(e);
2634    Query q = em.createQuery(
2635        "select from " + HasOneToOneJPA.class.getName() + " c" + " where book is null");
2636    q.setHint(DatastoreManager.QUERYEXT_INMEMORY_WHEN_UNSUPPORTED, "false");
2637    try {
2638      q.getResultList();
2639      fail("expected");
2640    } catch (PersistenceException pe) {
2641      // good
2642    }
2643  }
2644
2645  public void testIsNullParent() {
2646    Entity e = new Entity(HasOneToOneJPA.class.getSimpleName());
2647    Key key = ds.put(e);
2648    e = new Entity(HasOneToOneParentJPA.class.getSimpleName(), key);
2649    ds.put(e);
2650    Query q = em.createQuery(
2651        "select from " + HasOneToOneParentJPA.class.getName() + " c" + " where parent is null");
2652    q.setHint(DatastoreManager.QUERYEXT_INMEMORY_WHEN_UNSUPPORTED, "false");
2653    try {
2654      q.getResultList();
2655      fail("expected");
2656    } catch (PersistenceException pe) {
2657      // good
2658    }
2659  }
2660
2661  public void testQueryWithEntityShortName() {
2662    // Uninstall this since the test needs to update!
2663    DatastoreServiceInterceptor.uninstall();
2664
2665    Query q = em.createQuery("select b from bookalias b where title = 'yam'");
2666    assertTrue(q.getResultList().isEmpty());
2667
2668    Book b = new Book();
2669    b.setTitle("yam");
2670    beginTxn();
2671    em.persist(b);
2672    commitTxn();
2673
2674    assertEquals(b, q.getResultList().get(0));
2675
2676    q = em.createQuery("select from bookalias b where title = 'yam'");
2677    assertEquals(b, q.getResultList().get(0));
2678
2679    q = em.createQuery("select from bookalias b where b.title = 'yam'");
2680    assertEquals(b, q.getResultList().get(0));
2681  }
2682
2683  public void testQueryWithSingleCharacterLiteral() {
2684    Query q = em.createQuery("select from " + Book.class.getName() + " c" + " where title = 'y'");
2685    assertTrue(q.getResultList().isEmpty());
2686
2687    Entity e = Book.newBookEntity("author", "12345", "y");
2688    ds.put(e);
2689    Book b = (Book) q.getSingleResult();
2690    assertEquals(e.getKey(), KeyFactory.stringToKey(b.getId()));
2691  }
2692
2693  public void testSetFirstResultAfterSetParameter() {
2694    Entity e = Book.newBookEntity("author", "12345", "y");
2695    ds.put(e);
2696    e = Book.newBookEntity("author", "12345", "y");
2697    ds.put(e);
2698
2699    Query query = em.createQuery("SELECT b FROM " + Book.class.getName() + " b WHERE b.title = :title");
2700    query.setParameter("title", "y");
2701    query.setFirstResult(1);
2702    Book b = (Book) query.getSingleResult();
2703    assertEquals(e.getKey(), KeyFactory.stringToKey(b.getId()));
2704  }
2705
2706  public void testSetMaxResultsAfterSetParameter() {
2707    Entity e = Book.newBookEntity("author", "12345", "y");
2708    ds.put(e);
2709    Entity e2 = Book.newBookEntity("author", "12345", "y");
2710    ds.put(e2);
2711
2712    Query query = em.createQuery("SELECT b FROM " + Book.class.getName() + " b WHERE b.title = :title");
2713    query.setParameter("title", "y");
2714    query.setMaxResults(1);
2715    Book b = (Book) query.getSingleResult();
2716    assertEquals(e.getKey(), KeyFactory.stringToKey(b.getId()));
2717  }
2718
2719  public void testSetFirstResultAfterSetPCParameter() {
2720    Entity e = new Entity(HasOneToOneJPA.class.getSimpleName());
2721    Key key = ds.put(e);
2722    e = new Entity(HasOneToOneParentJPA.class.getSimpleName(), key);
2723    ds.put(e);
2724    e = new Entity(HasOneToOneParentJPA.class.getSimpleName(), key);
2725    ds.put(e);
2726    HasOneToOneJPA parent = em.find(HasOneToOneJPA.class, key);
2727    Query q = em.createQuery(
2728        "select from " + HasOneToOneParentJPA.class.getName() + " c" + " where parent = :p");
2729    q.setParameter("p", parent);
2730    q.setFirstResult(1);
2731    HasOneToOneParentJPA child = (HasOneToOneParentJPA) q.getSingleResult();
2732    assertEquals(e.getKey(), KeyFactory.stringToKey(child.getId()));
2733  }
2734
2735  public void testSetMaxResultsAfterSetPCParameter() {
2736    Entity e = new Entity(HasOneToOneJPA.class.getSimpleName());
2737    Key key = ds.put(e);
2738    Entity child1 = new Entity(HasOneToOneParentJPA.class.getSimpleName(), key);
2739    ds.put(child1);
2740    Entity child2 = new Entity(HasOneToOneParentJPA.class.getSimpleName(), key);
2741    ds.put(child2);
2742    HasOneToOneJPA parent = em.find(HasOneToOneJPA.class, key);
2743    Query q = em.createQuery(
2744        "select from " + HasOneToOneParentJPA.class.getName() + " c" + " where parent = :p");
2745    q.setParameter("p", parent);
2746    q.setMaxResults(1);
2747    HasOneToOneParentJPA child = (HasOneToOneParentJPA) q.getSingleResult();
2748    assertEquals(child1.getKey(), KeyFactory.stringToKey(child.getId()));
2749  }
2750
2751  public void testAccessResultsAfterClose() {
2752    for (int i = 0; i < 3; i++) {
2753      Entity e = Book.newBookEntity("this", "that", "the other");
2754      ds.put(e);
2755    }
2756    beginTxn();
2757    Query q = em.createQuery("select from " + Book.class.getName() + " c");
2758    List<Book> results = q.getResultList();
2759    Iterator<Book> iter = results.iterator();
2760    iter.next();
2761    commitTxn();
2762    em.close();
2763    Book b = iter.next();
2764    b.getIsbn();
2765    b.getAuthor();
2766    iter.next();
2767  }
2768
2769  public void testAncestorQueryForDifferentEntityGroupWithCurrentTxn() {
2770    switchDatasource(EntityManagerFactoryName.transactional_ds_non_transactional_ops_allowed);
2771    Entity e1 = Book.newBookEntity("this", "that", "the other");
2772    ds.put(e1);
2773
2774    beginTxn();
2775    // Not used, but associates the txn with the book's entity group
2776    /*Book b = */em.find(Book.class, e1.getKey());
2777    Query q = em.createQuery(
2778        "select from " + HasKeyAncestorStringPkJPA.class.getName() + " c" + " where ancestorKey = :p");
2779    q.setParameter("p", KeyFactory.keyToString(KeyFactory.createKey("yar", 33L)));
2780    try {
2781      q.getResultList().iterator();
2782      fail("expected iae");
2783    } catch (PersistenceException e) {
2784      // good
2785    }
2786
2787    q.setHint("gae.exclude-query-from-txn", false);
2788    try {
2789      q.getResultList().iterator();
2790      fail("expected exception");
2791    } catch (PersistenceException e) {
2792      // good
2793    }
2794    q.setHint("gae.exclude-query-from-txn", true);
2795    q.getResultList();
2796  }
2797
2798  public void testLikeQuery_Literal() {
2799    Entity e1 = Book.newBookEntity("this", "that", "xxxx");
2800    ds.put(e1);
2801    Entity e2 = Book.newBookEntity("this", "that", "y");
2802    ds.put(e2);
2803    Entity e3 = Book.newBookEntity("this", "that", "yb");
2804    ds.put(e3);
2805    Entity e4 = Book.newBookEntity("this", "that", "z");
2806    ds.put(e4);
2807
2808    Query q = em.createQuery("select from " + Book.class.getName() + " c" + " where title LIKE 'y%'");
2809    @SuppressWarnings("unchecked")
2810    List<Book> result = q.getResultList();
2811
2812    assertEquals(2, result.size());
2813    assertEquals(KeyFactory.keyToString(e2.getKey()), result.get(0).getId());
2814    assertEquals(KeyFactory.keyToString(e3.getKey()), result.get(1).getId());
2815
2816    q = em.createQuery("select from " + Book.class.getName() + " c" + " where title LIKE 'z%'");
2817    @SuppressWarnings("unchecked")
2818    List<Book> result2 = q.getResultList();
2819
2820    assertEquals(1, result2.size());
2821    assertEquals(KeyFactory.keyToString(e4.getKey()), result2.get(0).getId());
2822
2823    q = em.createQuery("select from " + Book.class.getName() + " c" + " where title LIKE 'za%'");
2824    @SuppressWarnings("unchecked")
2825    List<Book> result3 = q.getResultList();
2826    assertTrue(result3.isEmpty());
2827  }
2828
2829  public void testLikeQuery_Literal2() {
2830    for (int i = 0; i < 10; i++) {
2831      Entity e1 = Book.newBookEntity("this", "that", "xxxx");
2832      ds.put(e1);
2833    }
2834
2835    Query q = em.createQuery("select from " + Book.class.getName() + " c" + " where title LIKE 'x%'");
2836    @SuppressWarnings("unchecked")
2837    List<Book> result = q.getResultList();
2838
2839    assertEquals(10, result.size());
2840  }
2841
2842  public void testLikeQuery_Param() {
2843    Entity e1 = Book.newBookEntity("this", "that", "xxxx");
2844    ds.put(e1);
2845    Entity e2 = Book.newBookEntity("this", "that", "y");
2846    ds.put(e2);
2847    Entity e3 = Book.newBookEntity("this", "that", "yb");
2848    ds.put(e3);
2849    Entity e4 = Book.newBookEntity("this", "that", "z");
2850    ds.put(e4);
2851
2852    Query q = em.createQuery("select from " + Book.class.getName() + " c" + " where title LIKE :p");
2853    q.setParameter("p", "y%");
2854
2855    @SuppressWarnings("unchecked")
2856    List<Book> result = q.getResultList();
2857
2858    assertEquals(2, result.size());
2859    assertEquals(KeyFactory.keyToString(e2.getKey()), result.get(0).getId());
2860    assertEquals(KeyFactory.keyToString(e3.getKey()), result.get(1).getId());
2861
2862    q = em.createQuery("select from " + Book.class.getName() + " c" + " where title LIKE 'z%'");
2863    q.setParameter("p", "y%");
2864    @SuppressWarnings("unchecked")
2865    List<Book> result2 = q.getResultList();
2866
2867    assertEquals(1, result2.size());
2868    assertEquals(KeyFactory.keyToString(e4.getKey()), result2.get(0).getId());
2869
2870    q = em.createQuery("select from " + Book.class.getName() + " c" + " where title LIKE 'za%'");
2871    q.setParameter("p", "y%");
2872    @SuppressWarnings("unchecked")
2873    List<Book> result3 = q.getResultList();
2874    assertTrue(result3.isEmpty());
2875  }
2876
2877  public void testLikeQuery_InvalidLiteral() {
2878    Query q = em.createQuery("select from " + Book.class.getName() + " c" + " where title LIKE '%y'");
2879    q.setHint(DatastoreManager.QUERYEXT_INMEMORY_WHEN_UNSUPPORTED, "false");
2880    try {
2881      q.getResultList();
2882      fail("expected exception");
2883    } catch (PersistenceException pe) {
2884        if (pe.getCause() instanceof DatastoreQuery.UnsupportedDatastoreFeatureException) {
2885            // good
2886        }
2887        else {
2888          throw pe;
2889        }
2890    }
2891
2892    q = em.createQuery("select from " + Book.class.getName() + " c" + " where title LIKE 'y%y'");
2893    q.setHint(DatastoreManager.QUERYEXT_INMEMORY_WHEN_UNSUPPORTED, "false");
2894    try {
2895      q.getResultList();
2896      fail("expected exception");
2897    } catch (PersistenceException pe) {
2898        if (pe.getCause() instanceof DatastoreQuery.UnsupportedDatastoreFeatureException) {
2899            // good
2900        }
2901        else {
2902          throw pe;
2903        }
2904    }
2905
2906    q = em.createQuery("select from " + Book.class.getName() + " c" + " where title LIKE 'y'");
2907    q.setHint(DatastoreManager.QUERYEXT_INMEMORY_WHEN_UNSUPPORTED, "false");
2908    try {
2909      q.getResultList();
2910      fail("expected exception");
2911    } catch (PersistenceException pe) {
2912        if (pe.getCause() instanceof DatastoreQuery.UnsupportedDatastoreFeatureException) {
2913            // good
2914        }
2915        else {
2916          throw pe;
2917        }
2918    }
2919
2920    q = em.createQuery("select from " + Book.class.getName() + " c" + " where title LIKE 'y%' and author LIKE 'z%'");
2921    q.setHint(DatastoreManager.QUERYEXT_INMEMORY_WHEN_UNSUPPORTED, "false");
2922    try {
2923      q.getResultList().iterator();
2924      fail("expected exception");
2925    } catch (PersistenceException pe) {
2926      // good
2927    }
2928  }
2929
2930  public void testLikeQuery_InvalidParameter() {
2931    Query q = em.createQuery("select from " + Book.class.getName() + " c" + " where title LIKE :p");
2932    q.setParameter("p", "%y");
2933    q.setHint(DatastoreManager.QUERYEXT_INMEMORY_WHEN_UNSUPPORTED, "false");
2934    try {
2935      q.getResultList().iterator();
2936      fail("expected exception");
2937    } catch (PersistenceException pe) {
2938        if (pe.getCause() instanceof DatastoreQuery.UnsupportedDatastoreFeatureException) {
2939            // good
2940        }
2941        else {
2942          throw pe;
2943        }
2944    }
2945
2946    q.setParameter("p", "y%y");
2947    try {
2948      q.getResultList().iterator();
2949      fail("expected exception");
2950    } catch (PersistenceException pe) {
2951        if (pe.getCause() instanceof DatastoreQuery.UnsupportedDatastoreFeatureException) {
2952            // good
2953        }
2954        else {
2955          throw pe;
2956        }
2957    }
2958
2959    q.setParameter("p", "y");
2960    try {
2961      q.getResultList().iterator();
2962      fail("expected exception");
2963    } catch (PersistenceException pe) {
2964        if (pe.getCause() instanceof DatastoreQuery.UnsupportedDatastoreFeatureException) {
2965            // good
2966        }
2967        else {
2968          throw pe;
2969        }
2970    }
2971
2972    q.setParameter("p", 23);
2973    try {
2974      q.getResultList().iterator();
2975      fail("expected exception");
2976    } catch (PersistenceException pe) {
2977      // good
2978    }
2979
2980    q = em.createQuery("select from " + Book.class.getName() + " c" + " where title LIKE :p and author LIKE :q");
2981    q.setParameter("p", "y%");
2982    q.setParameter("q", "y%");
2983    try {
2984      q.getResultList().iterator();
2985      fail("expected exception");
2986    } catch (PersistenceException pe) {
2987      // good
2988    }
2989  }
2990
2991  public void testLikeQuery_Literal_CustomEscapeChar() {
2992    Entity e1 = Book.newBookEntity("this", "that", "xxxx");
2993    ds.put(e1);
2994    Entity e2 = Book.newBookEntity("this", "that", "%");
2995    ds.put(e2);
2996    Entity e3 = Book.newBookEntity("this", "that", "%a");
2997    ds.put(e3);
2998    Entity e4 = Book.newBookEntity("this", "that", "z");
2999    ds.put(e4);
3000
3001    Query q = em.createQuery("select from " + Book.class.getName() + " c" + " where title LIKE '%^' ESCAPE '^'");
3002    q.setHint(DatastoreManager.QUERYEXT_INMEMORY_WHEN_UNSUPPORTED, "false");
3003    try {
3004      q.getResultList();
3005      fail("Expected exception but query executed!");
3006    } catch (PersistenceException pe) {
3007        if (pe.getCause() instanceof DatastoreQuery.UnsupportedDatastoreFeatureException) {
3008          // We don't support ESCAPE yet
3009        } else {
3010          throw pe;
3011        }
3012    } catch (DatastoreQuery.UnsupportedDatastoreFeatureException uefe) {
3013    }
3014
3015//    assertEquals(2, result.size());
3016//    assertEquals(KeyFactory.keyToString(e2.getKey()), result.get(0).getId());
3017//    assertEquals(KeyFactory.keyToString(e3.getKey()), result.get(1).getId());
3018//
3019//    q = em.createQuery("select from " + Book.class.getName() + " where title LIKE 'a^' ESCAPE '^'");
3020//    @SuppressWarnings("unchecked")
3021//    List<Book> result3 = q.getResultList();
3022//    assertTrue(result3.isEmpty());
3023  }
3024
3025  public void testUpdateQueryFails() {
3026    Query q = em.createQuery("update " + Book.class.getName() + " set author = 'yar'");
3027    try {
3028      q.executeUpdate();
3029      fail("expected exception");
3030    } catch (PersistenceException e) {
3031      // good
3032    }
3033  }
3034
3035  public void testNullAncestorParam() {
3036    Query q = em.createQuery(
3037        "select from " + HasKeyAncestorStringPkJPA.class.getName() + " c" + " where ancestorKey = :p");
3038    q.setParameter("p", null);
3039    q.setHint(DatastoreManager.QUERYEXT_INMEMORY_WHEN_UNSUPPORTED, "false");
3040    try {
3041      q.getResultList();
3042      fail("expected exception");
3043    } catch (PersistenceException pe) {
3044        if (pe.getCause() instanceof DatastoreQuery.UnsupportedDatastoreFeatureException) {
3045          // good
3046        }
3047        else {
3048          throw pe;
3049        }
3050    }
3051  }
3052
3053  public void testCurrentDateFuncs() {
3054    Entity e = KitchenSink.newKitchenSinkEntity("blarg", null);
3055    ds.put(e);
3056
3057    for (String dateFunc : Arrays.asList("CURRENT_TIMESTAMP", "CURRENT_DATE")) {
3058      Query q = em.createQuery("select from " + KitchenSink.class.getName() + " c where dateVal < " + dateFunc);
3059      @SuppressWarnings("unchecked")
3060      List<KitchenSink> results = (List<KitchenSink>) q.getResultList();
3061      assertEquals(1, results.size());
3062
3063      DatastoreQuery.NowProvider orig = DatastoreQuery.NOW_PROVIDER;
3064      DatastoreQuery.NOW_PROVIDER = new DatastoreQuery.NowProvider() {
3065        public Date now() {
3066          return new Date(KitchenSink.DATE1.getTime() - 1);
3067        }
3068      };
3069      try {
3070        e.setProperty("dateVal", new Date(KitchenSink.DATE1.getTime() - 1));
3071        assertTrue(q.getResultList().isEmpty());
3072        assertEquals(1, results.size());
3073      } finally {
3074        DatastoreQuery.NOW_PROVIDER = orig;
3075      }
3076    }
3077  }
3078
3079  public void testPositionalParams() {
3080    Entity e = Book.newBookEntity("me", "isbn", "return of yam");
3081    ds.put(e);
3082    List<Book> result =
3083        em.createQuery("select b from " + Book.class.getName() + " b where isbn=?1 AND title=?2")
3084            .setParameter(1, "isbn").setParameter(2, "return of yam").getResultList();
3085    assertEquals(1, result.size());
3086  }
3087
3088  public void testOutOfOrderPositionalParams() {
3089    Entity e = Book.newBookEntity("me", "isbn", "return of yam");
3090    ds.put(e);
3091    List<Book> result =
3092        em.createQuery("select b from " + Book.class.getName() + " b where isbn=?2 AND title=?1")
3093            .setParameter(2, "isbn").setParameter(1, "return of yam").getResultList();
3094    assertEquals(1, result.size());
3095  }
3096
3097  public void testDefaultClassName() {
3098    ds.put(new Entity(DetachableJPA.class.getSimpleName()));
3099    assertEquals(1, em.createQuery("select o from " + DetachableJPA.class.getName() + " o").getResultList().size());
3100    assertEquals(1, em.createQuery("select o from DetachableJPA o").getResultList().size());
3101  }
3102
3103  public void testQueryWithEntityShortName2() {
3104    // Uninstall this since the test needs to update!
3105    DatastoreServiceInterceptor.uninstall();
3106
3107    Query q = em.createQuery("select b from bookalias b where title = 'yam'");
3108    assertTrue(q.getResultList().isEmpty());
3109
3110    Book b = new Book();
3111    b.setTitle("yam");
3112    beginTxn();
3113    em.persist(b);
3114    commitTxn();
3115
3116    assertEquals(b, q.getResultList().get(0));
3117
3118    q = em.createQuery("select from bookalias c where title = 'yam'");
3119    assertEquals(b, q.getResultList().get(0));
3120
3121    q = em.createQuery("select from bookalias b where b.title = 'yam'");
3122    assertEquals(b, q.getResultList().get(0));
3123  }
3124
3125
3126  public void testQueryForClassWithNameStartingWithIn() {
3127    // Uninstall this since the test needs to update!
3128    DatastoreServiceInterceptor.uninstall();
3129
3130    // force the class metadata to load - we've seen occasional problems
3131    // where the class can't be resolved for a query if we don't reference it first.
3132    InTheHouseJPA pojo = new InTheHouseJPA();
3133    beginTxn();
3134    em.persist(pojo);
3135    commitTxn();
3136
3137    assertNotNull(em.createQuery("select b from InTheHouseJPA b").getResultList().get(0));
3138  }
3139
3140  public void testNonexistentClassThrowsReasonableException() {
3141    try {
3142      em.createQuery("select o from xyam o").getResultList();
3143      fail("expected exception");
3144    } catch (PersistenceException e) {
3145      // good
3146    }
3147
3148  }
3149
3150  public void testSubclassesNotSupported() {
3151    JPQLQuery q = new JPQLQuery(getExecutionContext().getStoreManager(), getExecutionContext());
3152    q.setCandidateClass(Base1.class);
3153    q.setSubclasses(false);
3154    try {
3155      q.setSubclasses(true);
3156      fail("expected nue");
3157    } catch (NucleusUserException nue) {
3158      // good
3159    }
3160    q.setCandidateClass(UnidirTop.class);
3161    q.setSubclasses(false);
3162    q.setSubclasses(true);
3163  }
3164
3165  public void testQueryTimeout() {
3166    DatastoreServiceFactoryInternal.setDatastoreService(null);
3167    ApiProxy.ApiConfig config = new ApiProxy.ApiConfig();
3168    config.setDeadlineInSeconds(3.0);
3169    ApiProxy.Delegate delegate = EasyMock.createMock(ApiProxy.Delegate.class);
3170    EasyMock.expect(delegate.makeAsyncCall(EasyMock.isA(ApiProxy.Environment.class),
3171                              EasyMock.eq(LocalDatastoreService.PACKAGE),
3172                              EasyMock.eq("RunQuery"),
3173                              EasyMock.isA(byte[].class),
3174                              ApiConfigMatcher.eqApiConfig(config)))
3175            .andThrow(new DatastoreTimeoutException("too long")).anyTimes();
3176    EasyMock.replay(delegate);
3177    ApiProxy.Delegate original = getDelegateForThread();
3178    setDelegateForThread(delegate);
3179
3180    try {
3181      Query q = em.createQuery("select from " + Book.class.getName() + " c");
3182      q.setHint("javax.persistence.query.timeout", 3000);
3183      try {
3184        q.getResultList().iterator();
3185        fail("expected exception");
3186      } catch (QueryTimeoutException e) {
3187        // good
3188      }
3189    } finally {
3190      setDelegateForThread(original);
3191    }
3192    EasyMock.verify(delegate);
3193  }
3194
3195  public void testQueryTimeoutWhileIterating() {
3196    DatastoreServiceFactoryInternal.setDatastoreService(null);
3197
3198    // Need to have enough data to ensure a Next call
3199    for (int i = 0; i < 21; i++) {
3200      Entity bookEntity = Book.newBookEntity("Joe Blow", "67890", "Bar Book");
3201      ds.put(bookEntity);
3202    }
3203    ExceptionThrowingDatastoreDelegate.ExceptionPolicy policy =
3204        new ExceptionThrowingDatastoreDelegate.BaseExceptionPolicy() {
3205          boolean exploded = false;
3206          protected void doIntercept(String methodName) {
3207            if (!exploded && methodName.equals("Next")) {
3208              exploded = true;
3209              throw new DatastoreTimeoutException("boom: " + methodName);
3210            }
3211          }
3212        };
3213
3214    ApiProxy.Delegate original = getDelegateForThread();
3215    ExceptionThrowingDatastoreDelegate dd =
3216        new ExceptionThrowingDatastoreDelegate(getDelegateForThread(), policy);
3217    setDelegateForThread(dd);
3218
3219    try {
3220      javax.persistence.Query q = em.createQuery(
3221          "select from " + Book.class.getName() + " c");
3222      try {
3223        @SuppressWarnings("unchecked")
3224        List<Book> books = (List<Book>) q.getResultList();
3225        books.size();
3226        fail("expected exception");
3227      } catch (QueryTimeoutException qte) {
3228        assertTrue(qte.getCause() instanceof org.datanucleus.store.query.QueryTimeoutException);
3229        assertTrue(qte.getCause().getCause() instanceof DatastoreTimeoutException);        
3230      }
3231    } finally {
3232      setDelegateForThread(original);
3233    }
3234  }
3235
3236  public void testOverrideReadConsistency() throws ExecutionException, InterruptedException {
3237    DatastoreServiceFactoryInternal.setDatastoreService(null);
3238    ApiProxy.Delegate original = getDelegateForThread();
3239    try {
3240      ApiProxy.Delegate delegate = EasyMock.createMock(ApiProxy.Delegate.class);
3241      Future<DatastorePb.QueryResult> result = EasyMock.createMock(Future.class);
3242      setDelegateForThread(delegate);
3243      ApiProxy.ApiConfig config = new ApiProxy.ApiConfig();
3244      EasyMock.expect(delegate.makeAsyncCall(EasyMock.isA(ApiProxy.Environment.class),
3245                                EasyMock.eq(LocalDatastoreService.PACKAGE),
3246                                EasyMock.eq("RunQuery"),
3247                                FailoverMsMatcher.eqFailoverMs(null),
3248                                ApiConfigMatcher.eqApiConfig(config))).andReturn(result);
3249      EasyMock.expect(result.get()).andReturn(null);
3250      EasyMock.replay(delegate, result);
3251      Query q = em.createQuery("select from " + Book.class.getName() + " c");
3252      q.getResultList().isEmpty();
3253      EasyMock.verify(delegate, result);
3254
3255      delegate = EasyMock.createMock(ApiProxy.Delegate.class);
3256      setDelegateForThread(delegate);
3257      result = EasyMock.createMock(Future.class);
3258      EasyMock.expect(delegate.makeAsyncCall(EasyMock.isA(ApiProxy.Environment.class),
3259                                EasyMock.eq(LocalDatastoreService.PACKAGE),
3260                                EasyMock.eq("RunQuery"),
3261                                FailoverMsMatcher.eqFailoverMs(null),
3262                                ApiConfigMatcher.eqApiConfig(config))).andReturn(result);
3263      EasyMock.expect(result.get()).andReturn(null);
3264      EasyMock.replay(delegate, result);
3265      q = em.createQuery("select from " + Book.class.getName() + " c");
3266      q.setHint("datanucleus.appengine.datastoreReadConsistency", null);
3267      q.getResultList().isEmpty();
3268      EasyMock.verify(delegate);
3269
3270      delegate = EasyMock.createMock(ApiProxy.Delegate.class);
3271      setDelegateForThread(delegate);
3272      result = EasyMock.createMock(Future.class);
3273      EasyMock.expect(delegate.makeAsyncCall(EasyMock.isA(ApiProxy.Environment.class),
3274                                EasyMock.eq(LocalDatastoreService.PACKAGE),
3275                                EasyMock.eq("RunQuery"),
3276                                FailoverMsMatcher.eqFailoverMs(null),
3277                                ApiConfigMatcher.eqApiConfig(config))).andReturn(result);
3278      EasyMock.expect(result.get()).andReturn(null);
3279      EasyMock.replay(delegate, result);
3280      q = em.createQuery("select from " + Book.class.getName() + " c");
3281      q.setHint("datanucleus.appengine.datastoreReadConsistency", "STRONG");
3282      q.getResultList().isEmpty();
3283      EasyMock.verify(delegate, result);
3284
3285      delegate = EasyMock.createMock(ApiProxy.Delegate.class);
3286      setDelegateForThread(delegate);
3287      result = EasyMock.createMock(Future.class);
3288      EasyMock.expect(delegate.makeAsyncCall(EasyMock.isA(ApiProxy.Environment.class),
3289                                EasyMock.eq(LocalDatastoreService.PACKAGE),
3290                                EasyMock.eq("RunQuery"),
3291                                FailoverMsMatcher.eqFailoverMs(-1L),
3292                                ApiConfigMatcher.eqApiConfig(config))).andReturn(result);
3293      EasyMock.expect(result.get()).andReturn(null);
3294      EasyMock.replay(delegate, result);
3295      q = em.createQuery("select from " + Book.class.getName() + " c");
3296      q.setHint("datanucleus.appengine.datastoreReadConsistency", "EVENTUAL");
3297      q.getResultList().isEmpty();
3298      EasyMock.verify(delegate, result);
3299    } finally {
3300      setDelegateForThread(original);
3301    }
3302  }
3303
3304  public void testSetChunkSize() {
3305    DatastoreServiceFactoryInternal.setDatastoreService(null);
3306    ApiProxy.Delegate original = getDelegateForThread();
3307    Future<DatastorePb.QueryResult> result = EasyMock.createNiceMock(Future.class);
3308    try {
3309      ApiProxy.Delegate delegate = EasyMock.createMock(ApiProxy.Delegate.class);
3310      setDelegateForThread(delegate);
3311      ApiProxy.ApiConfig config = new ApiProxy.ApiConfig();
3312      EasyMock.expect(delegate.makeAsyncCall(EasyMock.isA(ApiProxy.Environment.class),
3313                                EasyMock.eq(LocalDatastoreService.PACKAGE),
3314                                EasyMock.eq("RunQuery"),
3315                                ChunkMatcher.eqChunkSize(33),
3316                                ApiConfigMatcher.eqApiConfig(config))).andReturn(result);
3317      EasyMock.replay(delegate, result);
3318      Query q = em.createQuery("select from " + Flight.class.getName() + " c");
3319      q.setHint("datanucleus.query.fetchSize", 33);
3320      q.getResultList();
3321      EasyMock.verify(delegate);
3322    } finally {
3323      setDelegateForThread(original);
3324    }
3325  }
3326
3327
3328  private void assertQueryUnsupportedByDatastore(String query, Class<?> expectedCauseClass) {
3329    Query q = em.createQuery(query);
3330    q.setHint(DatastoreManager.QUERYEXT_INMEMORY_WHEN_UNSUPPORTED, "false");
3331    try {
3332      q.getResultList().iterator();
3333      fail("expected PersistenceException for query <" + query + ">");
3334    } catch (PersistenceException e) {
3335      // good
3336      assertTrue(e.getCause().getClass().getName() + ": " + e.getCause().getMessage(),
3337                 expectedCauseClass.isAssignableFrom(e.getCause().getClass()));
3338    }
3339  }
3340
3341  private void assertQueryUnsupportedByOrm(String query,
3342                                           Expression.Operator unsupportedOp) {
3343    Query q = em.createQuery(query);
3344    q.setHint(DatastoreManager.QUERYEXT_INMEMORY_WHEN_UNSUPPORTED, "false"); // Dont allow in-memory for unsupported syntax
3345    try {
3346      q.getResultList();
3347      fail("expected PersistenceException->UnsupportedOperationException for query <" + query + ">");
3348    } catch (PersistenceException e) {
3349      Throwable cause = e.getCause();
3350      if (cause instanceof DatastoreQuery.UnsupportedDatastoreOperatorException) {
3351        // Good. Expression.Operator doesn't override equals
3352        // so we just compare the string representation. Operator case-insensitive
3353        assertEquals(unsupportedOp.toString().toLowerCase(), ((DatastoreQuery.UnsupportedDatastoreOperatorException)cause).getOperation().toString().toLowerCase());
3354      }
3355      else {
3356        throw e;
3357      }
3358    }
3359  }
3360
3361  private void assertQueryUnsupportedByOrm(String query,
3362                                           Expression.Operator unsupportedOp,
3363                                           Set<Expression.Operator> unsupportedOps) {
3364    assertQueryUnsupportedByOrm(query, unsupportedOp);
3365    unsupportedOps.remove(unsupportedOp);
3366  }
3367
3368  private void assertQueryRequiresUnsupportedDatastoreFeature(String query) {
3369    Query q = em.createQuery(query);
3370    q.setHint(DatastoreManager.QUERYEXT_INMEMORY_WHEN_UNSUPPORTED, "false");
3371    try {
3372      q.getResultList();
3373      fail("expected UnsupportedDatastoreFeatureException for query <" + query + ">");
3374    } catch (PersistenceException pe) {
3375        if (pe.getCause() instanceof DatastoreQuery.UnsupportedDatastoreFeatureException) {
3376          // good
3377        }
3378        else {
3379          throw pe;
3380        }
3381    }
3382  }
3383
3384  private void assertQuerySupported(String query, List<FilterPredicate> addedFilters,
3385                                    List<SortPredicate> addedSorts, Object... nameVals) {
3386    javax.persistence.Query q = em.createQuery(query);
3387    String name = null;
3388    for (Object nameOrVal : nameVals) {
3389      if (name == null) {
3390        name = (String) nameOrVal;
3391      } else {
3392        q.setParameter(name, nameOrVal);
3393        name = null;
3394      }
3395    }
3396    q.getResultList();
3397
3398    assertFilterPredicatesEqual(addedFilters, getFilterPredicates(q));
3399    assertEquals(addedSorts, getSortPredicates(q));
3400  }
3401
3402  // TODO(maxr): Get rid of this when we've fixed the npe in FilterPredicate.equals().
3403  private static void assertFilterPredicatesEqual(
3404      List<FilterPredicate> expected, List<FilterPredicate> actual) {
3405    List<FilterPredicate> expected2 = Utils.newArrayList();
3406    for (FilterPredicate fp : expected) {
3407      if (fp.getValue() == null) {
3408        expected2.add(new FilterPredicate(fp.getPropertyName(), fp.getOperator(), "____null"));
3409      } else {
3410        expected2.add(fp);
3411      }
3412    }
3413    List<FilterPredicate> actual2 = Utils.newArrayList();
3414    for (FilterPredicate fp : actual) {
3415      if (fp.getValue() == null) {
3416        actual2.add(new FilterPredicate(fp.getPropertyName(), fp.getOperator(), "____null"));
3417      } else {
3418        actual2.add(fp);
3419      }
3420    }
3421    assertEquals(expected2, actual2);
3422  }
3423  private DatastoreQuery getDatastoreQuery(javax.persistence.Query q) {
3424    return ((JPQLQuery) ((JPAQuery) q).getInternalQuery()).getDatastoreQuery();
3425  }
3426
3427  private List<FilterPredicate> getFilterPredicates(javax.persistence.Query q) {
3428    return getDatastoreQuery(q).getLatestDatastoreQuery().getFilterPredicates();
3429  }
3430
3431  private List<SortPredicate> getSortPredicates(javax.persistence.Query q) {
3432    return getDatastoreQuery(q).getLatestDatastoreQuery().getSortPredicates();
3433  }
3434}