You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by aa...@apache.org on 2026/09/20 16:45:33 UTC
(cayenne) branch master updated: Proper exists and friends to string conversion
This is an automated email from the ASF dual-hosted git repository.
asf-gitbox-commits 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 8ece7019f Proper exists and friends to string conversion
8ece7019f is described below
commit 8ece7019fedbf79b371f5bb18a1aec9cc4f49db3
Author: Andrus Adamchik <an...@objectstyle.com>
AuthorDate: Sun Sep 20 12:45:26 2026 -0400
Proper exists and friends to string conversion
---
.../java/org/apache/cayenne/exp/parser/ASTAll.java | 12 +++++
.../java/org/apache/cayenne/exp/parser/ASTAny.java | 12 +++++
.../org/apache/cayenne/exp/parser/ASTExists.java | 19 +++++++
.../apache/cayenne/exp/parser/ASTNotExists.java | 20 +++++++
.../org/apache/cayenne/exp/parser/ASTSubquery.java | 3 +-
.../apache/cayenne/exp/parser/ASTExistsTest.java | 17 +++++-
.../cayenne/exp/parser/ASTNotExistsTest.java | 17 +++++-
.../apache/cayenne/exp/parser/ASTSubqueryTest.java | 62 ++++++++++++++++++++++
8 files changed, 159 insertions(+), 3 deletions(-)
diff --git a/cayenne/src/main/java/org/apache/cayenne/exp/parser/ASTAll.java b/cayenne/src/main/java/org/apache/cayenne/exp/parser/ASTAll.java
index 96a58f11c..194c23e65 100644
--- a/cayenne/src/main/java/org/apache/cayenne/exp/parser/ASTAll.java
+++ b/cayenne/src/main/java/org/apache/cayenne/exp/parser/ASTAll.java
@@ -19,6 +19,8 @@
package org.apache.cayenne.exp.parser;
+import java.io.IOException;
+
import org.apache.cayenne.exp.Expression;
public class ASTAll extends ConditionNode {
@@ -46,6 +48,16 @@ public class ASTAll extends ConditionNode {
return null;
}
+ /**
+ * @since 5.0
+ */
+ @Override
+ public void appendAsString(Appendable out) throws IOException {
+ // the operator goes before the single operand, so it is never printed by the superclass
+ out.append("all ");
+ ((SimpleNode) jjtGetChild(0)).appendAsString(out);
+ }
+
@Override
protected String getExpressionOperator(int index) {
return "ALL";
diff --git a/cayenne/src/main/java/org/apache/cayenne/exp/parser/ASTAny.java b/cayenne/src/main/java/org/apache/cayenne/exp/parser/ASTAny.java
index 4ab2f6b98..28075df74 100644
--- a/cayenne/src/main/java/org/apache/cayenne/exp/parser/ASTAny.java
+++ b/cayenne/src/main/java/org/apache/cayenne/exp/parser/ASTAny.java
@@ -19,6 +19,8 @@
package org.apache.cayenne.exp.parser;
+import java.io.IOException;
+
import org.apache.cayenne.exp.Expression;
public class ASTAny extends ConditionNode {
@@ -51,6 +53,16 @@ public class ASTAny extends ConditionNode {
return true;
}
+ /**
+ * @since 5.0
+ */
+ @Override
+ public void appendAsString(Appendable out) throws IOException {
+ // the operator goes before the single operand, so it is never printed by the superclass
+ out.append("any ");
+ ((SimpleNode) jjtGetChild(0)).appendAsString(out);
+ }
+
@Override
protected String getExpressionOperator(int index) {
return "ANY";
diff --git a/cayenne/src/main/java/org/apache/cayenne/exp/parser/ASTExists.java b/cayenne/src/main/java/org/apache/cayenne/exp/parser/ASTExists.java
index ffacd9f7a..e3a6cce5f 100644
--- a/cayenne/src/main/java/org/apache/cayenne/exp/parser/ASTExists.java
+++ b/cayenne/src/main/java/org/apache/cayenne/exp/parser/ASTExists.java
@@ -23,6 +23,7 @@ import org.apache.cayenne.Persistent;
import org.apache.cayenne.exp.Expression;
import org.apache.cayenne.exp.ExpressionFactory;
+import java.io.IOException;
import java.util.Collection;
import java.util.Map;
@@ -78,6 +79,24 @@ public class ASTExists extends ConditionNode {
return Boolean.FALSE;
}
+ /**
+ * @since 5.0
+ */
+ @Override
+ public void appendAsString(Appendable out) throws IOException {
+ // the operator goes before the single operand, so it is never printed by the superclass
+ if (parent != null) {
+ out.append('(');
+ }
+
+ out.append("exists ");
+ ((SimpleNode) jjtGetChild(0)).appendAsString(out);
+
+ if (parent != null) {
+ out.append(')');
+ }
+ }
+
@Override
protected String getExpressionOperator(int index) {
return null;
diff --git a/cayenne/src/main/java/org/apache/cayenne/exp/parser/ASTNotExists.java b/cayenne/src/main/java/org/apache/cayenne/exp/parser/ASTNotExists.java
index 7348d0c14..6a1fbe8ae 100644
--- a/cayenne/src/main/java/org/apache/cayenne/exp/parser/ASTNotExists.java
+++ b/cayenne/src/main/java/org/apache/cayenne/exp/parser/ASTNotExists.java
@@ -19,6 +19,8 @@
package org.apache.cayenne.exp.parser;
+import java.io.IOException;
+
import org.apache.cayenne.exp.Expression;
import org.apache.cayenne.exp.ExpressionFactory;
@@ -55,6 +57,24 @@ public class ASTNotExists extends ConditionNode {
return !ASTExists.notEmpty(o);
}
+ /**
+ * @since 5.0
+ */
+ @Override
+ public void appendAsString(Appendable out) throws IOException {
+ // the operator goes before the single operand, so it is never printed by the superclass
+ if (parent != null) {
+ out.append('(');
+ }
+
+ out.append("not exists ");
+ ((SimpleNode) jjtGetChild(0)).appendAsString(out);
+
+ if (parent != null) {
+ out.append(')');
+ }
+ }
+
@Override
protected String getExpressionOperator(int index) {
return null;
diff --git a/cayenne/src/main/java/org/apache/cayenne/exp/parser/ASTSubquery.java b/cayenne/src/main/java/org/apache/cayenne/exp/parser/ASTSubquery.java
index 94f28f526..bf2aacd7a 100644
--- a/cayenne/src/main/java/org/apache/cayenne/exp/parser/ASTSubquery.java
+++ b/cayenne/src/main/java/org/apache/cayenne/exp/parser/ASTSubquery.java
@@ -91,7 +91,8 @@ public class ASTSubquery extends SimpleNode {
@Override
public void appendAsString(Appendable out) throws IOException {
- out.append("EXISTS");
+ // there is no String form for a query, so this is just a placeholder
+ out.append("(select ...)");
}
public FluentSelect<?, ?> getQuery() {
diff --git a/cayenne/src/test/java/org/apache/cayenne/exp/parser/ASTExistsTest.java b/cayenne/src/test/java/org/apache/cayenne/exp/parser/ASTExistsTest.java
index e9f4a4a73..e9d7a8b5b 100644
--- a/cayenne/src/test/java/org/apache/cayenne/exp/parser/ASTExistsTest.java
+++ b/cayenne/src/test/java/org/apache/cayenne/exp/parser/ASTExistsTest.java
@@ -68,4 +68,19 @@ public class ASTExistsTest {
assertEquals("(length(a) <= 5)", node.jjtGetChild(0).toString());
}
-}
\ No newline at end of file
+ @Test
+ public void toStringRoundTrip() {
+ for (String expString : new String[]{
+ "exists a",
+ "exists a.b.c",
+ "exists db:a.b.c",
+ "exists (a > 5)",
+ "(x = 1) and (exists a)",
+ "(x = 1) or (exists (a > 5))"}) {
+
+ Expression exp = ExpressionFactory.exp(expString);
+ assertEquals(expString, exp.toString());
+ assertEquals(exp, ExpressionFactory.exp(exp.toString()));
+ }
+ }
+}
diff --git a/cayenne/src/test/java/org/apache/cayenne/exp/parser/ASTNotExistsTest.java b/cayenne/src/test/java/org/apache/cayenne/exp/parser/ASTNotExistsTest.java
index 5015c29ce..213a9f226 100644
--- a/cayenne/src/test/java/org/apache/cayenne/exp/parser/ASTNotExistsTest.java
+++ b/cayenne/src/test/java/org/apache/cayenne/exp/parser/ASTNotExistsTest.java
@@ -68,4 +68,19 @@ public class ASTNotExistsTest {
assertEquals("(length(a) <= 5)", node.jjtGetChild(0).toString());
}
-}
\ No newline at end of file
+ @Test
+ public void toStringRoundTrip() {
+ for (String expString : new String[]{
+ "not exists a",
+ "not exists a.b.c",
+ "not exists db:a.b.c",
+ "not exists (a > 5)",
+ "(x = 1) and (not exists a)",
+ "(x = 1) or (not exists (a > 5))"}) {
+
+ Expression exp = ExpressionFactory.exp(expString);
+ assertEquals(expString, exp.toString());
+ assertEquals(exp, ExpressionFactory.exp(exp.toString()));
+ }
+ }
+}
diff --git a/cayenne/src/test/java/org/apache/cayenne/exp/parser/ASTSubqueryTest.java b/cayenne/src/test/java/org/apache/cayenne/exp/parser/ASTSubqueryTest.java
new file mode 100644
index 000000000..bcda6bd23
--- /dev/null
+++ b/cayenne/src/test/java/org/apache/cayenne/exp/parser/ASTSubqueryTest.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
+ *
+ * https://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;
+import org.apache.cayenne.exp.ExpressionFactory;
+import org.apache.cayenne.query.ColumnSelect;
+import org.apache.cayenne.query.ObjectSelect;
+import org.apache.cayenne.testdo.testmap.Artist;
+import org.apache.cayenne.testdo.testmap.Painting;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class ASTSubqueryTest {
+
+ private static final ColumnSelect<String> NAMES = ObjectSelect.columnQuery(Artist.class, Artist.ARTIST_NAME);
+
+ @Test
+ public void existsToString() {
+ Expression exists = ExpressionFactory.exists(ObjectSelect.query(Painting.class));
+ assertEquals("exists (select ...)", exists.toString());
+
+ Expression notExists = ExpressionFactory.notExists(ObjectSelect.query(Painting.class));
+ assertEquals("not exists (select ...)", notExists.toString());
+
+ assertEquals("(a = 1) and (not exists (select ...))",
+ ExpressionFactory.exp("a = 1").andExp(notExists).toString());
+ }
+
+ @Test
+ public void inToString() {
+ assertEquals("a in (select ...)", ExpressionFactory.inExp(ExpressionFactory.pathExp("a"), NAMES).toString());
+ assertEquals("a not in (select ...)",
+ ExpressionFactory.notInExp(ExpressionFactory.pathExp("a"), NAMES).toString());
+ }
+
+ @Test
+ public void allAndAnyToString() {
+ assertEquals("a > all (select ...)",
+ ExpressionFactory.greaterExp("a", ExpressionFactory.all(NAMES)).toString());
+ assertEquals("a = any (select ...)",
+ ExpressionFactory.matchExp("a", ExpressionFactory.any(NAMES)).toString());
+ }
+}