From 383ca496dc4ef1cd0e7e7dd556d24ab4bd330b19 Mon Sep 17 00:00:00 2001 From: "AD\\aabdoun" Date: Mon, 20 Jul 2026 13:33:50 +0200 Subject: [PATCH 1/2] #373 [SPARQL 1.1 Graph Management] - Parser and AST : DROP --- .../impl/parser/SparqlListenerDispatcher.java | 10 + .../impl/parser/SparqlQueryAnalyzer.java | 1 + .../impl/parser/SparqlUpdateAstBuilder.java | 13 ++ .../listener/DropUpdateAstListener.java | 20 ++ .../query/impl/sparql/ast/DropRequestAst.java | 24 ++ .../impl/sparql/ast/UpdateRequestUnitAst.java | 2 +- .../parser/SparqlParserDropQueryTest.java | 215 ++++++++++++++++++ 7 files changed, 284 insertions(+), 1 deletion(-) create mode 100644 src/main/java/fr/inria/corese/core/next/query/impl/parser/listener/DropUpdateAstListener.java create mode 100644 src/main/java/fr/inria/corese/core/next/query/impl/sparql/ast/DropRequestAst.java create mode 100644 src/test/java/fr/inria/corese/core/next/query/impl/parser/SparqlParserDropQueryTest.java diff --git a/src/main/java/fr/inria/corese/core/next/query/impl/parser/SparqlListenerDispatcher.java b/src/main/java/fr/inria/corese/core/next/query/impl/parser/SparqlListenerDispatcher.java index 327b66be5..8849fb3fb 100644 --- a/src/main/java/fr/inria/corese/core/next/query/impl/parser/SparqlListenerDispatcher.java +++ b/src/main/java/fr/inria/corese/core/next/query/impl/parser/SparqlListenerDispatcher.java @@ -302,4 +302,14 @@ public void enterCreate(SparqlParser.CreateContext ctx) { public void exitCreate(SparqlParser.CreateContext ctx) { for (var d : delegates) d.exitCreate(ctx); } + + @Override + public void enterDrop(SparqlParser.DropContext ctx) { + for (var d : delegates) d.enterDrop(ctx); + } + + @Override + public void exitDrop(SparqlParser.DropContext ctx) { + for (var d : delegates) d.exitDrop(ctx); + } } diff --git a/src/main/java/fr/inria/corese/core/next/query/impl/parser/SparqlQueryAnalyzer.java b/src/main/java/fr/inria/corese/core/next/query/impl/parser/SparqlQueryAnalyzer.java index 372982ca7..a4c845f75 100644 --- a/src/main/java/fr/inria/corese/core/next/query/impl/parser/SparqlQueryAnalyzer.java +++ b/src/main/java/fr/inria/corese/core/next/query/impl/parser/SparqlQueryAnalyzer.java @@ -160,6 +160,7 @@ private QueryAst buildAst( SparqlListenerDispatcher updateListener = new SparqlListenerDispatcher(List.of( new ClearUpdateAstListener(updateBuilder), new CreateUpdateAstListener(updateBuilder), + new DropUpdateAstListener(updateBuilder), new LoadUpdateAstListener(updateBuilder), new BgpAstListener(updateBuilder), new BindAstListener(updateBuilder), diff --git a/src/main/java/fr/inria/corese/core/next/query/impl/parser/SparqlUpdateAstBuilder.java b/src/main/java/fr/inria/corese/core/next/query/impl/parser/SparqlUpdateAstBuilder.java index ac4270542..3dd1e359a 100644 --- a/src/main/java/fr/inria/corese/core/next/query/impl/parser/SparqlUpdateAstBuilder.java +++ b/src/main/java/fr/inria/corese/core/next/query/impl/parser/SparqlUpdateAstBuilder.java @@ -58,6 +58,19 @@ public ClearRequestAst cleartoAst(SparqlParser.ClearContext ctx) { } } + public DropRequestAst droptoAst(SparqlParser.DropContext ctx) { + GraphRefAst targetGraphRef = null; + boolean silentFlag = ctx.SILENT() != null; + if (ctx.graphRefAll() != null) { + targetGraphRef = this.graphRefFromGraphRefAll(ctx.graphRefAll()); + } + if (targetGraphRef != null) { + return new DropRequestAst(targetGraphRef, silentFlag); + } else { + throw new QueryEvaluationException("No target graph reference found in DROP query"); + } + } + public CreateRequestAst createToAst(SparqlParser.CreateContext ctx) { boolean silentFlag = ctx.SILENT() != null; if (ctx.graphRef() != null) { diff --git a/src/main/java/fr/inria/corese/core/next/query/impl/parser/listener/DropUpdateAstListener.java b/src/main/java/fr/inria/corese/core/next/query/impl/parser/listener/DropUpdateAstListener.java new file mode 100644 index 000000000..ba8dcdacc --- /dev/null +++ b/src/main/java/fr/inria/corese/core/next/query/impl/parser/listener/DropUpdateAstListener.java @@ -0,0 +1,20 @@ +package fr.inria.corese.core.next.query.impl.parser.listener; + +import fr.inria.corese.core.next.impl.parser.antlr.SparqlParser; +import fr.inria.corese.core.next.query.impl.parser.SparqlUpdateAstBuilder; +import fr.inria.corese.core.next.query.impl.sparql.ast.DropRequestAst; + +/** + * AST feature listener for DROP SPARQL update query + */ +public class DropUpdateAstListener extends AbstractSparqlUpdateAstListener { + public DropUpdateAstListener(SparqlUpdateAstBuilder builder) { + super(builder); + } + + @Override + public void enterDrop(SparqlParser.DropContext ctx) { + DropRequestAst dropRequestAst = this.updateBuilder().droptoAst(ctx); + this.updateBuilder().addRequest(dropRequestAst); + } +} \ No newline at end of file diff --git a/src/main/java/fr/inria/corese/core/next/query/impl/sparql/ast/DropRequestAst.java b/src/main/java/fr/inria/corese/core/next/query/impl/sparql/ast/DropRequestAst.java new file mode 100644 index 000000000..82d6c3ea7 --- /dev/null +++ b/src/main/java/fr/inria/corese/core/next/query/impl/sparql/ast/DropRequestAst.java @@ -0,0 +1,24 @@ +package fr.inria.corese.core.next.query.impl.sparql.ast; + +import fr.inria.corese.core.next.query.impl.parser.semantic.support.AstVisitor; + +/** + * Represents the DROP operation as defined in the SPARQL 1.1 recommendation. + * @param graphRef targeted graph to be dropped + * @param silent Determine if the resolution of the query must be resolved silently or not. + */ +public record DropRequestAst(GraphRefAst graphRef, boolean silent) implements UpdateRequestUnitAst { + /** + * Construct DROP query with a silent flag to false. + * @param graphRef targeted graph to be dropped + */ + public DropRequestAst(GraphRefAst graphRef) { + this(graphRef, false); + } + + @Override + public void accept(AstVisitor visitor) { + visitor.visit(this); + this.graphRef.accept(visitor); + } +} \ No newline at end of file diff --git a/src/main/java/fr/inria/corese/core/next/query/impl/sparql/ast/UpdateRequestUnitAst.java b/src/main/java/fr/inria/corese/core/next/query/impl/sparql/ast/UpdateRequestUnitAst.java index e68694223..d65855d19 100644 --- a/src/main/java/fr/inria/corese/core/next/query/impl/sparql/ast/UpdateRequestUnitAst.java +++ b/src/main/java/fr/inria/corese/core/next/query/impl/sparql/ast/UpdateRequestUnitAst.java @@ -5,5 +5,5 @@ /** * Root interface for all operations related to the SPARQL Update operations listed in SPARQL 1.1 Update. */ -public sealed interface UpdateRequestUnitAst extends VisitableAst permits LoadRequestAst, ClearRequestAst, CreateRequestAst { +public sealed interface UpdateRequestUnitAst extends VisitableAst permits LoadRequestAst, ClearRequestAst, CreateRequestAst, DropRequestAst { } diff --git a/src/test/java/fr/inria/corese/core/next/query/impl/parser/SparqlParserDropQueryTest.java b/src/test/java/fr/inria/corese/core/next/query/impl/parser/SparqlParserDropQueryTest.java new file mode 100644 index 000000000..267334da7 --- /dev/null +++ b/src/test/java/fr/inria/corese/core/next/query/impl/parser/SparqlParserDropQueryTest.java @@ -0,0 +1,215 @@ +package fr.inria.corese.core.next.query.impl.parser; + +import fr.inria.corese.core.next.query.api.exception.QuerySyntaxException; +import fr.inria.corese.core.next.query.impl.parser.QueryParser; +import fr.inria.corese.core.next.query.impl.sparql.ast.DropRequestAst; +import fr.inria.corese.core.next.query.impl.sparql.ast.IriAst; +import fr.inria.corese.core.next.query.impl.sparql.ast.QueryAst; +import fr.inria.corese.core.next.query.impl.sparql.ast.UpdateRequestAst; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class SparqlParserDropQueryTest extends AbstractSparqlParserFeatureTest { + + @Test + @DisplayName("should parse DROP GRAPH ") + void graphQueryTest() { + QueryParser parser = newParserDefault(); + String query = """ + DROP GRAPH + """; + + QueryAst queryAst = parser.parse(query); + assertInstanceOf(UpdateRequestAst.class, queryAst); + UpdateRequestAst updateRequestAst = (UpdateRequestAst) queryAst; + assertEquals(1, updateRequestAst.operations().size()); + assertInstanceOf(DropRequestAst.class, updateRequestAst.operations().getFirst()); + DropRequestAst dropQueryAst = (DropRequestAst) updateRequestAst.operations().getFirst(); + assertNotNull(dropQueryAst.graphRef()); + IriAst graphIri = dropQueryAst.graphRef().graph(); + assertEquals("", graphIri.raw()); + assertFalse(dropQueryAst.silent()); + } + + @Test + @DisplayName("should parse DROP SILENT GRAPH and set the silent flag") + void graphSilentQueryTest() { + QueryParser parser = newParserDefault(); + String query = """ + DROP SILENT GRAPH + """; + + QueryAst queryAst = parser.parse(query); + assertInstanceOf(UpdateRequestAst.class, queryAst); + UpdateRequestAst updateRequestAst = (UpdateRequestAst) queryAst; + assertEquals(1, updateRequestAst.operations().size()); + assertInstanceOf(DropRequestAst.class, updateRequestAst.operations().getFirst()); + DropRequestAst dropQueryAst = (DropRequestAst) updateRequestAst.operations().getFirst(); + assertNotNull(dropQueryAst.graphRef()); + IriAst graphIri = dropQueryAst.graphRef().graph(); + assertEquals("", graphIri.raw()); + assertTrue(dropQueryAst.silent()); + } + + @Test + @DisplayName("should parse DROP NAMED") + void namedQueryTest() { + QueryParser parser = newParserDefault(); + String query = """ + DROP NAMED + """; + + QueryAst queryAst = parser.parse(query); + assertInstanceOf(UpdateRequestAst.class, queryAst); + UpdateRequestAst updateRequestAst = (UpdateRequestAst) queryAst; + assertEquals(1, updateRequestAst.operations().size()); + assertInstanceOf(DropRequestAst.class, updateRequestAst.operations().getFirst()); + DropRequestAst dropQueryAst = (DropRequestAst) updateRequestAst.operations().getFirst(); + assertNotNull(dropQueryAst.graphRef()); + assertTrue(dropQueryAst.graphRef().named()); + assertFalse(dropQueryAst.silent()); + } + + @Test + @DisplayName("should parse DROP SILENT NAMED and set the silent flag") + void namedSilentQueryTest() { + QueryParser parser = newParserDefault(); + String query = """ + DROP SILENT NAMED + """; + + QueryAst queryAst = parser.parse(query); + assertInstanceOf(UpdateRequestAst.class, queryAst); + UpdateRequestAst updateRequestAst = (UpdateRequestAst) queryAst; + assertEquals(1, updateRequestAst.operations().size()); + assertInstanceOf(DropRequestAst.class, updateRequestAst.operations().getFirst()); + DropRequestAst dropQueryAst = (DropRequestAst) updateRequestAst.operations().getFirst(); + assertNotNull(dropQueryAst.graphRef()); + assertTrue(dropQueryAst.graphRef().named()); + assertTrue(dropQueryAst.silent()); + } + + @Test + @DisplayName("should parse DROP DEFAULT") + void defaultQueryTest() { + QueryParser parser = newParserDefault(); + String query = """ + DROP DEFAULT + """; + + QueryAst queryAst = parser.parse(query); + assertInstanceOf(UpdateRequestAst.class, queryAst); + UpdateRequestAst updateRequestAst = (UpdateRequestAst) queryAst; + assertEquals(1, updateRequestAst.operations().size()); + assertInstanceOf(DropRequestAst.class, updateRequestAst.operations().getFirst()); + DropRequestAst dropQueryAst = (DropRequestAst) updateRequestAst.operations().getFirst(); + assertNotNull(dropQueryAst.graphRef()); + assertTrue(dropQueryAst.graphRef().defaultGraph()); + assertFalse(dropQueryAst.silent()); + } + + @Test + @DisplayName("should parse DROP SILENT DEFAULT and set the silent flag") + void defaultSilentQueryTest() { + QueryParser parser = newParserDefault(); + String query = """ + DROP SILENT DEFAULT + """; + + QueryAst queryAst = parser.parse(query); + assertInstanceOf(UpdateRequestAst.class, queryAst); + UpdateRequestAst updateRequestAst = (UpdateRequestAst) queryAst; + assertEquals(1, updateRequestAst.operations().size()); + assertInstanceOf(DropRequestAst.class, updateRequestAst.operations().getFirst()); + DropRequestAst dropQueryAst = (DropRequestAst) updateRequestAst.operations().getFirst(); + assertNotNull(dropQueryAst.graphRef()); + assertTrue(dropQueryAst.graphRef().defaultGraph()); + assertTrue(dropQueryAst.silent()); + } + + @Test + @DisplayName("should parse DROP ALL") + void allQueryTest() { + QueryParser parser = newParserDefault(); + String query = """ + DROP ALL + """; + + QueryAst queryAst = parser.parse(query); + assertInstanceOf(UpdateRequestAst.class, queryAst); + UpdateRequestAst updateRequestAst = (UpdateRequestAst) queryAst; + assertEquals(1, updateRequestAst.operations().size()); + assertInstanceOf(DropRequestAst.class, updateRequestAst.operations().getFirst()); + DropRequestAst dropQueryAst = (DropRequestAst) updateRequestAst.operations().getFirst(); + assertNotNull(dropQueryAst.graphRef()); + assertTrue(dropQueryAst.graphRef().all()); + assertFalse(dropQueryAst.silent()); + } + + @Test + @DisplayName("should parse DROP SILENT ALL and set the silent flag") + void allSilentQueryTest() { + QueryParser parser = newParserDefault(); + String query = """ + DROP SILENT ALL + """; + + QueryAst queryAst = parser.parse(query); + assertInstanceOf(UpdateRequestAst.class, queryAst); + UpdateRequestAst updateRequestAst = (UpdateRequestAst) queryAst; + assertEquals(1, updateRequestAst.operations().size()); + assertInstanceOf(DropRequestAst.class, updateRequestAst.operations().getFirst()); + DropRequestAst dropQueryAst = (DropRequestAst) updateRequestAst.operations().getFirst(); + assertNotNull(dropQueryAst.graphRef()); + assertTrue(dropQueryAst.graphRef().all()); + assertTrue(dropQueryAst.silent()); + } + + @Test + @DisplayName("should parse two chained DROP operations separated by ';'") + void chainedDropShouldCreateTwoUpdateOperations() { + QueryParser parser = newParserDefault(); + String query = """ + DROP GRAPH ; + DROP SILENT GRAPH + """; + + QueryAst queryAst = parser.parse(query); + UpdateRequestAst update = assertInstanceOf(UpdateRequestAst.class, queryAst); + assertEquals(2, update.operations().size()); + + DropRequestAst first = assertInstanceOf(DropRequestAst.class, update.operations().getFirst()); + assertEquals("", first.graphRef().graph().raw()); + assertFalse(first.silent()); + + DropRequestAst second = assertInstanceOf(DropRequestAst.class, update.operations().get(1)); + assertEquals("", second.graphRef().graph().raw()); + assertTrue(second.silent()); + } + + @Test + @DisplayName("should keep the prologue when DROP uses a prefixed graph name") + void dropMustKeepItsPrologue() { + QueryParser parser = newParserDefault(); + QueryAst ast = parser.parse("PREFIX ex: DROP GRAPH ex:g"); + UpdateRequestAst update = assertInstanceOf(UpdateRequestAst.class, ast); + + assertEquals(1, update.operations().size()); + assertInstanceOf(DropRequestAst.class, update.operations().getFirst()); + assertNotNull(update.prologue()); + assertEquals(1, update.prologue().prefixDeclarations().size()); + } + + @Test + @DisplayName("should reject invalid DROP syntax with multiple graph references") + void failingTooManyQueryTest() { + QueryParser parser = newParserDefault(); + String query = """ + DROP SILENT ALL NAMED GRAPH + """; + + assertThrows(QuerySyntaxException.class, () -> parser.parse(query)); + } +} From 3606c80c28787ec83e83641ad469c3cc5b7ab5e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20C=C3=A9r=C3=A8s?= Date: Wed, 22 Jul 2026 14:36:13 +0200 Subject: [PATCH 2/2] style(query): polish DROP AST integration --- .../core/next/query/impl/parser/SparqlUpdateAstBuilder.java | 2 +- .../query/impl/parser/listener/DropUpdateAstListener.java | 4 ++-- .../core/next/query/impl/sparql/ast/DropRequestAst.java | 2 +- .../core/next/query/impl/sparql/ast/UpdateRequestUnitAst.java | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/fr/inria/corese/core/next/query/impl/parser/SparqlUpdateAstBuilder.java b/src/main/java/fr/inria/corese/core/next/query/impl/parser/SparqlUpdateAstBuilder.java index 3dd1e359a..4a77a2031 100644 --- a/src/main/java/fr/inria/corese/core/next/query/impl/parser/SparqlUpdateAstBuilder.java +++ b/src/main/java/fr/inria/corese/core/next/query/impl/parser/SparqlUpdateAstBuilder.java @@ -58,7 +58,7 @@ public ClearRequestAst cleartoAst(SparqlParser.ClearContext ctx) { } } - public DropRequestAst droptoAst(SparqlParser.DropContext ctx) { + public DropRequestAst dropToAst(SparqlParser.DropContext ctx) { GraphRefAst targetGraphRef = null; boolean silentFlag = ctx.SILENT() != null; if (ctx.graphRefAll() != null) { diff --git a/src/main/java/fr/inria/corese/core/next/query/impl/parser/listener/DropUpdateAstListener.java b/src/main/java/fr/inria/corese/core/next/query/impl/parser/listener/DropUpdateAstListener.java index ba8dcdacc..e12b2bf8e 100644 --- a/src/main/java/fr/inria/corese/core/next/query/impl/parser/listener/DropUpdateAstListener.java +++ b/src/main/java/fr/inria/corese/core/next/query/impl/parser/listener/DropUpdateAstListener.java @@ -14,7 +14,7 @@ public DropUpdateAstListener(SparqlUpdateAstBuilder builder) { @Override public void enterDrop(SparqlParser.DropContext ctx) { - DropRequestAst dropRequestAst = this.updateBuilder().droptoAst(ctx); + DropRequestAst dropRequestAst = this.updateBuilder().dropToAst(ctx); this.updateBuilder().addRequest(dropRequestAst); } -} \ No newline at end of file +} diff --git a/src/main/java/fr/inria/corese/core/next/query/impl/sparql/ast/DropRequestAst.java b/src/main/java/fr/inria/corese/core/next/query/impl/sparql/ast/DropRequestAst.java index 82d6c3ea7..bf071d511 100644 --- a/src/main/java/fr/inria/corese/core/next/query/impl/sparql/ast/DropRequestAst.java +++ b/src/main/java/fr/inria/corese/core/next/query/impl/sparql/ast/DropRequestAst.java @@ -21,4 +21,4 @@ public void accept(AstVisitor visitor) { visitor.visit(this); this.graphRef.accept(visitor); } -} \ No newline at end of file +} diff --git a/src/main/java/fr/inria/corese/core/next/query/impl/sparql/ast/UpdateRequestUnitAst.java b/src/main/java/fr/inria/corese/core/next/query/impl/sparql/ast/UpdateRequestUnitAst.java index d65855d19..52dc6ca05 100644 --- a/src/main/java/fr/inria/corese/core/next/query/impl/sparql/ast/UpdateRequestUnitAst.java +++ b/src/main/java/fr/inria/corese/core/next/query/impl/sparql/ast/UpdateRequestUnitAst.java @@ -5,5 +5,5 @@ /** * Root interface for all operations related to the SPARQL Update operations listed in SPARQL 1.1 Update. */ -public sealed interface UpdateRequestUnitAst extends VisitableAst permits LoadRequestAst, ClearRequestAst, CreateRequestAst, DropRequestAst { +public sealed interface UpdateRequestUnitAst extends VisitableAst permits LoadRequestAst, ClearRequestAst, CreateRequestAst, DropRequestAst { }