diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index d94822198f3..3ce1b813175 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -25,6 +25,7 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima [[release-4-0-0]] === TinkerPop 4.0.0 (Release Date: NOT OFFICIALLY RELEASED YET) +* Allowed `inject()` to accept bound variables, aligning it with the other variadic steps (`V()`, `E()`, `within()`, `without()`, etc.) which already accept a `genericArgument`; previously `inject()` was the only such step restricted to literals. * Fixed `gremlin-go` to report a malformed or truncated GraphBinary response as a deserialization error rather than a bare decoder message. * Made `TinkerGraph` an interface and renamed the in-memory implementation to `TinkerMemoryGraph`; `TinkerGraph.open()` and `gremlin.graph=...TinkerGraph` behave as before. *(breaking)* * Renamed `TinkerTransactionGraph` to `TinkerStorageGraph`. *(breaking)* diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitor.java index 9c12ef900de..d93471b2d8d 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitor.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitor.java @@ -1037,7 +1037,7 @@ public GraphTraversal visitTraversalMethod_inV(final GremlinParser.TraversalMeth */ @Override public GraphTraversal visitTraversalMethod_inject(final GremlinParser.TraversalMethod_injectContext ctx) { - return graphTraversal.inject(antlr.genericVisitor.parseObjectVarargs(ctx.genericLiteralVarargs())); + return graphTraversal.inject(antlr.argumentVisitor.parseObjectVarargs(ctx.genericArgumentVarargs())); } @Override diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalSourceSpawnMethodVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalSourceSpawnMethodVisitor.java index ae8d0fba34d..090d4029ad6 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalSourceSpawnMethodVisitor.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalSourceSpawnMethodVisitor.java @@ -143,7 +143,7 @@ public GraphTraversal visitTraversalSourceSpawnMethod_V(final GremlinParser.Trav */ @Override public GraphTraversal visitTraversalSourceSpawnMethod_inject(final GremlinParser.TraversalSourceSpawnMethod_injectContext ctx) { - return this.traversalSource.inject(antlr.genericVisitor.parseObjectVarargs(ctx.genericLiteralVarargs())); + return this.traversalSource.inject(antlr.argumentVisitor.parseObjectVarargs(ctx.genericArgumentVarargs())); } /** diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/GroovyTranslateVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/GroovyTranslateVisitor.java index 76b369d2f10..137bff827d2 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/GroovyTranslateVisitor.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/GroovyTranslateVisitor.java @@ -20,7 +20,6 @@ import org.antlr.v4.runtime.ParserRuleContext; import org.apache.tinkerpop.gremlin.language.grammar.GremlinParser; -import org.apache.tinkerpop.gremlin.language.grammar.GremlinParser.GenericLiteralVarargsContext; import org.apache.tinkerpop.gremlin.structure.util.reference.ReferenceVertex; /** @@ -267,29 +266,26 @@ public Void visitTraversalMethod_hasLabel_String_String(final GremlinParser.Trav * inject() ends up being ambiguous with groovy's jdk extension of inject(Object initialValue, Closure closure) */ private Void handleInject(final ParserRuleContext ctx) { - if (ctx.getChildCount() > 3 && ctx.getChild(2) instanceof GremlinParser.GenericLiteralVarargsContext) { - final GremlinParser.GenericLiteralVarargsContext varArgs = (GremlinParser.GenericLiteralVarargsContext) ctx.getChild(2); - if (varArgs.getChildCount() == 1 && varArgs.getChild(0) instanceof GremlinParser.GenericLiteralExprContext) { - GremlinParser.GenericLiteralExprContext injectArgs = (GremlinParser.GenericLiteralExprContext) varArgs.getChild(0); - if (injectArgs.getChildCount() > 2 && "null".equals(injectArgs.getChild(2).getText())) { - sb.append(ctx.getChild(0).getText()); - sb.append("("); - for (int i = 0; i < injectArgs.getChildCount(); i += 2) { - if (i == 2) { - sb.append("(Object) null"); - } else { - visit(injectArgs.getChild(i)); - } - - if (i < injectArgs.getChildCount() - 1) { - sb.append(", "); - } - } - - sb.append(")"); - return null; + // ctx is always K_INJECT LPAREN genericArgumentVarargs RPAREN, so child(2) is the varargs node. + final GremlinParser.GenericArgumentVarargsContext injectArgs = + (GremlinParser.GenericArgumentVarargsContext) ctx.getChild(2); + if (injectArgs.getChildCount() > 2 && "null".equals(injectArgs.getChild(2).getText())) { + sb.append(ctx.getChild(0).getText()); + sb.append("("); + for (int i = 0; i < injectArgs.getChildCount(); i += 2) { + if (i == 2) { + sb.append("(Object) null"); + } else { + visit(injectArgs.getChild(i)); + } + + if (i < injectArgs.getChildCount() - 1) { + sb.append(", "); } } + + sb.append(")"); + return null; } return visitChildren(ctx); diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/GremlinQueryParserTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/GremlinQueryParserTest.java index cd16ef308c4..b0f12302e60 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/GremlinQueryParserTest.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/GremlinQueryParserTest.java @@ -67,6 +67,11 @@ public void shouldParseVariablesInVarargs() { t = (GraphTraversal) GremlinQueryParser.parse("g.V(x, y, 300).has('name',gt(z))", gremlinAntlrToJava); assertEquals(g.V(100, 200, 300).has("name", P.gt(50)).asAdmin().getGremlinLang(), t.asAdmin().getGremlinLang()); + + // inject() accepts bound variables like the other varargs steps (V, within, etc.) + t = (GraphTraversal) GremlinQueryParser.parse("g.inject(x, y, 300)", gremlinAntlrToJava); + assertEquals(g.inject(100, 200, 300).asAdmin().getGremlinLang(), + t.asAdmin().getGremlinLang()); } @Test(expected = GremlinParserException.class) diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/translator/GremlinTranslatorTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/translator/GremlinTranslatorTest.java index 7ddc147fe3c..49b8f4352f1 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/translator/GremlinTranslatorTest.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/translator/GremlinTranslatorTest.java @@ -803,6 +803,15 @@ public static Collection data() { "g.inject(null, null).inject(null, null)", "g.inject(null, null).inject(null, null)", "g.inject(None, None).inject(None, None)"}, + {"g.inject(1, 2)", + null, + "g.inject(number0, number1)", + "g.Inject(1, 2)", + "g.Inject(1, 2)", + null, + null, + null, + null}, {"g.V().hasLabel(null)", null, "g.V().hasLabel(string0)", diff --git a/gremlin-js/gremlin-javascript/lib/language/translator/GroovyTranslateVisitor.ts b/gremlin-js/gremlin-javascript/lib/language/translator/GroovyTranslateVisitor.ts index f061975e2bd..9396a9ab948 100644 --- a/gremlin-js/gremlin-javascript/lib/language/translator/GroovyTranslateVisitor.ts +++ b/gremlin-js/gremlin-javascript/lib/language/translator/GroovyTranslateVisitor.ts @@ -290,29 +290,23 @@ export default class GroovyTranslateVisitor extends TranslateVisitor { private handleInject(ctx: any): void { if (ctx.getChildCount() > 3) { const child2 = ctx.getChild(2); - if (child2?.constructor?.name === 'GenericLiteralVarargsContext') { - const varArgs = child2; - if (varArgs.getChildCount() === 1) { - const child0 = varArgs.getChild(0); - if (child0?.constructor?.name === 'GenericLiteralExprContext') { - const injectArgs = child0; - if (injectArgs.getChildCount() > 2 && injectArgs.getChild(2).getText() === 'null') { - this.sb.push(ctx.getChild(0).getText()); - this.sb.push('('); - for (let i = 0; i < injectArgs.getChildCount(); i += 2) { - if (i === 2) { - this.sb.push('(Object) null'); - } else { - this.visit(injectArgs.getChild(i)); - } - if (i < injectArgs.getChildCount() - 1) { - this.sb.push(', '); - } - } - this.sb.push(')'); - return; + if (child2?.constructor?.name === 'GenericArgumentVarargsContext') { + const injectArgs = child2; + if (injectArgs.getChildCount() > 2 && injectArgs.getChild(2).getText() === 'null') { + this.sb.push(ctx.getChild(0).getText()); + this.sb.push('('); + for (let i = 0; i < injectArgs.getChildCount(); i += 2) { + if (i === 2) { + this.sb.push('(Object) null'); + } else { + this.visit(injectArgs.getChild(i)); + } + if (i < injectArgs.getChildCount() - 1) { + this.sb.push(', '); } } + this.sb.push(')'); + return; } } } diff --git a/gremlin-language/src/main/antlr4/Gremlin.g4 b/gremlin-language/src/main/antlr4/Gremlin.g4 index d15a2982df8..db253728d93 100644 --- a/gremlin-language/src/main/antlr4/Gremlin.g4 +++ b/gremlin-language/src/main/antlr4/Gremlin.g4 @@ -133,7 +133,7 @@ traversalSourceSpawnMethod_V ; traversalSourceSpawnMethod_inject - : K_INJECT LPAREN genericLiteralVarargs RPAREN + : K_INJECT LPAREN genericArgumentVarargs RPAREN ; traversalSourceSpawnMethod_io @@ -626,7 +626,7 @@ traversalMethod_index ; traversalMethod_inject - : K_INJECT LPAREN genericLiteralVarargs RPAREN + : K_INJECT LPAREN genericArgumentVarargs RPAREN ; traversalMethod_is diff --git a/gremlin-language/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/BasicGrammarTest.java b/gremlin-language/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/BasicGrammarTest.java index 14ac12108e8..da46d1e153f 100644 --- a/gremlin-language/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/BasicGrammarTest.java +++ b/gremlin-language/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/BasicGrammarTest.java @@ -28,4 +28,14 @@ public class BasicGrammarTest extends AbstractGrammarTest { public void shouldParseV() { parse("g.V()", ParserRule.QUERY_LIST); } + + @Test + public void shouldParseInjectWithVariable() { + parse("g.inject(x)", ParserRule.QUERY_LIST); + } + + @Test + public void shouldParseInjectStepWithVariable() { + parse("g.V().inject(x)", ParserRule.QUERY_LIST); + } }