From 45e32de33053f768278a21838bc603e9e7f45ea7 Mon Sep 17 00:00:00 2001 From: Arseni Bulatski Date: Wed, 19 Jun 2019 15:20:52 +0300 Subject: [PATCH] CAY-2541 Performing query with expression with ObjectId throws NPE in some cases --- RELEASE-NOTES.txt | 1 + .../select/QualifierTranslator.java | 51 +++++++---- .../org/apache/cayenne/query/CAY2541IT.java | 84 +++++++++++++++++++ 3 files changed, 119 insertions(+), 17 deletions(-) create mode 100644 cayenne-server/src/test/java/org/apache/cayenne/query/CAY2541IT.java diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index 286e2a7951..1d7ba4e54e 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -15,6 +15,7 @@ Date: Bug Fixes: CAY-2417 Modeler: wrong title in attribute context menu +CAY-2541 Performing query with expression with ObjectId throws NPE in some cases CAY-2553 Wrong disjoint prefetch query qualifier CAY-2573 DI field injection is triggered when creating sql Driver CAY-2580 Cgen: Can't use custom templates for client mode diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/QualifierTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/QualifierTranslator.java index 3795976095..6f5cac97a2 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/QualifierTranslator.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/QualifierTranslator.java @@ -23,6 +23,7 @@ import java.util.Arrays; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.function.Function; import org.apache.cayenne.CayenneRuntimeException; @@ -198,29 +199,43 @@ protected void appendObjectMatch() throws IOException { // superclass's // impl. matchingObject = false; - boolean first = true; + boolean needToProcessLiterals = true; DbRelationship relationship = objectMatchTranslator.getRelationship(); - if (!relationship.isToMany() && !relationship.isToPK()) { + if (relationship != null && !relationship.isToMany() && !relationship.isToPK()) { + needToProcessLiterals = false; queryAssembler.dbRelationshipAdded(relationship, JoinType.INNER, objectMatchTranslator.getJoinSplitAlias()); } - Iterator it = objectMatchTranslator.keys(); - while (it.hasNext()) { - if (first) { - first = false; - } else { - out.append(" AND "); - } + Map attributes = objectMatchTranslator.attributes; + if(attributes != null) { + needToProcessLiterals = false; + Iterator it = objectMatchTranslator.keys(); + while (it.hasNext()) { + if (first) { + first = false; + } else { + out.append(" AND "); + } + + String key = it.next(); + DbAttribute attr = objectMatchTranslator.getAttribute(key); + Object val = objectMatchTranslator.getValue(key); - String key = it.next(); - DbAttribute attr = objectMatchTranslator.getAttribute(key); - Object val = objectMatchTranslator.getValue(key); + processColumn(attr); + out.append(objectMatchTranslator.getOperation()); + appendLiteral(val, attr, objectMatchTranslator.getExpression()); + } + } - processColumn(attr); - out.append(objectMatchTranslator.getOperation()); - appendLiteral(val, attr, objectMatchTranslator.getExpression()); + if(needToProcessLiterals) { + DbAttribute attribute = paramsDbType(objectMatchTranslator.getExpression()); + matchingObject = false; + appendLiteral( + objectMatchTranslator.getValue(attribute.getName()), + attribute, + objectMatchTranslator.getExpression()); } objectMatchTranslator.reset(); @@ -236,8 +251,10 @@ public void finishedChild(Expression node, int childIndex, boolean hasMoreChildr if (!hasMoreChildren) { return; } - - Appendable out = (matchingObject) ? new StringBuilder() : this.out; + boolean hasObjectsToMatch = objectMatchTranslator != null && + (objectMatchTranslator.attributes != null || + objectMatchTranslator.relationship != null); + Appendable out = (matchingObject && hasObjectsToMatch) ? new StringBuilder() : this.out; try { switch (node.getType()) { diff --git a/cayenne-server/src/test/java/org/apache/cayenne/query/CAY2541IT.java b/cayenne-server/src/test/java/org/apache/cayenne/query/CAY2541IT.java new file mode 100644 index 0000000000..f9bdec6759 --- /dev/null +++ b/cayenne-server/src/test/java/org/apache/cayenne/query/CAY2541IT.java @@ -0,0 +1,84 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + ****************************************************************/ +package org.apache.cayenne.query; + +import java.util.List; + +import org.apache.cayenne.ObjectId; +import org.apache.cayenne.access.DataContext; +import org.apache.cayenne.di.Inject; +import org.apache.cayenne.exp.parser.ASTDbPath; +import org.apache.cayenne.exp.parser.ASTEqual; +import org.apache.cayenne.exp.parser.ASTScalar; +import org.apache.cayenne.test.jdbc.DBHelper; +import org.apache.cayenne.test.jdbc.TableHelper; +import org.apache.cayenne.testdo.testmap.Artist; +import org.apache.cayenne.unit.di.server.CayenneProjects; +import org.apache.cayenne.unit.di.server.ServerCase; +import org.apache.cayenne.unit.di.server.UseServerRuntime; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +@UseServerRuntime(CayenneProjects.TESTMAP_PROJECT) +public class CAY2541IT extends ServerCase { + + @Inject + private DataContext context; + + @Inject + private DBHelper dbHelper; + + @Before + public void createArtistsDataSet() throws Exception { + TableHelper tArtist = new TableHelper(dbHelper, "ARTIST"); + tArtist.setColumns("ARTIST_ID", "ARTIST_NAME", "DATE_OF_BIRTH"); + + long dateBase = System.currentTimeMillis(); + for (int i = 1; i <= 20; i++) { + tArtist.insert(i, "artist" + i, new java.sql.Date(dateBase + 10000 * i)); + } + + TableHelper tGallery = new TableHelper(dbHelper, "GALLERY"); + tGallery.setColumns("GALLERY_ID", "GALLERY_NAME"); + tGallery.insert(1, "tate modern"); + + TableHelper tPaintings = new TableHelper(dbHelper, "PAINTING"); + tPaintings.setColumns("PAINTING_ID", "PAINTING_TITLE", "ARTIST_ID", "GALLERY_ID"); + for (int i = 1; i <= 20; i++) { + tPaintings.insert(i, "painting" + i, i % 5 + 1, 1); + } + } + + @Test + public void testCay2541() { + ObjectId id = new ObjectId("Artist", "ARTIST_ID", 1); + ASTDbPath astDbPath = new ASTDbPath("ARTIST_ID"); + ASTScalar astScalar = new ASTScalar(id); + ASTEqual astEqual = new ASTEqual(); + astEqual.setOperand(0, astDbPath); + astEqual.setOperand(1, astScalar); + List artists = ObjectSelect.query(Artist.class) + .where(astEqual) + .select(context); + assertEquals(1, artists.size()); + assertEquals("artist1", artists.get(0).getArtistName()); + } +}