diff --git a/engines/parser-sparql-1-2/lib/Parser.ts b/engines/parser-sparql-1-2/lib/Parser.ts index eb811f83..12a0eab5 100644 --- a/engines/parser-sparql-1-2/lib/Parser.ts +++ b/engines/parser-sparql-1-2/lib/Parser.ts @@ -254,6 +254,7 @@ export const sparql12ParserBuilder = ParserBuilder.create(sparql11ParserBuilder) S12.buildInPredicate, S12.buildInObject, ) + .patchRule(S12.selectQuery) .patchRule(S12.dataBlockValue) .patchRule(S12.triplesSameSubject) .patchRule(S12.triplesSameSubjectPath) diff --git a/packages/rules-sparql-1-1/lib/index.ts b/packages/rules-sparql-1-1/lib/index.ts index 3e8286be..0578f5c4 100644 --- a/packages/rules-sparql-1-1/lib/index.ts +++ b/packages/rules-sparql-1-1/lib/index.ts @@ -5,5 +5,8 @@ export * from './Sparql11types.js'; export * from './MinimalSparqlParser.js'; export * from './sparql11HelperTypes.js'; export * from './astFactory.js'; +// TODO: deprecate this export export * from './validation/validators.js'; + +export * as validation from './validation/validators.js'; export * from './utils.js'; diff --git a/packages/rules-sparql-1-1/lib/validation/validators.ts b/packages/rules-sparql-1-1/lib/validation/validators.ts index 755d6a7d..f71b118a 100644 --- a/packages/rules-sparql-1-1/lib/validation/validators.ts +++ b/packages/rules-sparql-1-1/lib/validation/validators.ts @@ -19,7 +19,7 @@ const transformer = new AstTransformer(); /** * Get all 'aggregate' rules from an expression */ -function getAggregatesOfExpression(expression: Expression): ExpressionAggregate[] { +export function getAggregatesOfExpression(expression: Expression): ExpressionAggregate[] { if (F.isExpressionAggregate(expression)) { return [ expression ]; } @@ -36,7 +36,7 @@ function getAggregatesOfExpression(expression: Expression): ExpressionAggregate[ /** * Return the variable value id of an expression if bounded */ -function getExpressionId(expression: SolutionModifierGroupBind | Expression | TermVariable): string | undefined { +export function getExpressionId(expression: SolutionModifierGroupBind | Expression | TermVariable): string | undefined { // Check if grouping if (F.isTerm(expression) && F.isTermVariable(expression)) { return expression.value; @@ -53,7 +53,7 @@ function getExpressionId(expression: SolutionModifierGroupBind | Expression | Te /** * Get all variables used in an expression */ -function getVariablesFromExpression(expression: Expression, variables: Set): void { +export function getVariablesFromExpression(expression: Expression, variables: Set): void { if (F.isExpressionOperator(expression)) { for (const expr of expression.args) { getVariablesFromExpression(expr, variables); diff --git a/packages/rules-sparql-1-2/lib/grammar.ts b/packages/rules-sparql-1-2/lib/grammar.ts index be6be180..ccac9e59 100644 --- a/packages/rules-sparql-1-2/lib/grammar.ts +++ b/packages/rules-sparql-1-2/lib/grammar.ts @@ -18,6 +18,7 @@ import type { GraphNode, GraphTerm, PatternBgp, + QuerySelect, Term, TermBlank, TermIri, @@ -28,7 +29,7 @@ import type { TripleCollectionReifiedTriple, TripleNesting, } from './sparql12Types.js'; -import { langTagHasCorrectRange } from './validators.js'; +import { langTagHasCorrectRange, queryProjectionIsGood } from './validators.js'; /** *[[7]](https://www.w3.org/TR/sparql12-query/#rVersionDecl) @@ -48,6 +49,42 @@ export const versionDecl: SparqlRule<'versionDecl', ContextDefinitionVersion> = }, }; +/** + * [[9]](https://www.w3.org/TR/sparql12-query/#rSelectQuery) + * (Validator has changed: https://github.com/w3c/sparql-query/pull/380) + */ +export const selectQuery: SparqlGrammarRule<'selectQuery', Omit> = { + name: 'selectQuery', + impl: ({ ACTION, SUBRULE }) => (C) => { + const selectVal = SUBRULE(S11.selectClause); + const from = SUBRULE(S11.datasetClauseStar); + const where = SUBRULE(S11.whereClause); + const modifiers = SUBRULE(S11.solutionModifier); + + return ACTION(() => { + const ret = { + subType: 'select', + where: where.val, + solutionModifiers: modifiers, + datasets: from, + ...selectVal.val, + loc: C.astFactory.sourceLocation( + selectVal, + where, + modifiers.group, + modifiers.having, + modifiers.order, + modifiers.limitOffset, + ), + } satisfies RuleDefReturn; + if (!C.skipValidation) { + queryProjectionIsGood(ret); + } + return ret; + }); + }, +}; + /** * [[8]](https://www.w3.org/TR/sparql12-query/#rVersionSpecifier) */ diff --git a/packages/rules-sparql-1-2/lib/index.ts b/packages/rules-sparql-1-2/lib/index.ts index 18807554..4f5b0ca4 100644 --- a/packages/rules-sparql-1-2/lib/index.ts +++ b/packages/rules-sparql-1-2/lib/index.ts @@ -1,7 +1,19 @@ +import { + validation as validation11, +} from '@traqula/rules-sparql-1-1'; +import * as validation12 from './validators.js'; + export * as gram from './grammar.js'; export * as lex from './lexer.js'; export * from './sparql12Types.js'; export * from './sparql12HelperTypes.js'; export * from './parserUtils.js'; export * from './AstFactory.js'; +// TODO: deprecate this export export * from './validators.js'; + +type OverriddenKeys = keyof typeof validation11 & keyof typeof validation12; +export const validation = & typeof validation12> { + ...validation11, + ...validation12, +}; diff --git a/packages/rules-sparql-1-2/lib/validators.ts b/packages/rules-sparql-1-2/lib/validators.ts index 8c83c090..006a2d1b 100644 --- a/packages/rules-sparql-1-2/lib/validators.ts +++ b/packages/rules-sparql-1-2/lib/validators.ts @@ -1,10 +1,15 @@ +import { getAggregatesOfExpression, getExpressionId, getVariablesFromExpression } from '@traqula/rules-sparql-1-1'; +import type * as T11 from '@traqula/rules-sparql-1-1'; import { AstFactory } from './AstFactory.js'; import type { Path, Pattern, + PatternBind, + QuerySelect, SparqlQuery, Term, TermLiteral, + TermVariable, TripleCollection, TripleNesting, Wildcard, @@ -104,3 +109,83 @@ export function findPatternBoundedVars( } } } + +/** + * Verify that the projected variables (select head) are allowed: + * - no group-by on select * + * - if group-by, selected variables need to be collected by the group-by + * - 'select ?var as ?other', ?other cannot be in scope + */ +export function queryProjectionIsGood(query: Pick): void { + // NoGroupByOnWildcardSelect + if (query.variables.length === 1 && F.isWildcard(query.variables[0])) { + if (query.solutionModifiers.group !== undefined) { + throw new Error('GROUP BY not allowed with wildcard'); + } + return; + } + + // CannotProjectUngroupedVars - can be skipped if `SELECT *` + // Check for projection of ungrouped variable + // Check can be skipped in case of wildcard select. + const variables = > query.variables; + const hasCountAggregate = variables.flatMap( + varVal => F.isTerm(varVal) ? [] : getAggregatesOfExpression( varVal.expression), + ).some(agg => agg.aggregation === 'count' && !agg.expression.some(arg => F.isWildcard(arg))); + const groupBy = query.solutionModifiers.group; + if (hasCountAggregate || groupBy) { + // We have to check whether + // 1. Variables used in projection are usable given the group by clause + // 2. A selectCount will create an implicit group by clause. + // Variables bound by preceding (expr AS ?var) expressions are in scope for later expressions. + const asBoundVars = new Set(); + for (const selectVar of variables) { + if (F.isTerm(selectVar)) { + if (!groupBy || !groupBy.groupings.map(groupvar => + getExpressionId( groupvar)) + .includes((getExpressionId(selectVar)))) { + throw new Error('Variable not allowed in projection'); + } + } else if (getAggregatesOfExpression( selectVar.expression).length === 0) { + // Current value binding does not use aggregates + const usedvars = new Set(); + getVariablesFromExpression( selectVar.expression, usedvars); + for (const usedvar of usedvars) { + // If the var is created within the select, it is fine. + if (asBoundVars.has(usedvar)) { + continue; + } + if (!groupBy || !groupBy.groupings.map(groupVar => + getExpressionId(groupVar)).includes(usedvar)) { + throw new Error(`Use of ungrouped variable in projection of operation (?${usedvar})`); + } + } + } + if (!F.isTerm(selectVar)) { + // Register a var is created by a bind + asBoundVars.add(selectVar.variable.value); + } + } + } + + // NOTE 12: Check if id of each AS-selected column is not yet bound by subquery + const subqueries = query.where.patterns.filter(pattern => pattern.type === 'query'); + if (subqueries.length > 0) { + const selectBoundedVars = new Set(); + for (const variable of variables) { + if ('variable' in variable) { + selectBoundedVars.add(variable.variable.value); + } + } + + // Look at in scope variables + const vars = subqueries.flatMap(sub => sub.variables) + .map(v => F.isTerm(v) ? v.value : (F.isWildcard(v) ? '*' : v.variable.value)); + const subqueryIds = new Set(vars); + for (const selectedVarId of selectBoundedVars) { + if (subqueryIds.has(selectedVarId)) { + throw new Error(`Target id of 'AS' (?${selectedVarId}) already used in subquery`); + } + } + } +} diff --git a/packages/test-utils/statics/algebra/algebra-blank-to-var/sparql12/select-variable-reuse.json b/packages/test-utils/statics/algebra/algebra-blank-to-var/sparql12/select-variable-reuse.json new file mode 100644 index 00000000..87c91b0c --- /dev/null +++ b/packages/test-utils/statics/algebra/algebra-blank-to-var/sparql12/select-variable-reuse.json @@ -0,0 +1,137 @@ +{ + "type": "project", + "input": { + "type": "extend", + "input": { + "type": "extend", + "input": { + "type": "group", + "input": { + "type": "values", + "variables": [ + { + "termType": "Variable", + "value": "v" + } + ], + "bindings": [ + { + "v": { + "termType": "Literal", + "value": "0", + "datatype": { + "termType": "NamedNode", + "value": "http://www.w3.org/2001/XMLSchema#integer" + } + } + }, + { + "v": { + "termType": "Literal", + "value": "1", + "datatype": { + "termType": "NamedNode", + "value": "http://www.w3.org/2001/XMLSchema#integer" + } + } + }, + { + "v": { + "termType": "Literal", + "value": "2", + "datatype": { + "termType": "NamedNode", + "value": "http://www.w3.org/2001/XMLSchema#integer" + } + } + }, + { + "v": { + "termType": "Literal", + "value": "3", + "datatype": { + "termType": "NamedNode", + "value": "http://www.w3.org/2001/XMLSchema#integer" + } + } + } + ] + }, + "variables": [], + "aggregates": [ + { + "type": "expression", + "subType": "aggregate", + "aggregator": "count", + "expression": { + "type": "expression", + "subType": "term", + "term": { + "termType": "Variable", + "value": "v" + } + }, + "distinct": false, + "variable": { + "termType": "Variable", + "value": "var0" + } + } + ] + }, + "variable": { + "termType": "Variable", + "value": "count" + }, + "expression": { + "type": "expression", + "subType": "term", + "term": { + "termType": "Variable", + "value": "var0" + } + } + }, + "variable": { + "termType": "Variable", + "value": "countPlusOne" + }, + "expression": { + "type": "expression", + "subType": "operator", + "operator": "+", + "args": [ + { + "type": "expression", + "subType": "term", + "term": { + "termType": "Variable", + "value": "count" + } + }, + { + "type": "expression", + "subType": "term", + "term": { + "termType": "Literal", + "value": "1", + "datatype": { + "termType": "NamedNode", + "value": "http://www.w3.org/2001/XMLSchema#integer" + } + } + } + ] + } + }, + "variables": [ + { + "termType": "Variable", + "value": "count" + }, + { + "termType": "Variable", + "value": "countPlusOne" + } + ] +} diff --git a/packages/test-utils/statics/algebra/algebra/sparql12/select-variable-reuse.json b/packages/test-utils/statics/algebra/algebra/sparql12/select-variable-reuse.json new file mode 100644 index 00000000..87c91b0c --- /dev/null +++ b/packages/test-utils/statics/algebra/algebra/sparql12/select-variable-reuse.json @@ -0,0 +1,137 @@ +{ + "type": "project", + "input": { + "type": "extend", + "input": { + "type": "extend", + "input": { + "type": "group", + "input": { + "type": "values", + "variables": [ + { + "termType": "Variable", + "value": "v" + } + ], + "bindings": [ + { + "v": { + "termType": "Literal", + "value": "0", + "datatype": { + "termType": "NamedNode", + "value": "http://www.w3.org/2001/XMLSchema#integer" + } + } + }, + { + "v": { + "termType": "Literal", + "value": "1", + "datatype": { + "termType": "NamedNode", + "value": "http://www.w3.org/2001/XMLSchema#integer" + } + } + }, + { + "v": { + "termType": "Literal", + "value": "2", + "datatype": { + "termType": "NamedNode", + "value": "http://www.w3.org/2001/XMLSchema#integer" + } + } + }, + { + "v": { + "termType": "Literal", + "value": "3", + "datatype": { + "termType": "NamedNode", + "value": "http://www.w3.org/2001/XMLSchema#integer" + } + } + } + ] + }, + "variables": [], + "aggregates": [ + { + "type": "expression", + "subType": "aggregate", + "aggregator": "count", + "expression": { + "type": "expression", + "subType": "term", + "term": { + "termType": "Variable", + "value": "v" + } + }, + "distinct": false, + "variable": { + "termType": "Variable", + "value": "var0" + } + } + ] + }, + "variable": { + "termType": "Variable", + "value": "count" + }, + "expression": { + "type": "expression", + "subType": "term", + "term": { + "termType": "Variable", + "value": "var0" + } + } + }, + "variable": { + "termType": "Variable", + "value": "countPlusOne" + }, + "expression": { + "type": "expression", + "subType": "operator", + "operator": "+", + "args": [ + { + "type": "expression", + "subType": "term", + "term": { + "termType": "Variable", + "value": "count" + } + }, + { + "type": "expression", + "subType": "term", + "term": { + "termType": "Literal", + "value": "1", + "datatype": { + "termType": "NamedNode", + "value": "http://www.w3.org/2001/XMLSchema#integer" + } + } + } + ] + } + }, + "variables": [ + { + "termType": "Variable", + "value": "count" + }, + { + "termType": "Variable", + "value": "countPlusOne" + } + ] +} diff --git a/packages/test-utils/statics/algebra/canonical-sparql/base/sparql12/select-variable-reuse.sparql b/packages/test-utils/statics/algebra/canonical-sparql/base/sparql12/select-variable-reuse.sparql new file mode 100644 index 00000000..86119c9f --- /dev/null +++ b/packages/test-utils/statics/algebra/canonical-sparql/base/sparql12/select-variable-reuse.sparql @@ -0,0 +1,8 @@ +SELECT ( COUNT( ?v ) AS ?count ) ( ( ?count + "1"^^ ) AS ?countPlusOne ) WHERE { + VALUES ?v { + "0"^^ + "1"^^ + "2"^^ + "3"^^ + } +} \ No newline at end of file diff --git a/packages/test-utils/statics/algebra/canonical-sparql/blank-to-var/sparql12/select-variable-reuse.sparql b/packages/test-utils/statics/algebra/canonical-sparql/blank-to-var/sparql12/select-variable-reuse.sparql new file mode 100644 index 00000000..86119c9f --- /dev/null +++ b/packages/test-utils/statics/algebra/canonical-sparql/blank-to-var/sparql12/select-variable-reuse.sparql @@ -0,0 +1,8 @@ +SELECT ( COUNT( ?v ) AS ?count ) ( ( ?count + "1"^^ ) AS ?countPlusOne ) WHERE { + VALUES ?v { + "0"^^ + "1"^^ + "2"^^ + "3"^^ + } +} \ No newline at end of file diff --git a/packages/test-utils/statics/algebra/sparql/sparql12/select-variable-reuse.sparql b/packages/test-utils/statics/algebra/sparql/sparql12/select-variable-reuse.sparql new file mode 100644 index 00000000..3709bb6e --- /dev/null +++ b/packages/test-utils/statics/algebra/sparql/sparql12/select-variable-reuse.sparql @@ -0,0 +1,3 @@ +SELECT (COUNT(?v) AS ?count) (?count + 1 AS ?countPlusOne) WHERE { + VALUES ?v { 0 1 2 3 } +} diff --git a/packages/test-utils/statics/ast/ast-source-tracked/sparql-1-1/note-as-var-not-in-term-subquery.json b/packages/test-utils/statics/ast/ast-source-tracked/sparql-1-1/note-as-var-not-in-term-subquery.json new file mode 100644 index 00000000..002af35f --- /dev/null +++ b/packages/test-utils/statics/ast/ast-source-tracked/sparql-1-1/note-as-var-not-in-term-subquery.json @@ -0,0 +1,166 @@ +{ + "context": [], + "subType": "select", + "where": { + "type": "pattern", + "subType": "group", + "patterns": [ + { + "type": "query", + "subType": "select", + "where": { + "type": "pattern", + "subType": "group", + "patterns": [ + { + "type": "pattern", + "subType": "bgp", + "triples": [ + { + "type": "triple", + "subject": { + "type": "term", + "subType": "variable", + "value": "y", + "loc": { + "sourceLocationType": "source", + "start": 43, + "end": 45 + } + }, + "predicate": { + "type": "term", + "subType": "variable", + "value": "p", + "loc": { + "sourceLocationType": "source", + "start": 46, + "end": 48 + } + }, + "object": { + "type": "term", + "subType": "variable", + "value": "o", + "loc": { + "sourceLocationType": "source", + "start": 49, + "end": 51 + } + }, + "loc": { + "sourceLocationType": "source", + "start": 43, + "end": 51 + } + } + ], + "loc": { + "sourceLocationType": "source", + "start": 43, + "end": 51 + } + } + ], + "loc": { + "sourceLocationType": "source", + "start": 41, + "end": 53 + } + }, + "datasets": { + "type": "datasetClauses", + "clauses": [], + "loc": { + "sourceLocationType": "noMaterialize" + } + }, + "context": [], + "solutionModifiers": {}, + "variables": [ + { + "type": "term", + "subType": "variable", + "value": "y", + "loc": { + "sourceLocationType": "source", + "start": 32, + "end": 34 + } + } + ], + "loc": { + "sourceLocationType": "source", + "start": 25, + "end": 53 + } + } + ], + "loc": { + "sourceLocationType": "source", + "start": 23, + "end": 55 + } + }, + "solutionModifiers": {}, + "datasets": { + "type": "datasetClauses", + "clauses": [], + "loc": { + "sourceLocationType": "noMaterialize" + } + }, + "variables": [ + { + "type": "pattern", + "subType": "bind", + "expression": { + "type": "term", + "subType": "literal", + "value": "1", + "langOrIri": { + "type": "term", + "subType": "namedNode", + "value": "http://www.w3.org/2001/XMLSchema#integer", + "loc": { + "sourceLocationType": "noMaterialize" + } + }, + "loc": { + "sourceLocationType": "source", + "start": 8, + "end": 9 + } + }, + "variable": { + "type": "term", + "subType": "variable", + "value": "x", + "loc": { + "sourceLocationType": "source", + "start": 13, + "end": 15 + } + }, + "loc": { + "sourceLocationType": "source", + "start": 7, + "end": 16 + } + } + ], + "loc": { + "sourceLocationType": "inlinedSource", + "newSource": "SELECT (1 AS ?x) WHERE { SELECT ?y WHERE { ?y ?p ?o } }\n", + "start": 0, + "end": 9007199254740991, + "loc": { + "sourceLocationType": "source", + "start": 0, + "end": 55 + }, + "startOnNew": 0, + "endOnNew": 55 + }, + "type": "query" +} diff --git a/packages/test-utils/statics/ast/ast-source-tracked/sparql-1-1/note-as-var-not-in-wildcard-subquery.json b/packages/test-utils/statics/ast/ast-source-tracked/sparql-1-1/note-as-var-not-in-wildcard-subquery.json new file mode 100644 index 00000000..8412d147 --- /dev/null +++ b/packages/test-utils/statics/ast/ast-source-tracked/sparql-1-1/note-as-var-not-in-wildcard-subquery.json @@ -0,0 +1,164 @@ +{ + "context": [], + "subType": "select", + "where": { + "type": "pattern", + "subType": "group", + "patterns": [ + { + "type": "query", + "subType": "select", + "where": { + "type": "pattern", + "subType": "group", + "patterns": [ + { + "type": "pattern", + "subType": "bgp", + "triples": [ + { + "type": "triple", + "subject": { + "type": "term", + "subType": "variable", + "value": "s", + "loc": { + "sourceLocationType": "source", + "start": 42, + "end": 44 + } + }, + "predicate": { + "type": "term", + "subType": "variable", + "value": "p", + "loc": { + "sourceLocationType": "source", + "start": 45, + "end": 47 + } + }, + "object": { + "type": "term", + "subType": "variable", + "value": "o", + "loc": { + "sourceLocationType": "source", + "start": 48, + "end": 50 + } + }, + "loc": { + "sourceLocationType": "source", + "start": 42, + "end": 50 + } + } + ], + "loc": { + "sourceLocationType": "source", + "start": 42, + "end": 50 + } + } + ], + "loc": { + "sourceLocationType": "source", + "start": 40, + "end": 52 + } + }, + "datasets": { + "type": "datasetClauses", + "clauses": [], + "loc": { + "sourceLocationType": "noMaterialize" + } + }, + "context": [], + "solutionModifiers": {}, + "variables": [ + { + "type": "wildcard", + "loc": { + "sourceLocationType": "source", + "start": 32, + "end": 33 + } + } + ], + "loc": { + "sourceLocationType": "source", + "start": 25, + "end": 52 + } + } + ], + "loc": { + "sourceLocationType": "source", + "start": 23, + "end": 54 + } + }, + "solutionModifiers": {}, + "datasets": { + "type": "datasetClauses", + "clauses": [], + "loc": { + "sourceLocationType": "noMaterialize" + } + }, + "variables": [ + { + "type": "pattern", + "subType": "bind", + "expression": { + "type": "term", + "subType": "literal", + "value": "1", + "langOrIri": { + "type": "term", + "subType": "namedNode", + "value": "http://www.w3.org/2001/XMLSchema#integer", + "loc": { + "sourceLocationType": "noMaterialize" + } + }, + "loc": { + "sourceLocationType": "source", + "start": 8, + "end": 9 + } + }, + "variable": { + "type": "term", + "subType": "variable", + "value": "x", + "loc": { + "sourceLocationType": "source", + "start": 13, + "end": 15 + } + }, + "loc": { + "sourceLocationType": "source", + "start": 7, + "end": 16 + } + } + ], + "loc": { + "sourceLocationType": "inlinedSource", + "newSource": "SELECT (1 AS ?x) WHERE { SELECT * WHERE { ?s ?p ?o } }\n", + "start": 0, + "end": 9007199254740991, + "loc": { + "sourceLocationType": "source", + "start": 0, + "end": 54 + }, + "startOnNew": 0, + "endOnNew": 54 + }, + "type": "query" +} diff --git a/packages/test-utils/statics/ast/ast-source-tracked/sparql-1-1/note-expression-grouped-var.json b/packages/test-utils/statics/ast/ast-source-tracked/sparql-1-1/note-expression-grouped-var.json new file mode 100644 index 00000000..f4bd10db --- /dev/null +++ b/packages/test-utils/statics/ast/ast-source-tracked/sparql-1-1/note-expression-grouped-var.json @@ -0,0 +1,169 @@ +{ + "context": [], + "subType": "select", + "where": { + "type": "pattern", + "subType": "group", + "patterns": [ + { + "type": "pattern", + "subType": "bgp", + "triples": [ + { + "type": "triple", + "subject": { + "type": "term", + "subType": "variable", + "value": "s", + "loc": { + "sourceLocationType": "source", + "start": 35, + "end": 37 + } + }, + "predicate": { + "type": "term", + "subType": "variable", + "value": "p", + "loc": { + "sourceLocationType": "source", + "start": 38, + "end": 40 + } + }, + "object": { + "type": "term", + "subType": "variable", + "value": "x", + "loc": { + "sourceLocationType": "source", + "start": 41, + "end": 43 + } + }, + "loc": { + "sourceLocationType": "source", + "start": 35, + "end": 43 + } + } + ], + "loc": { + "sourceLocationType": "source", + "start": 35, + "end": 43 + } + } + ], + "loc": { + "sourceLocationType": "source", + "start": 33, + "end": 45 + } + }, + "solutionModifiers": { + "group": { + "type": "solutionModifier", + "subType": "group", + "groupings": [ + { + "type": "term", + "subType": "variable", + "value": "x", + "loc": { + "sourceLocationType": "source", + "start": 55, + "end": 57 + } + } + ], + "loc": { + "sourceLocationType": "source", + "start": 46, + "end": 57 + } + } + }, + "datasets": { + "type": "datasetClauses", + "clauses": [], + "loc": { + "sourceLocationType": "noMaterialize" + } + }, + "variables": [ + { + "type": "pattern", + "subType": "bind", + "expression": { + "type": "expression", + "subType": "operation", + "operator": "+", + "args": [ + { + "type": "term", + "subType": "variable", + "value": "x", + "loc": { + "sourceLocationType": "source", + "start": 8, + "end": 10 + } + }, + { + "type": "term", + "subType": "literal", + "value": "1", + "langOrIri": { + "type": "term", + "subType": "namedNode", + "value": "http://www.w3.org/2001/XMLSchema#integer", + "loc": { + "sourceLocationType": "noMaterialize" + } + }, + "loc": { + "sourceLocationType": "source", + "start": 13, + "end": 14 + } + } + ], + "loc": { + "sourceLocationType": "source", + "start": 8, + "end": 14 + } + }, + "variable": { + "type": "term", + "subType": "variable", + "value": "result", + "loc": { + "sourceLocationType": "source", + "start": 18, + "end": 25 + } + }, + "loc": { + "sourceLocationType": "source", + "start": 7, + "end": 26 + } + } + ], + "loc": { + "sourceLocationType": "inlinedSource", + "newSource": "SELECT (?x + 1 AS ?result) WHERE { ?s ?p ?x } GROUP BY ?x\n", + "start": 0, + "end": 9007199254740991, + "loc": { + "sourceLocationType": "source", + "start": 0, + "end": 57 + }, + "startOnNew": 0, + "endOnNew": 57 + }, + "type": "query" +} diff --git a/packages/test-utils/statics/ast/ast-source-tracked/sparql-1-2/select-variable-reuse.json b/packages/test-utils/statics/ast/ast-source-tracked/sparql-1-2/select-variable-reuse.json new file mode 100644 index 00000000..e064cbd2 --- /dev/null +++ b/packages/test-utils/statics/ast/ast-source-tracked/sparql-1-2/select-variable-reuse.json @@ -0,0 +1,243 @@ +{ + "context": [], + "subType": "select", + "where": { + "type": "pattern", + "subType": "group", + "patterns": [ + { + "type": "pattern", + "subType": "values", + "variables": [ + { + "type": "term", + "subType": "variable", + "value": "v", + "loc": { + "sourceLocationType": "source", + "start": 76, + "end": 78 + } + } + ], + "values": [ + { + "v": { + "type": "term", + "subType": "literal", + "value": "0", + "langOrIri": { + "type": "term", + "subType": "namedNode", + "value": "http://www.w3.org/2001/XMLSchema#integer", + "loc": { + "sourceLocationType": "noMaterialize" + } + }, + "loc": { + "sourceLocationType": "source", + "start": 81, + "end": 82 + } + } + }, + { + "v": { + "type": "term", + "subType": "literal", + "value": "1", + "langOrIri": { + "type": "term", + "subType": "namedNode", + "value": "http://www.w3.org/2001/XMLSchema#integer", + "loc": { + "sourceLocationType": "noMaterialize" + } + }, + "loc": { + "sourceLocationType": "source", + "start": 83, + "end": 84 + } + } + }, + { + "v": { + "type": "term", + "subType": "literal", + "value": "2", + "langOrIri": { + "type": "term", + "subType": "namedNode", + "value": "http://www.w3.org/2001/XMLSchema#integer", + "loc": { + "sourceLocationType": "noMaterialize" + } + }, + "loc": { + "sourceLocationType": "source", + "start": 85, + "end": 86 + } + } + }, + { + "v": { + "type": "term", + "subType": "literal", + "value": "3", + "langOrIri": { + "type": "term", + "subType": "namedNode", + "value": "http://www.w3.org/2001/XMLSchema#integer", + "loc": { + "sourceLocationType": "noMaterialize" + } + }, + "loc": { + "sourceLocationType": "source", + "start": 87, + "end": 88 + } + } + } + ], + "loc": { + "sourceLocationType": "source", + "start": 69, + "end": 90 + } + } + ], + "loc": { + "sourceLocationType": "source", + "start": 65, + "end": 92 + } + }, + "solutionModifiers": {}, + "datasets": { + "type": "datasetClauses", + "clauses": [], + "loc": { + "sourceLocationType": "noMaterialize" + } + }, + "variables": [ + { + "type": "pattern", + "subType": "bind", + "expression": { + "type": "expression", + "subType": "aggregate", + "aggregation": "count", + "distinct": false, + "loc": { + "sourceLocationType": "source", + "start": 8, + "end": 17 + }, + "expression": [ + { + "type": "term", + "subType": "variable", + "value": "v", + "loc": { + "sourceLocationType": "source", + "start": 14, + "end": 16 + } + } + ] + }, + "variable": { + "type": "term", + "subType": "variable", + "value": "count", + "loc": { + "sourceLocationType": "source", + "start": 21, + "end": 27 + } + }, + "loc": { + "sourceLocationType": "source", + "start": 7, + "end": 28 + } + }, + { + "type": "pattern", + "subType": "bind", + "expression": { + "type": "expression", + "subType": "operation", + "operator": "+", + "args": [ + { + "type": "term", + "subType": "variable", + "value": "count", + "loc": { + "sourceLocationType": "source", + "start": 30, + "end": 36 + } + }, + { + "type": "term", + "subType": "literal", + "value": "1", + "langOrIri": { + "type": "term", + "subType": "namedNode", + "value": "http://www.w3.org/2001/XMLSchema#integer", + "loc": { + "sourceLocationType": "noMaterialize" + } + }, + "loc": { + "sourceLocationType": "source", + "start": 39, + "end": 40 + } + } + ], + "loc": { + "sourceLocationType": "source", + "start": 30, + "end": 40 + } + }, + "variable": { + "type": "term", + "subType": "variable", + "value": "countPlusOne", + "loc": { + "sourceLocationType": "source", + "start": 44, + "end": 57 + } + }, + "loc": { + "sourceLocationType": "source", + "start": 29, + "end": 58 + } + } + ], + "loc": { + "sourceLocationType": "inlinedSource", + "newSource": "SELECT (COUNT(?v) AS ?count) (?count + 1 AS ?countPlusOne) WHERE {\n VALUES ?v { 0 1 2 3 }\n}", + "start": 0, + "end": 9007199254740991, + "loc": { + "sourceLocationType": "source", + "start": 0, + "end": 92 + }, + "startOnNew": 0, + "endOnNew": 92 + }, + "type": "query" +} diff --git a/packages/test-utils/statics/ast/sparql-generated-compact/sparql-1-1/note-as-var-not-in-term-subquery.sparql b/packages/test-utils/statics/ast/sparql-generated-compact/sparql-1-1/note-as-var-not-in-term-subquery.sparql new file mode 100644 index 00000000..7eff8cd7 --- /dev/null +++ b/packages/test-utils/statics/ast/sparql-generated-compact/sparql-1-1/note-as-var-not-in-term-subquery.sparql @@ -0,0 +1 @@ +SELECT ( "1"^^ AS ?x ) WHERE { SELECT ?y WHERE { ?y ?p ?o . } } \ No newline at end of file diff --git a/packages/test-utils/statics/ast/sparql-generated-compact/sparql-1-1/note-as-var-not-in-wildcard-subquery.sparql b/packages/test-utils/statics/ast/sparql-generated-compact/sparql-1-1/note-as-var-not-in-wildcard-subquery.sparql new file mode 100644 index 00000000..1b9e77d8 --- /dev/null +++ b/packages/test-utils/statics/ast/sparql-generated-compact/sparql-1-1/note-as-var-not-in-wildcard-subquery.sparql @@ -0,0 +1 @@ +SELECT ( "1"^^ AS ?x ) WHERE { SELECT * WHERE { ?s ?p ?o . } } \ No newline at end of file diff --git a/packages/test-utils/statics/ast/sparql-generated-compact/sparql-1-1/note-expression-grouped-var.sparql b/packages/test-utils/statics/ast/sparql-generated-compact/sparql-1-1/note-expression-grouped-var.sparql new file mode 100644 index 00000000..4c74614c --- /dev/null +++ b/packages/test-utils/statics/ast/sparql-generated-compact/sparql-1-1/note-expression-grouped-var.sparql @@ -0,0 +1 @@ +SELECT ( ( ?x + "1"^^ ) AS ?result ) WHERE { ?s ?p ?x . } GROUP BY ?x \ No newline at end of file diff --git a/packages/test-utils/statics/ast/sparql-generated-compact/sparql-1-2/select-variable-reuse.sparql b/packages/test-utils/statics/ast/sparql-generated-compact/sparql-1-2/select-variable-reuse.sparql new file mode 100644 index 00000000..09a69889 --- /dev/null +++ b/packages/test-utils/statics/ast/sparql-generated-compact/sparql-1-2/select-variable-reuse.sparql @@ -0,0 +1 @@ +SELECT ( COUNT( ?v ) AS ?count ) ( ( ?count + "1"^^ ) AS ?countPlusOne ) WHERE { VALUES ?v { "0"^^ "1"^^ "2"^^ "3"^^ } } \ No newline at end of file diff --git a/packages/test-utils/statics/ast/sparql-generated/sparql-1-1/note-as-var-not-in-term-subquery.sparql b/packages/test-utils/statics/ast/sparql-generated/sparql-1-1/note-as-var-not-in-term-subquery.sparql new file mode 100644 index 00000000..605a6816 --- /dev/null +++ b/packages/test-utils/statics/ast/sparql-generated/sparql-1-1/note-as-var-not-in-term-subquery.sparql @@ -0,0 +1,5 @@ +SELECT ( "1"^^ AS ?x ) WHERE { + SELECT ?y WHERE { + ?y ?p ?o . + } +} \ No newline at end of file diff --git a/packages/test-utils/statics/ast/sparql-generated/sparql-1-1/note-as-var-not-in-wildcard-subquery.sparql b/packages/test-utils/statics/ast/sparql-generated/sparql-1-1/note-as-var-not-in-wildcard-subquery.sparql new file mode 100644 index 00000000..1820194c --- /dev/null +++ b/packages/test-utils/statics/ast/sparql-generated/sparql-1-1/note-as-var-not-in-wildcard-subquery.sparql @@ -0,0 +1,5 @@ +SELECT ( "1"^^ AS ?x ) WHERE { + SELECT * WHERE { + ?s ?p ?o . + } +} \ No newline at end of file diff --git a/packages/test-utils/statics/ast/sparql-generated/sparql-1-1/note-expression-grouped-var.sparql b/packages/test-utils/statics/ast/sparql-generated/sparql-1-1/note-expression-grouped-var.sparql new file mode 100644 index 00000000..e6a3ab5e --- /dev/null +++ b/packages/test-utils/statics/ast/sparql-generated/sparql-1-1/note-expression-grouped-var.sparql @@ -0,0 +1,4 @@ +SELECT ( ( ?x + "1"^^ ) AS ?result ) WHERE { + ?s ?p ?x . +} +GROUP BY ?x \ No newline at end of file diff --git a/packages/test-utils/statics/ast/sparql-generated/sparql-1-2/select-variable-reuse.sparql b/packages/test-utils/statics/ast/sparql-generated/sparql-1-2/select-variable-reuse.sparql new file mode 100644 index 00000000..86119c9f --- /dev/null +++ b/packages/test-utils/statics/ast/sparql-generated/sparql-1-2/select-variable-reuse.sparql @@ -0,0 +1,8 @@ +SELECT ( COUNT( ?v ) AS ?count ) ( ( ?count + "1"^^ ) AS ?countPlusOne ) WHERE { + VALUES ?v { + "0"^^ + "1"^^ + "2"^^ + "3"^^ + } +} \ No newline at end of file diff --git a/packages/test-utils/statics/ast/sparql/sparql-1-1-invalid/count-with-ungrouped-expression-variable.sparql b/packages/test-utils/statics/ast/sparql/sparql-1-1-invalid/count-with-ungrouped-expression-variable.sparql new file mode 100644 index 00000000..76ab54cd --- /dev/null +++ b/packages/test-utils/statics/ast/sparql/sparql-1-1-invalid/count-with-ungrouped-expression-variable.sparql @@ -0,0 +1 @@ +SELECT (COUNT(?y) AS ?cnt) (?y + 1 AS ?result) WHERE { ?s ?p ?y } diff --git a/packages/test-utils/statics/ast/sparql/sparql-1-1-invalid/select-star-with-group-by.sparql b/packages/test-utils/statics/ast/sparql/sparql-1-1-invalid/select-star-with-group-by.sparql new file mode 100644 index 00000000..bc66d86d --- /dev/null +++ b/packages/test-utils/statics/ast/sparql/sparql-1-1-invalid/select-star-with-group-by.sparql @@ -0,0 +1 @@ +SELECT * WHERE { ?s ?p ?o } GROUP BY ?s diff --git a/packages/test-utils/statics/ast/sparql/sparql-1-1-invalid/ungrouped-variable-in-expression-projection.sparql b/packages/test-utils/statics/ast/sparql/sparql-1-1-invalid/ungrouped-variable-in-expression-projection.sparql new file mode 100644 index 00000000..a14bb8a7 --- /dev/null +++ b/packages/test-utils/statics/ast/sparql/sparql-1-1-invalid/ungrouped-variable-in-expression-projection.sparql @@ -0,0 +1 @@ +SELECT (?y + 1 AS ?result) WHERE { ?s ?p ?y } GROUP BY ?s diff --git a/packages/test-utils/statics/ast/sparql/sparql-1-1/note-as-var-not-in-term-subquery.sparql b/packages/test-utils/statics/ast/sparql/sparql-1-1/note-as-var-not-in-term-subquery.sparql new file mode 100644 index 00000000..d3c69a00 --- /dev/null +++ b/packages/test-utils/statics/ast/sparql/sparql-1-1/note-as-var-not-in-term-subquery.sparql @@ -0,0 +1 @@ +SELECT (1 AS ?x) WHERE { SELECT ?y WHERE { ?y ?p ?o } } diff --git a/packages/test-utils/statics/ast/sparql/sparql-1-1/note-as-var-not-in-wildcard-subquery.sparql b/packages/test-utils/statics/ast/sparql/sparql-1-1/note-as-var-not-in-wildcard-subquery.sparql new file mode 100644 index 00000000..6dd1a8ce --- /dev/null +++ b/packages/test-utils/statics/ast/sparql/sparql-1-1/note-as-var-not-in-wildcard-subquery.sparql @@ -0,0 +1 @@ +SELECT (1 AS ?x) WHERE { SELECT * WHERE { ?s ?p ?o } } diff --git a/packages/test-utils/statics/ast/sparql/sparql-1-1/note-expression-grouped-var.sparql b/packages/test-utils/statics/ast/sparql/sparql-1-1/note-expression-grouped-var.sparql new file mode 100644 index 00000000..54aaf3dc --- /dev/null +++ b/packages/test-utils/statics/ast/sparql/sparql-1-1/note-expression-grouped-var.sparql @@ -0,0 +1 @@ +SELECT (?x + 1 AS ?result) WHERE { ?s ?p ?x } GROUP BY ?x diff --git a/packages/test-utils/statics/ast/sparql/sparql-1-2/select-variable-reuse.sparql b/packages/test-utils/statics/ast/sparql/sparql-1-2/select-variable-reuse.sparql new file mode 100644 index 00000000..adc1dd8b --- /dev/null +++ b/packages/test-utils/statics/ast/sparql/sparql-1-2/select-variable-reuse.sparql @@ -0,0 +1,3 @@ +SELECT (COUNT(?v) AS ?count) (?count + 1 AS ?countPlusOne) WHERE { + VALUES ?v { 0 1 2 3 } +} \ No newline at end of file