sqlib Docs HomeGet startedGitHub
Guides

The sqlib command line

sqlib runs SPARQL queries, ETL jobs and SPARQL RL rule sets in one process, with no sqlib server. It is built for pipelines: a container that reads rows, converts them to RDF, infers over them and writes the result, with no HTTP between the steps.

It runs the same code the server runs. A query is compiled by the server's parser and applied by the export runtime's splice; an ETL job goes through the server's ETL loop; a rule set goes through the server's rule runner. Two tests hold that true: packages/api/test/lib/etlParity.test.ts and packages/api/test/lib/srlParity.test.ts.

Running it

The packages are not on npm yet. They are ready to publish, and pass the same install checks as the static-export runtime; once they are, npm install -g @sparql-query-lib/cli installs the command. Two ways to run it today:

It needs Node 22.4 or later. DuckDB's native binding is installed with it.

Commands

Every command reads - as standard input and writes to standard output unless --out names a file. Logs and progress go to standard error, so a command's output can be piped into the next. Each command takes --help.

sqlib query

sqlib query --file by-city.rq --data people.ttl --arg 'city=<http://ex/Perth>' --limit 1=20

Runs a query from a file. Parameters work as they do on the server: a VALUES ?x { UNDEF } clause is a slot, LIMIT 0001 is a named limit.

SELECT and ASK print SPARQL Results JSON. CONSTRUCT and DESCRIBE print RDF, in --format (Turtle by default).

sqlib etl run

sqlib etl run --job job.json --out customers.nt

Runs an ETL job: the SQL runs in DuckDB, its rows are read in chunks, each chunk fills the CONSTRUCT template's VALUES slot, and each chunk's RDF is written as soon as it exists. A run holds one chunk's RDF at a time, whatever the source's size.

The job file is JSON, in the shape of a stored ETL job:

{
  "sql": "SELECT * FROM read_csv('customers.csv')",
  "templateFile": "construct.rq",
  "chunkSize": 1000,
  "columns": [
    { "columnName": "id", "targetVariable": "c", "termType": "uri",
      "iriTemplate": "http://ex/c/{value}", "nullPolicy": "skipRow" },
    { "columnName": "name", "targetVariable": "name", "termType": "literal",
      "nullPolicy": "undef" }
  ]
}

DuckDB access. The server keeps every capability off by default, because submitted SQL reaches the server's host. The CLI runs in your own process and reading local files is its job, so:

Flag Default Grants
--allow-filesystem / --no-allow-filesystem on local file reads and writes
--allow-http off http(s) reads; implies filesystem, as in the server
--allow-extension-install off DuckDB installing extensions
--unrestricted off no sandbox at all
--memory-limit 1GB DuckDB's memory_limit; empty for none
--query-timeout 300000 milliseconds per chunk; 0 for none

The resolved profile is printed to standard error at the start of each run. An extension already installed loads under the default profile: start the SQL with LOAD azure;, for example. What each setting enforces is in the ETL guide.

sqlib rules run

sqlib rules run --srl rules.srl --data base.ttl --out inferred.nt

Runs an SRL document: its DATA blocks once, then each stratum of rules to its own fixpoint. The --data files are the base graph; the output is the inference graph, the triples the rules and DATA blocks added.

Exit status 0 means converged. Status 2 means a stratum hit --max-iterations or the rules cycle; the output is then the graph so far.

sqlib pull and --bundle

SQLIB_TOKEN=… sqlib pull --from https://sqlib.example.org/api \
  --library urn:sqlib:library:main --out lib.json
sqlib check lib.json
sqlib etl run --bundle lib.json --job customers --out customers.nt
sqlib rules run --bundle lib.json --rule-set ancestors --data customers.nt

sqlib pull fetches a library's export bundle from a running server and writes it to a file. Export… on the web app's main page downloads the same file. A run from that file then needs no server. The bundle carries the library's queries, its ETL jobs and its rule sets:

--bundle <file> with --query, --job or --rule-set <key> runs one entry. sqlib check lib.json validates a bundle, verifies its hashes and lists the keys. A bundle someone edited by hand fails its hash check and is refused.

pull options:

What the server could not export is reported on standard error, one line for each entry, with the reason: a job whose template is not a CONSTRUCT, say, or a rule set with a data block that has no DATA form.

Run reports

etl run and rules run take --report <file>. After the run, the file holds a JSON record of it, because a run the server never saw leaves no execution record behind:

A failed run still writes its report. --report - writes it to standard output, and then --out has to name a file.

sqlib check

sqlib check queries/*.rq rules/*.srl

Parses each file and reports what sqlib would refuse, without running it. For a query: its form, its parameters, and whether it compiles to a template. For SRL: whether it parses, is well formed and stratifies. For a bundle (.json): its shape, its hashes, whether its rule sets compile, and its keys. Exit status 1 if any file has a problem, so it fits a CI step.

Pipelines

Commands compose through files and pipes. Rows to RDF, then inference:

sqlib etl run --job customers.json \
  | sqlib rules run --srl customers.srl --data - --data-format nt --out inferred.nt

Container images

examples/docker/ holds three examples, none of them the published image:

The two Azure examples need a storage account, so CI builds neither.

What it does not do yet