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 @@ -137,6 +137,11 @@ private record ServiceEntry(int groupDepth, TermAst endpoint, boolean silent) {}
private final Set<IriAst> datasetDefaultGraphs = new LinkedHashSet<>();
private final Set<IriAst> datasetNamedGraphs = new LinkedHashSet<>();

/**
* VALUES clause (agglomerate all VALUES declarations in the query)
*/
private final List<ValueMappingAst> values = new ArrayList<>();

/** SELECT DISTINCT / REDUCED. Set by SelectQueryFeature when parsing SELECT (DISTINCT | REDUCED)? ... */
private boolean distinct;
private boolean reduced;
Expand Down Expand Up @@ -274,12 +279,15 @@ public void exitSelectQuery() {
new IriAst(baseUri)
);

ValuesAst valuesClause = new ValuesAst(this.values);

SelectQueryAst selectQueryAst = new SelectQueryAst(
frame.projection,
datasetClauseAst,
frame.whereClause,
buildSolutionModifier(frame),
prologueAst
prologueAst,
valuesClause
);

/*
Expand Down Expand Up @@ -562,14 +570,18 @@ public void addTriple(TermAst s, TermAst p, TermAst o) {
// --- Filters ---

/**
* Exits a Filter, builds FilterAst and adds it to the current group
* Exits a Filter and adds it to the current group
*/
public void addFilter(FilterAst filter) {
if (this.hasCurrentGroup()) {
this.currentGroup().add(filter);
}
}

public void addValues(List<ValueMappingAst> mappings) {
this.values.addAll(mappings);
}

/**
* Adds {@code BIND(expression AS ?var)} to the current group. The variable must not already be
* visible from earlier patterns in the same group (SPARQL 1.1 scoping).
Expand Down Expand Up @@ -705,11 +717,12 @@ public QueryAst getResult() {
}
DatasetClauseAst datasetClauseAst = new DatasetClauseAst(datasetDefaultGraphs, datasetNamedGraphs);
QueryPrologueAst prologueAst = new QueryPrologueAst(List.copyOf(prefixDeclarations), new IriAst(baseUri));
ValuesAst valuesClause = new ValuesAst(this.values);
return switch (this.queryType) {
case ASK -> buildAskQueryAst(datasetClauseAst, prologueAst);
case CONSTRUCT -> buildConstructQueryAst(datasetClauseAst, prologueAst);
case DESCRIBE -> buildDescribeQueryAst(datasetClauseAst, prologueAst);
case SELECT -> buildSelectQueryAst(datasetClauseAst, prologueAst);
case ASK -> buildAskQueryAst(datasetClauseAst, prologueAst, valuesClause);
case CONSTRUCT -> buildConstructQueryAst(datasetClauseAst, prologueAst, valuesClause);
case DESCRIBE -> buildDescribeQueryAst(datasetClauseAst, prologueAst, valuesClause);
case SELECT -> buildSelectQueryAst(datasetClauseAst, prologueAst, valuesClause);
case UNDEFINED -> throw new QueryEvaluationException("Could not determine the type of query during parsing");
};
}
Expand Down Expand Up @@ -743,51 +756,54 @@ private void ensureNoOpenBgp() {
/**
* Builds the AST for ASK queries.
*/
private AskQueryAst buildAskQueryAst(DatasetClauseAst datasetClauseAst, QueryPrologueAst prologue) {
return new AskQueryAst(datasetClauseAst, whereClause, buildSolutionModifier(), prologue);
private AskQueryAst buildAskQueryAst(DatasetClauseAst datasetClauseAst, QueryPrologueAst prologue, ValuesAst valuesClause) {
return new AskQueryAst(datasetClauseAst, whereClause, buildSolutionModifier(), prologue, valuesClause);
}

/**
* Builds the AST for SELECT queries.
*/
private SelectQueryAst buildSelectQueryAst(DatasetClauseAst datasetClauseAst, QueryPrologueAst prologue) {
private SelectQueryAst buildSelectQueryAst(DatasetClauseAst datasetClauseAst, QueryPrologueAst prologue, ValuesAst valuesClause) {
if (hasCurrentSelect()) {
SelectFrame frame = getCurrentSelectFrame();
return new SelectQueryAst(
frame.projection,
datasetClauseAst,
frame.whereClause,
buildSolutionModifier(frame),
prologue
prologue,
valuesClause
);
}
return new SelectQueryAst(projection, datasetClauseAst, whereClause, buildSolutionModifier(), prologue);
return new SelectQueryAst(projection, datasetClauseAst, whereClause, buildSolutionModifier(), prologue, valuesClause);
}

/**
* Builds the AST for DESCRIBE queries.
*/
private DescribeQueryAst buildDescribeQueryAst(DatasetClauseAst datasetClauseAst, QueryPrologueAst prologue) {
private DescribeQueryAst buildDescribeQueryAst(DatasetClauseAst datasetClauseAst, QueryPrologueAst prologue, ValuesAst valuesClause) {
// TODO #306: validate variable scope for DESCRIBE modifiers when DescribeQueryAst carries them.
return new DescribeQueryAst(
datasetClauseAst,
describeResources,
whereClause,
buildSolutionModifier(),
prologue);
prologue,
valuesClause);
}

/**
* Builds the AST for CONSTRUCT queries.
*/
private ConstructQueryAst buildConstructQueryAst(DatasetClauseAst datasetClauseAst, QueryPrologueAst prologue) {
private ConstructQueryAst buildConstructQueryAst(DatasetClauseAst datasetClauseAst, QueryPrologueAst prologue, ValuesAst valuesClause) {
// TODO #306: validate variable scope for CONSTRUCT modifiers when ConstructQueryAst carries them.
return new ConstructQueryAst(
constructTemplate != null ? constructTemplate : new ConstructTemplateAst(List.of()),
datasetClauseAst,
whereClause,
buildSolutionModifier(),
prologue);
prologue,
valuesClause);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,4 +242,24 @@ public void enterNotExistsFunc(SparqlParser.NotExistsFuncContext ctx) {
public void exitBind(SparqlParser.BindContext ctx) {
for (var d : delegates) d.exitBind(ctx);
}

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

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

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

@Override
public void exitInlineData(SparqlParser.InlineDataContext ctx) {
for (var d : delegates) d.exitInlineData(ctx);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,7 @@
import fr.inria.corese.core.next.query.api.sparql.options.SparqlAstError;
import fr.inria.corese.core.next.query.api.validation.QueryDiagnostic;
import fr.inria.corese.core.next.query.api.validation.QueryValidationResult;
import fr.inria.corese.core.next.query.impl.parser.listener.AskQueryFeature;
import fr.inria.corese.core.next.query.impl.parser.listener.BgpFeature;
import fr.inria.corese.core.next.query.impl.parser.listener.BindFeature;
import fr.inria.corese.core.next.query.impl.parser.listener.ConstructQueryFeature;
import fr.inria.corese.core.next.query.impl.parser.listener.DatasetClauseFeature;
import fr.inria.corese.core.next.query.impl.parser.listener.DescribeQueryFeature;
import fr.inria.corese.core.next.query.impl.parser.listener.FilterFeature;
import fr.inria.corese.core.next.query.impl.parser.listener.HavingFeature;
import fr.inria.corese.core.next.query.impl.parser.listener.MinusFeature;
import fr.inria.corese.core.next.query.impl.parser.listener.PrologueFeature;
import fr.inria.corese.core.next.query.impl.parser.listener.SelectQueryFeature;
import fr.inria.corese.core.next.query.impl.parser.listener.ServiceFeature;
import fr.inria.corese.core.next.query.impl.parser.listener.SolutionModifierFeature;
import fr.inria.corese.core.next.query.impl.parser.listener.UnionFeature;
import fr.inria.corese.core.next.query.impl.parser.listener.*;
import fr.inria.corese.core.next.query.impl.parser.semantic.validator.SparqlQuerySemanticValidator;
import fr.inria.corese.core.next.query.impl.sparql.ast.QueryAst;
import org.antlr.v4.runtime.BailErrorStrategy;
Expand Down Expand Up @@ -139,7 +126,8 @@ private QueryAst buildAst(ParseTree tree, SparqlParserOptions options) {
new DatasetClauseFeature(builder),
new PrologueFeature(builder),
new BindFeature(builder),
new ServiceFeature(builder)
new ServiceFeature(builder),
new ValuesFeature(builder)
));

ParseTreeWalker walker = new ParseTreeWalker();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@
*/
public class FilterFeature extends AbstractSparqlFeature {

private static final Logger logger = LoggerFactory.getLogger(FilterFeature.class);

public FilterFeature(SparqlAstBuilder builder) {
super(builder);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
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.api.exception.QuerySyntaxException;
import fr.inria.corese.core.next.query.impl.parser.SparqlAstBuilder;
import fr.inria.corese.core.next.query.impl.sparql.ast.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

/**
* Capture parsing of VALUES. VALUES can be declared both in the WHERE clause (through {@code inlineData}) and outside the query (through {@code valuesClause}).
*/
public class ValuesFeature extends AbstractSparqlFeature {

public ValuesFeature(SparqlAstBuilder builder) {
super(builder);
}

@Override
public void exitValuesClause(SparqlParser.ValuesClauseContext ctx) {
if(ctx.dataBlock() != null) {
processDataBlock(ctx.dataBlock());
}
}

@Override
public void exitInlineData(SparqlParser.InlineDataContext ctx) {
if(ctx.dataBlock() != null) {
processDataBlock(ctx.dataBlock());
}
}

private void processDataBlock(SparqlParser.DataBlockContext ctx) {
if(ctx.inlineDataOneVar() != null) {
processInlineDataOneVar(ctx.inlineDataOneVar());
} else if (ctx.inlineDataFull() != null) {
processInlineDataFull(ctx.inlineDataFull());
}
}

private void processInlineDataOneVar(SparqlParser.InlineDataOneVarContext ctx) {
if(ctx.var_() != null) {
VarAst varAst = (VarAst) this.builder().termFromVar(ctx.var_());
if(!Objects.equals(varAst.name(), "()")) {
List<ValueMappingAst> mappingAstList = new ArrayList<>();
if(ctx.dataBlockValue() != null) {
ctx.dataBlockValue().forEach(dataBlockValueContext -> {
Map<VarAst, TermAst> valueMap = termAstFromDataBlockValues(List.of(varAst), List.of(dataBlockValueContext));
// Each dataBlockValue is a solution
mappingAstList.add(new ValueMappingAst(valueMap));
});
this.builder().addValues(mappingAstList);
}
}
} else if(ctx.var_() == null && ctx.dataBlockValue() != null) {
throw new QuerySyntaxException("Missing variable for solution mapping in VALUES clause");
}
}

/**
*
* @param dataBlockValueList
* @return A list of terms or null for UNDEF values
*/
private Map<VarAst, TermAst> termAstFromDataBlockValues(List<VarAst> variables, List<SparqlParser.DataBlockValueContext> dataBlockValueList) {
if(variables.size() != dataBlockValueList.size()) {
throw new QuerySyntaxException("VALUE solutions should have a value for every variable and at least a variable for a solution.");
}
Map<VarAst, TermAst> valuesList = new HashMap<>();
for(int varNum = 0; varNum < variables.size(); varNum++) {
VarAst variable = variables.get(varNum);
SparqlParser.DataBlockValueContext dataBlockValueContext = dataBlockValueList.get(varNum);
if(dataBlockValueContext.iriRef() != null) {
valuesList.put(variable, this.builder().termFromIriRef(dataBlockValueContext.iriRef()));
} else if(dataBlockValueContext.rdfLiteral() != null) {
valuesList.put(variable, this.builder().termFromRdfLiteral(dataBlockValueContext.rdfLiteral()));
} else if(dataBlockValueContext.numericLiteral() != null) {
valuesList.put(variable, this.builder().termFromNumericLiteral(dataBlockValueContext.numericLiteral()));
} else if(dataBlockValueContext.booleanLiteral() != null) {
valuesList.put(variable, this.builder().termFromBooleanLiteral(dataBlockValueContext.booleanLiteral()));
} else if(dataBlockValueContext.UNDEF() != null) {
valuesList.put(variable, null);
}
}
dataBlockValueList.forEach(dataBlockValueContext -> {
});
return valuesList;
}

private void processInlineDataFull(SparqlParser.InlineDataFullContext ctx) {
List<VarAst> varList = new ArrayList<>();
if(ctx.var_() != null) {
ctx.var_().forEach(varContext -> {
VarAst varAst = (VarAst) this.builder().termFromVar(varContext);
if(!Objects.equals(varAst.name(), "()")) {
varList.add(varAst);
}
});
}
if(!varList.isEmpty()) {
List<ValueMappingAst> valuesList = new ArrayList<>();
if(ctx.dataBlockValues() != null) { // Each dataBlockValues is a solution
ctx.dataBlockValues().forEach(dataBlockValuesContext -> { // Each dataBlockValue is a value for a variable in a solution
if(dataBlockValuesContext.dataBlockValue() != null) {
Map<VarAst, TermAst> valueList = termAstFromDataBlockValues(varList, dataBlockValuesContext.dataBlockValue());
valuesList.add(new ValueMappingAst(valueList));
}
});
}
this.builder().addValues(valuesList);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package fr.inria.corese.core.next.query.impl.parser.semantic.rule;

import fr.inria.corese.core.next.query.api.validation.QueryDiagnostic;

public abstract class AbstractSemanticValidationRule implements SemanticValidationRule {

protected abstract String getDiagnosticSource();

protected QueryDiagnostic buildOutOfScopeDiagnostic(String variableName, String clause) {
return new QueryDiagnostic(
QueryDiagnostic.Kind.SEMANTIC_ERROR,
QueryDiagnostic.Severity.ERROR,
"Variable ?" + variableName + " used in " + clause + " is not visible in WHERE clause",
-1,
-1,
"?" + variableName,
getDiagnosticSource());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,17 @@
* Validates that variables referenced from ORDER BY are visible from the
* WHERE clause scope.
*/
public final class OrderByScopeValidationRule implements SemanticValidationRule {
public final class OrderByScopeValidationRule extends AbstractSemanticValidationRule {

private static final String DIAGNOSTIC_SOURCE = "OrderByScopeValidationRule";

private final VariableScopeAnalyzer variableScopeAnalyzer = new VariableScopeAnalyzer();

@Override
protected String getDiagnosticSource() {
return DIAGNOSTIC_SOURCE;
}

@Override
public List<QueryDiagnostic> validate(QueryAst queryAst) {
return switch (queryAst) {
Expand All @@ -44,7 +49,7 @@ public List<QueryDiagnostic> validate(QueryAst queryAst) {
}

private List<QueryDiagnostic> validateSelectQuery(SelectQueryAst queryAst) {
Set<String> visibleVariables = variableScopeAnalyzer.collectVisibleVariables(queryAst.whereClause());
Set<String> visibleVariables = variableScopeAnalyzer.collectVisibleVariables(queryAst);
List<QueryDiagnostic> diagnostics = new ArrayList<>();
validateOrderVariables(
collectOrderByAvailableVariables(queryAst.projection(), visibleVariables),
Expand Down Expand Up @@ -99,15 +104,4 @@ private void validateOrderVariables(
}
}
}

private QueryDiagnostic buildOutOfScopeDiagnostic(String variableName, String clause) {
return new QueryDiagnostic(
QueryDiagnostic.Kind.SEMANTIC_ERROR,
QueryDiagnostic.Severity.ERROR,
"Variable ?" + variableName + " used in " + clause + " is not visible in WHERE clause",
-1,
-1,
"?" + variableName,
DIAGNOSTIC_SOURCE);
}
}
Loading
Loading