Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -312,4 +312,14 @@ public void enterDrop(SparqlParser.DropContext ctx) {
public void exitDrop(SparqlParser.DropContext ctx) {
for (var d : delegates) d.exitDrop(ctx);
}

@Override
public void enterAdd(SparqlParser.AddContext ctx) {
for (var d : delegates) d.enterAdd(ctx);
}

@Override
public void exitAdd(SparqlParser.AddContext ctx) {
for (var d : delegates) d.exitAdd(ctx);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ private QueryAst buildAst(
new CreateUpdateAstListener(updateBuilder),
new DropUpdateAstListener(updateBuilder),
new LoadUpdateAstListener(updateBuilder),
new AddUpdateAstListener(updateBuilder),
new BgpAstListener(updateBuilder),
new BindAstListener(updateBuilder),
new FilterAstListener(updateBuilder),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,11 @@ public CreateRequestAst createToAst(SparqlParser.CreateContext ctx) {
public void addRequest(UpdateRequestUnitAst ast) {
this.updateRequestAst.add(ast);
}

public AddRequestAst addToAst(SparqlParser.AddContext ctx) {
return new AddRequestAst(
graphRefFromGraphOrDefault(ctx.graphOrDefault(0)),
graphRefFromGraphOrDefault(ctx.graphOrDefault(1)),
ctx.SILENT() != null);
}
}
Original file line number Diff line number Diff line change
@@ -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.AddRequestAst;

/**
* AST feature listener for ADD SPARQL update query.
*/
public class AddUpdateAstListener extends AbstractSparqlUpdateAstListener {
public AddUpdateAstListener(SparqlUpdateAstBuilder builder) {
super(builder);
}

@Override
public void enterAdd(SparqlParser.AddContext ctx) {
AddRequestAst ast = this.updateBuilder().addToAst(ctx);
this.updateBuilder().addRequest(ast);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package fr.inria.corese.core.next.query.impl.sparql.ast;

import fr.inria.corese.core.next.query.impl.parser.semantic.support.AstVisitor;

/**
* Represents the ADD operation as defined in the <a href="https://www.w3.org/TR/2013/REC-sparql11-update-20130321/#add">SPARQL 1.1 recommendation</a>.
* @param source source graph ({@code GRAPH <iri>} or {@code DEFAULT})
* @param destination destination graph ({@code GRAPH <iri>} or {@code DEFAULT})
* @param silent Determine if the resolution of the query must be resolved silently or not.
*/
public record AddRequestAst(GraphRefAst source, GraphRefAst destination, boolean silent) implements UpdateRequestUnitAst {
/**
* Construct an ADD query with the silent flag set to false.
* @param source source graph
* @param destination destination graph
*/
public AddRequestAst(GraphRefAst source, GraphRefAst destination) {
this(source, destination, false);
}

@Override
public void accept(AstVisitor visitor) {
visitor.visit(this);
this.source.accept(visitor);
this.destination.accept(visitor);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@
/**
* Root interface for all operations related to the SPARQL Update operations listed in <a href="https://www.w3.org/TR/sparql11-update/">SPARQL 1.1 Update</a>.
*/
public sealed interface UpdateRequestUnitAst extends VisitableAst permits LoadRequestAst, ClearRequestAst, CreateRequestAst, DropRequestAst {
public sealed interface UpdateRequestUnitAst extends VisitableAst permits LoadRequestAst, ClearRequestAst, CreateRequestAst, DropRequestAst, AddRequestAst {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
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.sparql.ast.AddRequestAst;
import fr.inria.corese.core.next.query.impl.sparql.ast.QueryAst;
import fr.inria.corese.core.next.query.impl.sparql.ast.UpdateRequestAst;
import fr.inria.corese.core.next.query.impl.sparql.ast.UpdateRequestUnitAst;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

class AddUpdateAstBuilderTest extends AbstractSparqlParserFeatureTest {

private UpdateRequestUnitAst parseUnit(String query) {
QueryAst ast = newParserDefault().parse(query);
UpdateRequestAst update = assertInstanceOf(UpdateRequestAst.class, ast);
assertEquals(1, update.operations().size(), "one update operation");
return update.operations().getFirst();
}

@Test
@DisplayName("ADD GRAPH <s> TO GRAPH <d>: source and destination named graphs, not silent")
void addGraphToGraph() {
AddRequestAst add = assertInstanceOf(AddRequestAst.class,
parseUnit("ADD GRAPH <http://example.org/s> TO GRAPH <http://example.org/d>"));
assertFalse(add.silent());
assertEquals("<http://example.org/s>", add.source().graph().raw());
assertEquals("<http://example.org/d>", add.destination().graph().raw());
}

@Test
@DisplayName("ADD accepts the SPARQL shorthand graph IRI form without GRAPH")
void addAcceptsGraphIriShorthand() {
AddRequestAst add = assertInstanceOf(AddRequestAst.class,
parseUnit("ADD <http://example.org/s> TO <http://example.org/d>"));
assertEquals("<http://example.org/s>", add.source().graph().raw());
assertEquals("<http://example.org/d>", add.destination().graph().raw());
}

@Test
@DisplayName("ADD SILENT DEFAULT TO GRAPH <d>: silent, source is DEFAULT")
void addSilentDefaultToGraph() {
AddRequestAst add = assertInstanceOf(AddRequestAst.class,
parseUnit("ADD SILENT DEFAULT TO GRAPH <http://example.org/d>"));
assertTrue(add.silent());
assertTrue(add.source().defaultGraph());
assertEquals("<http://example.org/d>", add.destination().graph().raw());
}

@Test
@DisplayName("ADD GRAPH <s> TO DEFAULT: destination is DEFAULT")
void addGraphToDefault() {
AddRequestAst add = assertInstanceOf(AddRequestAst.class,
parseUnit("ADD GRAPH <http://example.org/s> TO DEFAULT"));
assertFalse(add.silent());
assertEquals("<http://example.org/s>", add.source().graph().raw());
assertTrue(add.destination().defaultGraph());
}

@Test
@DisplayName("ADD DEFAULT TO DEFAULT: both source and destination are DEFAULT")
void addDefaultToDefault() {
AddRequestAst add = assertInstanceOf(AddRequestAst.class,
parseUnit("ADD DEFAULT TO DEFAULT"));
assertFalse(add.silent());
assertTrue(add.source().defaultGraph());
assertTrue(add.destination().defaultGraph());
}

@Test
@DisplayName("ADD SILENT DEFAULT TO DEFAULT: silent with both DEFAULT")
void addSilentDefaultToDefault() {
AddRequestAst add = assertInstanceOf(AddRequestAst.class,
parseUnit("ADD SILENT DEFAULT TO DEFAULT"));
assertTrue(add.silent());
assertTrue(add.source().defaultGraph());
assertTrue(add.destination().defaultGraph());
}

@Test
@DisplayName("chained ADD operations separated by ';' produce two update operations")
void chainedAddProducesTwoOperations() {
QueryAst ast = newParserDefault().parse(
"ADD GRAPH <http://example.org/s> TO GRAPH <http://example.org/d> ; " +
"ADD SILENT DEFAULT TO GRAPH <http://example.org/d2>");
UpdateRequestAst update = assertInstanceOf(UpdateRequestAst.class, ast);
assertEquals(2, update.operations().size());

AddRequestAst first = assertInstanceOf(AddRequestAst.class, update.operations().get(0));
assertEquals("<http://example.org/s>", first.source().graph().raw());
assertEquals("<http://example.org/d>", first.destination().graph().raw());
assertFalse(first.silent());

AddRequestAst second = assertInstanceOf(AddRequestAst.class, update.operations().get(1));
assertTrue(second.source().defaultGraph());
assertEquals("<http://example.org/d2>", second.destination().graph().raw());
assertTrue(second.silent());
}

@Test
@DisplayName("PREFIX declaration is preserved in the prologue of an ADD query")
void addMustKeepItsPrologue() {
QueryAst ast = newParserDefault().parse(
"PREFIX ex: <http://example.org/> ADD GRAPH ex:s TO GRAPH ex:d");
UpdateRequestAst update = assertInstanceOf(UpdateRequestAst.class, ast);
assertEquals(1, update.operations().size());
assertInstanceOf(AddRequestAst.class, update.operations().getFirst());
assertNotNull(update.prologue());
assertEquals(1, update.prologue().prefixDeclarations().size());
}

@Test
@DisplayName("ADD without TO keyword should throw a syntax exception")
void addMissingToShouldFail() {
assertThrows(QuerySyntaxException.class,
() -> newParserDefault().parse("ADD GRAPH <http://example.org/s>"));
}

@Test
@DisplayName("ADD without source should throw a syntax exception")
void addMissingSourceShouldFail() {
assertThrows(QuerySyntaxException.class,
() -> newParserDefault().parse("ADD TO GRAPH <http://example.org/d>"));
}
}
Loading