Skip to content
Open
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
3 changes: 2 additions & 1 deletion engines/algebra-sparql-1-1/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ indicating whether patterns should be translated to triple or quad patterns.
In the case of quads the `graph` operation will be removed
and embedded into the patterns it contained.
The default value for this parameter is `false`.
For update queries, even in the case of `quad: false`, the target triple patterns are converted to quads.
```
PREFIX : <http://www.example.org/>

Expand Down Expand Up @@ -252,4 +253,4 @@ and the project operation always gets used (even in the case of `SELECT *`).
## A note on tests

Every test consists of a sparql file and a corresponding json file containing the algebra result.
Tests ending with `(quads)` in their name are tested/generated with `quads: true` in the options.
Tests ending with `-quads` in their name are tested/generated with `quads: true` in the options.
3 changes: 2 additions & 1 deletion engines/algebra-sparql-1-1/lib/toAlgebra.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@ export const toAlgebra11Builder = IndirBuilder
* @param options - Optional options object. Current options:
* @param options.dataFactory - The Datafactory used to generate terms. Default @rdfjs/data-model.
* @param options.quads - Boolean indicating whether triples should be converted to Quads
* (consumes GRAPH statements). Default false.
* (consuming the GRAPH statements).
* Default false. - In case of false, graph targets of updates are still pushed down
* @param options.prefixes - Pre-defined prefixes for the given query. Default empty.
* @param options.baseIRI - Base IRI that should be used for the query.
* Default undefined (throws error if required).
Expand Down
4 changes: 2 additions & 2 deletions engines/algebra-sparql-1-1/test/algebra.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ describe('algebra output', () => {
describe(suite, () => {
for (const blankToVariable of [ true, false ]) {
for (const test of sparqlAlgebraTests(suite, blankToVariable, true)) {
const { name, json, sparql: query } = test;
const { name, json, quads, sparql: query } = test;
it(`${name}${blankToVariable ? ' (no blanks)' : ''}`, ({ expect }) => {
const ast = parser.parse(query);
const algebra = algebraUtils.objectify(
toAlgebra(ast, {
quads: name.endsWith('-quads'),
quads,
blankToVariable,
}),
);
Expand Down
19 changes: 14 additions & 5 deletions engines/algebra-sparql-1-1/test/extraCoverage.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,21 @@ GROUP BY ( ?y AS ?x )`);
});
});

describe('insert/DELETE without quads option throws', () => {
it('toAlgebra throws when INSERT DATA is converted without quads option', ({ expect }) => {
describe('insert/DELETE without quads option works', () => {
it('toAlgebra succeeds when INSERT DATA is converted without quads option', ({ expect }) => {
const ast = parser.parse('INSERT DATA { <http://s> <http://p> <http://o> }');
expect(() => toAlgebra(ast, { quads: false })).toThrowError(
/INSERT\/DELETE operations are only supported with quads option enabled/u,
);
const result = algebraUtils.objectify(toAlgebra(ast, { quads: false }));
expect(result).toMatchObject({
type: 'deleteinsert',
insert: [{
type: 'pattern',
termType: 'Quad',
subject: { termType: 'NamedNode', value: 'http://s' },
predicate: { termType: 'NamedNode', value: 'http://p' },
object: { termType: 'NamedNode', value: 'http://o' },
graph: { termType: 'DefaultGraph', value: '' },
}],
});
});
});

Expand Down
69 changes: 36 additions & 33 deletions engines/algebra-sparql-1-1/test/generateJson.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,43 +25,46 @@ describe.skip('algebra test generate', () => {
for (const suite of suites) {
describe(suite, () => {
for (const { query, name } of sparqlQueries(suite)) {
for (const blankToVariable of [ false, true ]) {
it(`${name} - blankToVar: ${blankToVariable}`, ({ expect }) => {
expect(() => {
astFactory.resetBlankNodeCounter();
const ast = parser.parse(query, { astFactory });
const algebra = algebraUtils.objectify(toAlgebra(ast, {
quads: name.endsWith('-quads'),
blankToVariable,
}));
const canonicalString = generator.generate(toAst(algebra));
for (const quads of [ false, true ]) {
for (const blankToVariable of [ false, true ]) {
const suffix = quads ? '-quads' : '';
it(`${name}${suffix} - blankToVar: ${blankToVariable}`, ({ expect }) => {
expect(() => {
astFactory.resetBlankNodeCounter();
const ast = parser.parse(query, { astFactory });
const algebra = algebraUtils.objectify(toAlgebra(ast, {
quads,
blankToVariable,
}));
const canonicalString = generator.generate(toAst(algebra));

const algebraFileName = `${name}.json`;
let newPath = blankToVariable ? rootJsonBlankToVariable : rootJson;
let newPathCanonical = blankToVariable ? canonicalSparqlBlankToVar : canonicalSparqlBase;
for (const piece of name.split(sep).slice(0, -1)) {
newPath = join(newPath, piece);
if (!existsSync(newPath)) {
mkdirSync(newPath);
const algebraFileName = `${name}${suffix}.json`;
let newPath = blankToVariable ? rootJsonBlankToVariable : rootJson;
let newPathCanonical = blankToVariable ? canonicalSparqlBlankToVar : canonicalSparqlBase;
for (const piece of name.split(sep).slice(0, -1)) {
newPath = join(newPath, piece);
if (!existsSync(newPath)) {
mkdirSync(newPath);
}
}
}
for (const piece of name.split(sep).slice(0, -1)) {
newPathCanonical = join(newPathCanonical, piece);
if (!existsSync(newPathCanonical)) {
mkdirSync(newPathCanonical);
for (const piece of name.split(sep).slice(0, -1)) {
newPathCanonical = join(newPathCanonical, piece);
if (!existsSync(newPathCanonical)) {
mkdirSync(newPathCanonical);
}
}
}

writeFileSync(
join(blankToVariable ? rootJsonBlankToVariable : rootJson, algebraFileName),
JSON.stringify(algebra, null, 2),
);
writeFileSync(
join(blankToVariable ? canonicalSparqlBlankToVar : canonicalSparqlBase, `${name}.sparql`),
canonicalString,
);
}).not.toThrow();
});
writeFileSync(
join(blankToVariable ? rootJsonBlankToVariable : rootJson, algebraFileName),
JSON.stringify(algebra, null, 2),
);
writeFileSync(
join(blankToVariable ? canonicalSparqlBlankToVar : canonicalSparqlBase, `${name}${suffix}.sparql`),
canonicalString,
);
}).not.toThrow();
});
}
}
}
});
Expand Down
3 changes: 2 additions & 1 deletion engines/algebra-sparql-1-2/lib/toAlgebra12.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,8 @@ export const toAlgebra12Builder = IndirBuilder
* @param options - Optional options object. Current options:
* @param options.dataFactory - The Datafactory used to generate terms. Default @rdfjs/data-model.
* @param options.quads - Boolean indicating whether triples should be converted to Quads
* (consumes GRAPH statements). Default false.
* (consuming the GRAPH statements).
* Default false. - In case of false, graph targets of updates are still pushed down
* @param options.prefixes - Pre-defined prefixes for the given query. Default empty.
* @param options.baseIRI - Base IRI that should be used for the query.
* Default undefined (throws error if required).
Expand Down
8 changes: 2 additions & 6 deletions engines/algebra-sparql-1-2/test/algebra.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,12 @@ describe('algebra output 1.2', () => {
describe(suite, () => {
for (const blankToVariable of [ true, false ]) {
for (const test of sparqlAlgebraTests(suite, blankToVariable, true)) {
const { name, json, sparql: query } = test;
// If (!name.includes('sparql-1-2-syntax-nested-anonreifier-01') ||
// blankToVariable || name.includes('-quads')) {
// continue;
// }
const { name, json, quads, sparql: query } = test;
it(`${name}${blankToVariable ? ' (no blanks)' : ''}`, ({ expect }) => {
const ast = parser.parse(query);
const algebra = algebraUtils.objectify(
toAlgebra(ast, {
quads: name.endsWith('-quads'),
quads,
blankToVariable,
}),
);
Expand Down
71 changes: 37 additions & 34 deletions engines/algebra-sparql-1-2/test/generateJson.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
import { join, sep } from 'node:path';
import { algebraUtils } from '@traqula/algebra-transformations-1-1';
import { algebraUtils } from '@traqula/algebra-transformations-1-2';
import { Generator } from '@traqula/generator-sparql-1-2';
import { Parser } from '@traqula/parser-sparql-1-2';
import { AstFactory } from '@traqula/rules-sparql-1-2';
Expand All @@ -27,43 +27,46 @@ describe.skip('algebra 1.2 test generate', () => {
for (const suite of suites) {
describe(suite, () => {
for (const { query, name } of sparqlQueries(suite)) {
for (const blankToVariable of [ false, true ]) {
it(`${name} - blankToVar: ${blankToVariable}`, ({ expect }) => {
expect(() => {
astFactory.resetBlankNodeCounter();
const ast = parser.parse(query, { astFactory });
const algebra = algebraUtils.objectify(toAlgebra(ast, {
quads: name.endsWith('-quads'),
blankToVariable,
}));
const canonicalString = generator.generate(toAst(algebra));
for (const quads of [ false, true ]) {
for (const blankToVariable of [ false, true ]) {
const suffix = quads ? '-quads' : '';
it(`${name}${suffix} - blankToVar: ${blankToVariable}`, ({ expect }) => {
expect(() => {
astFactory.resetBlankNodeCounter();
const ast = parser.parse(query, { astFactory });
const algebra = algebraUtils.objectify(toAlgebra(ast, {
quads,
blankToVariable,
}));
const canonicalString = generator.generate(toAst(algebra));

const algebraFileName = `${name}.json`;
let newPath = blankToVariable ? rootJsonBlankToVariable : rootJson;
let newPathCanonical = blankToVariable ? canonicalSparqlBlankToVar : canonicalSparqlBase;
for (const piece of name.split(sep).slice(0, -1)) {
newPath = join(newPath, piece);
if (!existsSync(newPath)) {
mkdirSync(newPath);
const algebraFileName = `${name}${suffix}.json`;
let newPath = blankToVariable ? rootJsonBlankToVariable : rootJson;
let newPathCanonical = blankToVariable ? canonicalSparqlBlankToVar : canonicalSparqlBase;
for (const piece of name.split(sep).slice(0, -1)) {
newPath = join(newPath, piece);
if (!existsSync(newPath)) {
mkdirSync(newPath);
}
}
}
for (const piece of name.split(sep).slice(0, -1)) {
newPathCanonical = join(newPathCanonical, piece);
if (!existsSync(newPathCanonical)) {
mkdirSync(newPathCanonical);
for (const piece of name.split(sep).slice(0, -1)) {
newPathCanonical = join(newPathCanonical, piece);
if (!existsSync(newPathCanonical)) {
mkdirSync(newPathCanonical);
}
}
}

writeFileSync(
join(blankToVariable ? rootJsonBlankToVariable : rootJson, algebraFileName),
JSON.stringify(algebra, null, 2),
);
writeFileSync(
join(blankToVariable ? canonicalSparqlBlankToVar : canonicalSparqlBase, `${name}.sparql`),
canonicalString,
);
}).not.toThrow();
});
writeFileSync(
join(blankToVariable ? rootJsonBlankToVariable : rootJson, algebraFileName),
JSON.stringify(algebra, null, 2),
);
writeFileSync(
join(blankToVariable ? canonicalSparqlBlankToVar : canonicalSparqlBase, `${name}${suffix}.sparql`),
canonicalString,
);
}).not.toThrow();
});
}
}
}
});
Expand Down
15 changes: 8 additions & 7 deletions packages/algebra-transformations-1-1/lib/toAlgebra/updates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,9 @@ Algebra.Update,
[UpdateOperationInsertData | UpdateOperationDeleteData | UpdateOperationDeleteWhere | UpdateOperationModify]
> = {
name: 'translateInsertDelete',
fun: ({ SUBRULE }) => ({ useQuads, algebraFactory: AF, astFactory: F }, op) => {
if (!useQuads) {
throw new Error('INSERT/DELETE operations are only supported with quads option enabled');
}

fun: ({ SUBRULE }) => ({ algebraFactory: AF, astFactory: F, useQuads }, op) => {
// `useQuads: false` is supported in update queries,
// but within the target, the quad push down is still performed.
const deleteTriples: Algebra.Pattern[] = [];
const insertTriples: Algebra.Pattern[] = [];
let where: Algebra.Operation | undefined;
Expand All @@ -114,8 +112,11 @@ Algebra.Update,
if (use.default.length > 0 || use.named.length > 0) {
where = AF.createFrom(where, use.default, use.named);
} else if (F.isUpdateOperationModify(op) && op.graph) {
// This is equivalent
where = SUBRULE(recurseGraph, where, SUBRULE(translateNamed, op.graph), undefined);
if (useQuads) {
where = SUBRULE(recurseGraph, where, SUBRULE(translateNamed, op.graph), undefined);
} else {
where = AF.createGraph(where, SUBRULE(translateNamed, op.graph));
}
}
}
}
Expand Down
41 changes: 26 additions & 15 deletions packages/test-utils/lib/generators/algebraGenerators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ export type AlgebraTestSuite = 'dawg-syntax' | 'sparql-1.1' | 'sparql11-query' |
/**
* Yields algebra-level test cases from the static test fixtures.
* Each test provides a SPARQL query, expected algebra JSON, and optionally a canonical SPARQL string.
* For each unique base test name, yields both a `quads: false` and a `quads: true` variant
* when the corresponding expected algebra fixture exists.
* @param suite - The test suite to iterate.
* @param blankToVariable - Whether to use the blank-to-variable fixture variant.
* @param getSPARQL - Whether to load the SPARQL and canonical SPARQL strings.
Expand All @@ -35,32 +37,41 @@ export function sparqlAlgebraTests(suite: AlgebraTestSuite, blankToVariable: boo
Generator<algebraTestGen>;
export function* sparqlAlgebraTests(suite: AlgebraTestSuite, blankToVariable: boolean, getSPARQL: boolean):
Generator<algebraTestGen> {
const jsonRoot = blankToVariable ? rootJsonBlankToVariable : rootJson;

// Relative path starting from roots declared above.
function* subGen(relativePath: string): Generator<algebraTestGen> {
const absolutePath = join(blankToVariable ? rootJsonBlankToVariable : rootJson, relativePath);
const absolutePath = join(rootSparql, relativePath);
if (lstatSync(absolutePath).isDirectory()) {
// Recursion
for (const sub of readdirSync(absolutePath)) {
// Relative path appended with sub
yield* subGen(join(relativePath, sub));
}
} else {
const name = relativePath.replace(/\.json$/u, '');
const sparqlPath = join(rootSparql, relativePath.replace(/\.json/u, '.sparql'));
const canonicalSparqlPath = join(
blankToVariable ? rootCanonicalSparqlBlankToVar : rootCanonicalSparql,
relativePath.replace(/\.json/u, '.sparql'),
);
yield {
name,
json: JSON.parse(readFileSync(absolutePath)),
sparql: getSPARQL ? readFileSync(sparqlPath, 'utf8') : undefined,
canonicalSparql: getSPARQL ? readFileSync(canonicalSparqlPath, 'utf-8') : undefined,
quads: name.endsWith('-quads'),
};
// Emit tests
const baseName = relativePath.replace(/\.sparql$/u, '');

for (const suffix of [ '', '-quads' ]) {
const name = `${baseName}${suffix}`;
const jsonPath = join(jsonRoot, `${name}.json`);
const canonicalSparqlPath = join(
blankToVariable ? rootCanonicalSparqlBlankToVar : rootCanonicalSparql,
`${name}.sparql`,
);

yield {
name,
json: JSON.parse(readFileSync(jsonPath)),
sparql: getSPARQL ? readFileSync(absolutePath, 'utf8') : undefined,
canonicalSparql: getSPARQL ? readFileSync(canonicalSparqlPath, 'utf-8') : undefined,
quads: suffix === '-quads',
};
}
}
}

const subfolders = readdirSync(blankToVariable ? rootJsonBlankToVariable : rootJson);
const subfolders = readdirSync(rootSparql);
if (subfolders.includes(suite)) {
yield* subGen(suite);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"type": "project",
"input": {
"type": "bgp",
"patterns": []
},
"variables": []
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"type": "project",
"input": {
"type": "bgp",
"patterns": []
},
"variables": []
}
Loading
Loading