You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by nt...@apache.org on 2019/09/03 15:02:10 UTC

[cayenne] branch master updated: Support for NOT EXISTS subqueries

This is an automated email from the ASF dual-hosted git repository.

ntimofeev pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cayenne.git


The following commit(s) were added to refs/heads/master by this push:
     new 901b10c  Support for NOT EXISTS subqueries
901b10c is described below

commit 901b10c37961cabb93a669b566a7c003c2d517b1
Author: Nikita Timofeev <st...@gmail.com>
AuthorDate: Tue Sep 3 18:02:01 2019 +0300

    Support for NOT EXISTS subqueries
---
 .../translator/select/QualifierTranslator.java     |  4 +-
 .../java/org/apache/cayenne/exp/Expression.java    |  9 +++-
 .../org/apache/cayenne/exp/ExpressionFactory.java  |  9 ++++
 .../apache/cayenne/exp/parser/ASTNotExists.java    | 62 ++++++++++++++++++++++
 .../cayenne/query/ObjectSelect_SubqueryIT.java     | 12 ++++-
 5 files changed, 92 insertions(+), 4 deletions(-)

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 8112ce1..1308091 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
@@ -212,6 +212,8 @@ class QualifierTranslator implements TraversalHandler {
 
             case EXISTS:
                 return new FunctionNode("EXISTS", null, false);
+            case NOT_EXISTS:
+                return new FunctionNode("NOT EXISTS", null, false);
 
             case SUBQUERY:
                 ASTSubquery subquery = (ASTSubquery)node;
@@ -369,7 +371,7 @@ class QualifierTranslator implements TraversalHandler {
             case FUNCTION_CALL: case ADD: case SUBTRACT: case MULTIPLY: case DIVIDE: case NEGATIVE:
             case BITWISE_AND: case BITWISE_LEFT_SHIFT: case BITWISE_OR: case BITWISE_RIGHT_SHIFT: case BITWISE_XOR:
             case OR: case AND: case LESS_THAN: case LESS_THAN_EQUAL_TO: case GREATER_THAN: case GREATER_THAN_EQUAL_TO:
-            case TRUE: case FALSE: case ASTERISK: case EXISTS: case SUBQUERY: case ENCLOSING_OBJECT: case FULL_OBJECT:
+            case TRUE: case FALSE: case ASTERISK: case EXISTS: case NOT_EXISTS: case SUBQUERY: case ENCLOSING_OBJECT: case FULL_OBJECT:
                 return true;
         }
         return false;
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/Expression.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/Expression.java
index 689745f..415659f 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/Expression.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/Expression.java
@@ -177,12 +177,17 @@ public abstract class Expression implements Serializable, XMLSerializable {
 	/**
 	 * @since 4.2
 	 */
-	public static final int SUBQUERY = 50;
+	public static final int NOT_EXISTS = 50;
 
 	/**
 	 * @since 4.2
 	 */
-	public static final int DBID_PATH = 51;
+	public static final int SUBQUERY = 51;
+
+	/**
+	 * @since 4.2
+	 */
+	public static final int DBID_PATH = 52;
 
 	protected int type;
 
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/ExpressionFactory.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/ExpressionFactory.java
index 3eaf00b..7d3b207 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/ExpressionFactory.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/ExpressionFactory.java
@@ -50,6 +50,7 @@ import org.apache.cayenne.exp.parser.ASTNegate;
 import org.apache.cayenne.exp.parser.ASTNot;
 import org.apache.cayenne.exp.parser.ASTNotBetween;
 import org.apache.cayenne.exp.parser.ASTNotEqual;
+import org.apache.cayenne.exp.parser.ASTNotExists;
 import org.apache.cayenne.exp.parser.ASTNotIn;
 import org.apache.cayenne.exp.parser.ASTNotLike;
 import org.apache.cayenne.exp.parser.ASTNotLikeIgnoreCase;
@@ -1441,6 +1442,14 @@ public class ExpressionFactory {
 	}
 
 	/**
+	 * @param subQuery {@link org.apache.cayenne.query.ObjectSelect} or {@link ColumnSelect}
+	 * @since 4.2
+	 */
+	public static Expression notExists(FluentSelect<?> subQuery) {
+		return new ASTNotExists(new ASTSubquery(subQuery));
+	}
+
+	/**
 	 * @since 4.2
 	 */
 	public static Expression inExp(Expression exp, ColumnSelect<?> subQuery) {
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTNotExists.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTNotExists.java
new file mode 100644
index 0000000..61e78ef
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTNotExists.java
@@ -0,0 +1,62 @@
+/*****************************************************************
+ *   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.exp.parser;
+
+import org.apache.cayenne.exp.Expression;
+
+/**
+ * @since 4.2
+ */
+public class ASTNotExists extends ConditionNode {
+
+    public ASTNotExists(ASTSubquery subquery) {
+        super(0);
+        jjtAddChild(subquery, 0);
+    }
+
+    ASTNotExists(int id) {
+        super(id);
+    }
+
+    @Override
+    protected int getRequiredChildrenCount() {
+        return 1;
+    }
+
+    @Override
+    protected Boolean evaluateSubNode(Object o, Object[] evaluatedChildren) throws Exception {
+        return null;
+    }
+
+    @Override
+    protected String getExpressionOperator(int index) {
+        return null;
+    }
+
+    @Override
+    public Expression shallowCopy() {
+        return new ASTNotExists(id);
+    }
+
+    @Override
+    public int getType() {
+        return Expression.NOT_EXISTS;
+    }
+}
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelect_SubqueryIT.java b/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelect_SubqueryIT.java
index 0a02d7d..64904d5 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelect_SubqueryIT.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelect_SubqueryIT.java
@@ -82,7 +82,7 @@ public class ObjectSelect_SubqueryIT extends ServerCase {
 
     @Test
     public void selectQuery_existsWithExpressionFromParentQuery() {
-        Expression exp = Painting.TO_ARTIST.eq(PropertyFactory.createSelf(Artist.class).enclosing())
+        Expression exp = Painting.TO_ARTIST.eq(Artist.ARTIST_ID_PK_PROPERTY.enclosing())
                 .andExp(Painting.PAINTING_TITLE.like("painting%"))
                 .andExp(Artist.ARTIST_NAME.enclosing().like("art%"));
 
@@ -94,6 +94,16 @@ public class ObjectSelect_SubqueryIT extends ServerCase {
     }
 
     @Test
+    public void selectQuery_notExistsWithExpressionFromParentQuery() {
+        ObjectSelect<Painting> subQuery = ObjectSelect.query(Painting.class)
+                .where(Painting.TO_ARTIST.eq(Artist.ARTIST_ID_PK_PROPERTY.enclosing()));
+        long count = ObjectSelect.query(Artist.class)
+                .where(ExpressionFactory.notExists(subQuery))
+                .selectCount(context);
+        assertEquals(15L, count);
+    }
+
+    @Test
     public void selectQuery_twoLevelExists() {
         Expression exp = Painting.PAINTING_TITLE.like("painting%")
                 .andExp(ExpressionFactory.exists(ObjectSelect.query(Gallery.class)));