This is an automated email from the ASF dual-hosted git repository.
spmallette pushed a commit to branch TINKERPOP-2605
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
The following commit(s) were added to refs/heads/TINKERPOP-2605 by this push:
new 84b51d4 TINKERPOP-2598 Addressed NullPointerException with
P.within/out
84b51d4 is described below
commit 84b51d455034dd665a89708e99bdf6a103ea040f
Author: Stephen Mallette <[email protected]>
AuthorDate: Wed Aug 25 10:38:39 2021 -0400
TINKERPOP-2598 Addressed NullPointerException with P.within/out
---
CHANGELOG.asciidoc | 1 +
.../tinkerpop/gremlin/process/traversal/P.java | 20 ++++++++++++-----
.../structure/io/binary/types/PSerializer.java | 2 +-
.../structure/io/gryo/GryoSerializersV1d0.java | 2 +-
.../structure/io/gryo/GryoSerializersV3d0.java | 2 +-
.../tinkerpop/gremlin/process/traversal/PTest.java | 26 ++++++++++++++++++++++
.../graphson/GraphSONMapperEmbeddedTypeTest.java | 4 ++--
.../Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs | 1 +
.../GraphBinaryReaderWriterRoundTripTest.java | 2 +-
.../gremlin-javascript/test/cucumber/gremlin.js | 1 +
gremlin-python/src/main/python/radish/gremlin.py | 1 +
gremlin-test/features/filter/Has.feature | 11 +++++++++
12 files changed, 61 insertions(+), 12 deletions(-)
diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 825b951..9762fca 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -30,6 +30,7 @@
image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
* Deprecated `JsonBuilder` serialization for GraphSON and Gryo.
* Allowed `null` string values in the Gremlin grammar.
* Prevented exception with `hasLabel(null)` and instead filter away traversers
as labels can't ever be null.
+* Improved handling of `null` when passed to `P` predicates.
* Refined `DotNetTranslator` to be more explicit with `null` arguments to
ensure that the right overloads are called.
* Created `GremlinParser` to construct `Traversal` objects from
`gremlin-language`.
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/P.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/P.java
index 8454f33..a79231a 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/P.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/P.java
@@ -204,38 +204,46 @@ public class P<V> implements Predicate<V>, Serializable,
Cloneable {
}
/**
- * Determines if a value is within the specified list of values.
+ * Determines if a value is within the specified list of values. If the
array of arguments itself is {@code null}
+ * then the argument is treated as {@code Object[1]} where that single
value is {@code null}.
*
* @since 3.0.0-incubating
*/
public static <V> P<V> within(final V... values) {
- return P.within(Arrays.asList(values));
+ final V[] v = null == values ? (V[]) new Object[] { null } : values;
+ return P.within(Arrays.asList(v));
}
/**
- * Determines if a value is within the specified list of values.
+ * Determines if a value is within the specified list of values. Calling
this with {@code null} is the same as
+ * calling {@link #within(Object[])} using {@code null}.
*
* @since 3.0.0-incubating
*/
public static <V> P<V> within(final Collection<V> value) {
+ if (null == value) return P.within((V) null);
return new P(Contains.within, value);
}
/**
- * Determines if a value is not within the specified list of values.
+ * Determines if a value is not within the specified list of values. If
the array of arguments itself is {@code null}
+ * then the argument is treated as {@code Object[1]} where that single
value is {@code null}.
*
* @since 3.0.0-incubating
*/
public static <V> P<V> without(final V... values) {
- return P.without(Arrays.asList(values));
+ final V[] v = null == values ? (V[]) new Object[] { null } : values;
+ return P.without(Arrays.asList(v));
}
/**
- * Deermines if a value is not within the specified list of values.
+ * Determines if a value is not within the specified list of values.
Calling this with {@code null} is the same as
+ * calling {@link #within(Object[])} using {@code null}.
*
* @since 3.0.0-incubating
*/
public static <V> P<V> without(final Collection<V> value) {
+ if (null == value) return P.without((V) null);
return new P(Contains.without, value);
}
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/PSerializer.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/PSerializer.java
index e5ea3e3..a44e41d 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/PSerializer.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/PSerializer.java
@@ -60,7 +60,7 @@ public class PSerializer<T extends P> extends
SimpleTypeSerializer<T> {
final Class<?>[] argumentClasses = new Class[length];
for (int i = 0; i < length; i++) {
args[i] = context.read(buffer);
- argumentClasses[i] = args[i].getClass();
+ argumentClasses[i] = null == args[i] ? Object.class :
args[i].getClass();
}
if ("and".equals(predicateName))
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoSerializersV1d0.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoSerializersV1d0.java
index 7697462..33e535a 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoSerializersV1d0.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoSerializersV1d0.java
@@ -196,7 +196,7 @@ public final class GryoSerializersV1d0 {
final boolean isCollection = input.readByte() == (byte) 0;
final Object value;
if (isCollection) {
- value = new ArrayList();
+ value = new ArrayList<>();
final int size = input.readInt();
for (int ix = 0; ix < size; ix++) {
((List) value).add(kryo.readClassAndObject(input));
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoSerializersV3d0.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoSerializersV3d0.java
index e35bd33..d45bdd9 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoSerializersV3d0.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoSerializersV3d0.java
@@ -312,7 +312,7 @@ public final class GryoSerializersV3d0 {
final boolean isCollection = input.readByte() == (byte) 0;
final Object value;
if (isCollection) {
- value = new ArrayList();
+ value = new ArrayList<>();
final int size = input.readInt();
for (int ix = 0; ix < size; ix++) {
((List) value).add(kryo.readClassAndObject(input));
diff --git
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/PTest.java
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/PTest.java
index f2e0868..f6ec0d9 100644
---
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/PTest.java
+++
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/PTest.java
@@ -52,8 +52,14 @@ public class PTest {
return new ArrayList<>(Arrays.asList(new Object[][]{
{P.eq(0), 0, true},
{P.eq(0), 1, false},
+ {P.eq(0), null, false},
+ {P.eq(null), null, true},
+ {P.eq(null), 0, false},
{P.neq(0), 0, false},
{P.neq(0), 1, true},
+ {P.neq(0), null, true},
+ {P.neq(null), null, false},
+ {P.neq(null), 0, true},
{P.gt(0), -1, false},
{P.gt(0), 0, false},
{P.gt(0), 1, true},
@@ -78,15 +84,35 @@ public class PTest {
{P.outside(1, 10), 1, false},
{P.outside(1, 10), 9, false},
{P.outside(1, 10), 10, false},
+ {P.within((Object) null), 0, false},
+ {P.within((Object) null), null, true},
+ {P.within((Collection) null), 0, false},
+ {P.within((Collection) null), null, true},
+ {P.within(null, 2, 3), 0, false},
+ {P.within(null, 2, 3), null, true},
+ {P.within(null, 2, 3), 2, true},
{P.within(1, 2, 3), 0, false},
{P.within(1, 2, 3), 1, true},
{P.within(1, 2, 3), 10, false},
+ {P.within(Arrays.asList(null, 2, 3)), null, true},
+ {P.within(Arrays.asList(null, 2, 3)), 1, false},
+ {P.within(Arrays.asList(null, 2, 3)), 2, true},
{P.within(Arrays.asList(1, 2, 3)), 0, false},
{P.within(Arrays.asList(1, 2, 3)), 1, true},
{P.within(Arrays.asList(1, 2, 3)), 10, false},
+ {P.without((Object) null), 0, true},
+ {P.without((Object) null), null, false},
+ {P.without((Collection) null), 0, true},
+ {P.without((Collection) null), null, false},
+ {P.without(null, 2, 3), 0, true},
+ {P.without(null, 2, 3), null, false},
+ {P.without(null, 2, 3), 2, false},
{P.without(1, 2, 3), 0, true},
{P.without(1, 2, 3), 1, false},
{P.without(1, 2, 3), 10, true},
+ {P.without(Arrays.asList(null, 2, 3)), null, false},
+ {P.without(Arrays.asList(null, 2, 3)), 1, true},
+ {P.without(Arrays.asList(null, 2, 3)), 2, false},
{P.without(Arrays.asList(1, 2, 3)), 0, true},
{P.without(Arrays.asList(1, 2, 3)), 1, false},
{P.without(Arrays.asList(1, 2, 3)), 10, true},
diff --git
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapperEmbeddedTypeTest.java
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapperEmbeddedTypeTest.java
index dd25ec9..3238480 100644
---
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapperEmbeddedTypeTest.java
+++
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapperEmbeddedTypeTest.java
@@ -373,7 +373,7 @@ public class GraphSONMapperEmbeddedTypeTest extends
AbstractGraphSONTest {
public void shouldHandlePMultiValue() throws Exception {
assumeThat(version, either(startsWith("v2")).or(startsWith("v3")));
- final P o = P.within(1,2,3);
+ final P o = P.within(1,2,3,null);
assertEquals(o, serializeDeserialize(mapper, o, P.class));
}
@@ -389,7 +389,7 @@ public class GraphSONMapperEmbeddedTypeTest extends
AbstractGraphSONTest {
public void shouldHandlePMultiValueAsList() throws Exception {
assumeThat(version, either(startsWith("v2")).or(startsWith("v3")));
- final P o = P.within(Arrays.asList(1,2,3));
+ final P o = P.within(Arrays.asList(1,2,3,null));
assertEquals(o, serializeDeserialize(mapper, o, P.class));
}
diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
index f9567a0..6bc762c 100644
--- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
@@ -199,6 +199,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
{"g_V_hasLabelXpersonX_hasXage_notXlteX10X_andXnotXbetweenX11_20XXXX_andXltX29X_orXeqX35XXXX_name",
new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p)
=>g.V().HasLabel("person").Has("age",P.Gt(10).Or(P.Gte(11).And(P.Lt(20))).And(P.Lt(29).Or(P.Eq(35)))).Values<object>("name")}},
{"g_V_in_hasIdXneqX1XX", new List<Func<GraphTraversalSource,
IDictionary<string, object>, ITraversal>> {(g,p)
=>g.V().In().HasId(p["xx1"])}},
{"g_V_hasXage_withinX27X_count", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.V().Has("age",P.Within(new List<object> {27})).Count()}},
+ {"g_V_hasXage_withinX27_nullX_count", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.V().Has("age",P.Within(new List<object> {27, null})).Count()}},
{"g_V_hasXage_withinX27_29X_count", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.V().Has("age",P.Within(new List<object> {27, 29})).Count()}},
{"g_V_hasXage_withoutX27X_count", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.V().Has("age",P.Without(new List<object> {27})).Count()}},
{"g_V_hasXage_withoutX27_29X_count", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.V().Has("age",P.Without(new List<object> {27, 29})).Count()}},
diff --git
a/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/binary/GraphBinaryReaderWriterRoundTripTest.java
b/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/binary/GraphBinaryReaderWriterRoundTripTest.java
index 5a351f1..3fd54e2 100644
---
a/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/binary/GraphBinaryReaderWriterRoundTripTest.java
+++
b/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/binary/GraphBinaryReaderWriterRoundTripTest.java
@@ -231,7 +231,7 @@ public class GraphBinaryReaderWriterRoundTripTest {
new Object[] {"Pand", P.gt(1).and(P.lt(2)), null},
new Object[] {"Por", P.gt(1).or(P.lt(2)), null},
new Object[] {"Pnot", P.not(P.lte(1)), null},
- new Object[] {"Pwithout", P.without(1,2,3,4), null},
+ new Object[] {"Pwithout", P.without(1,2,3,4,null), null},
new Object[] {"Pinside", P.inside(0.0d, 0.6d), null},
new Object[] {"TextP", TextP.startingWith("mark"), null},
diff --git
a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js
b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js
index 13a5a24..a82873b 100644
---
a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js
+++
b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js
@@ -188,6 +188,7 @@ const gremlins = {
g_V_hasLabelXpersonX_hasXage_notXlteX10X_andXnotXbetweenX11_20XXXX_andXltX29X_orXeqX35XXXX_name:
[function({g}) { return
g.V().hasLabel("person").has("age",P.gt(10).or(P.gte(11).and(P.lt(20))).and(P.lt(29).or(P.eq(35)))).values("name")
}],
g_V_in_hasIdXneqX1XX: [function({g, xx1}) { return g.V().in_().hasId(xx1)
}],
g_V_hasXage_withinX27X_count: [function({g}) { return
g.V().has("age",P.within([27])).count() }],
+ g_V_hasXage_withinX27_nullX_count: [function({g}) { return
g.V().has("age",P.within([27, null])).count() }],
g_V_hasXage_withinX27_29X_count: [function({g}) { return
g.V().has("age",P.within([27, 29])).count() }],
g_V_hasXage_withoutX27X_count: [function({g}) { return
g.V().has("age",P.without([27])).count() }],
g_V_hasXage_withoutX27_29X_count: [function({g}) { return
g.V().has("age",P.without([27, 29])).count() }],
diff --git a/gremlin-python/src/main/python/radish/gremlin.py
b/gremlin-python/src/main/python/radish/gremlin.py
index 3974ce9..8b4100c 100644
--- a/gremlin-python/src/main/python/radish/gremlin.py
+++ b/gremlin-python/src/main/python/radish/gremlin.py
@@ -173,6 +173,7 @@ world.gremlins = {
'g_V_hasLabelXpersonX_hasXage_notXlteX10X_andXnotXbetweenX11_20XXXX_andXltX29X_orXeqX35XXXX_name':
[(lambda
g:g.V().hasLabel('person').has('age',P.gt(10).or_(P.gte(11).and_(P.lt(20))).and_(P.lt(29).or_(P.eq(35)))).name)],
'g_V_in_hasIdXneqX1XX': [(lambda g, xx1=None:g.V().in_().hasId(xx1))],
'g_V_hasXage_withinX27X_count': [(lambda
g:g.V().has('age',P.within([27])).count())],
+ 'g_V_hasXage_withinX27_nullX_count': [(lambda
g:g.V().has('age',P.within([27,None])).count())],
'g_V_hasXage_withinX27_29X_count': [(lambda
g:g.V().has('age',P.within([27,29])).count())],
'g_V_hasXage_withoutX27X_count': [(lambda
g:g.V().has('age',P.without([27])).count())],
'g_V_hasXage_withoutX27_29X_count': [(lambda
g:g.V().has('age',P.without([27,29])).count())],
diff --git a/gremlin-test/features/filter/Has.feature
b/gremlin-test/features/filter/Has.feature
index 3be3586..db78114 100644
--- a/gremlin-test/features/filter/Has.feature
+++ b/gremlin-test/features/filter/Has.feature
@@ -419,6 +419,17 @@ Feature: Step - has()
| result |
| d[1].l |
+ Scenario: g_V_hasXage_withinX27_nullX_count
+ Given the modern graph
+ And the traversal of
+ """
+ g.V().has("age", P.within(27,null)).count()
+ """
+ When iterated to list
+ Then the result should be ordered
+ | result |
+ | d[1].l |
+
Scenario: g_V_hasXage_withinX27_29X_count
Given the modern graph
And the traversal of