agentleFS
OpenLinkSoftwareOpenLinkSoftware/ai-agent-skills/virtuoso-rdf-loader/SKILL.md

virtuoso-rdf-loader

Skill42 starsChanged 50 days agoMITView on GitHub

Bulk-load RDF archives (N-Triples, Turtle, RDF/XML, N-Quads, TriG, JSON-LD, Notation3 — gzip-compressed or raw) into a Virtuoso instance via isql, using ld_dir + rdf_loader_run. Covers single-file loads, directory-wide ingestion, load monitoring, named-graph management (clear/replace/append), and troubleshooting. Triggers on phrases like "load this RDF into Virtuoso", "bulk load into Virtuoso", "ingest .nt.gz into Virtuoso", or any request to push RDF data to a Virtuoso SPARQL endpoint via isql.

The file

Show its rules and text

We split this skill into the rules it gives an agent

14 rules. About 0 are common, 8 are distinctive, and 6 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈8

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 6

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    All formats are handled by the same ld_dir + rdf_loader_run pipeline — no format-specific flags needed.

    line 32
  2. Distinctive:

    Default order for data ingestion:

    line 38
  3. Distinctive:

    If the user names a specific mode, honor that preference.

    line 45
  4. Distinctive:

    If isql is not found, ask the user for:

    line 59
  5. Distinctive:

    Before any load, collect:

    line 68
  6. Distinctive:

    After loading, check the row count in the target graph:

    line 200
  7. Very specific:

    Prefer ld_dir + rdf_loader_run for everything except single small files — it's parallel, transactional, restartable, and format-agnostic.

    line 282
  8. Distinctive:

    Run Step 0 — verify isql is available and Virtuoso is reachable

    line 308
  9. Distinctive:

    Ask for: host, port, credentials, graph IRI, server-side directory, file pattern

    line 309
  10. Very specific:

    If mode is clear or replace: warn user, then execute SPARQL CLEAR GRAPH

    line 312
  11. Very specific:

    Run ld_dir to register files with the correct pattern

    line 313
  12. Very specific:

    Run rdf_loader_run to start loading

    line 314
  13. Distinctive:

    Run verification count query — report triples loaded per file

    line 316
  14. Very specific:

    If any files report ll_state = 3 (failed), surface ll_error and troubleshoot

    line 317

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Virtuoso RDF Bulk Loader Skill

ld_dir + rdf_loader_run · isql · N-Triples · Turtle · RDF/XML · N-Quads · TriG · JSON-LD · Notation3 · gzip

Purpose

Load RDF archives directly into a Virtuoso quad store — no pre-extraction needed. Virtuoso's ld_dir + rdf_loader_run reads compressed files (.gz, .bz2) natively and supports every major RDF serialization. The skill provides:

  • Single-file loads — one archive → one named graph
  • Directory loads — every matching file in a server-side directory
  • Graph management — clear before load, append, or replace
  • Progress monitoring — poll DB.DBA.LOAD_LIST until completion
  • Error triage — interpret load errors and suggest fixes

Supported Formats

FormatExtensionsNotes
N-Triples.nt, .nt.gz, .nt.bz2Line-based; fastest for bulk
Turtle.ttl, .ttl.gz, .ttl.bz2Compact; common for LOD
RDF/XML.rdf, .rdf.gz, .rdf.bz2Legacy; still widespread
N-Quads.nq, .nq.gz, .nq.bz2Quad format; includes graph IRIs
TriG.trig, .trig.gzMulti-graph Turtle superset
JSON-LD.jsonld, .jsonld.gzJSON-based; web-friendly
Notation3.n3, .n3.gzSuperset of Turtle
OWL.owl, .owl.gzRDF/XML variant

All formats are handled by the same ld_dir + rdf_loader_run pipeline — no format-specific flags needed.


Execution Routing

Default order for data ingestion:

  1. isql CLI — direct isql host:port execution (preferred; always available)
  2. MCP — if a virtuoso-support-agent MCP server is connected, it can relay SQL
  3. SPARQL INSERT — via SPARQL endpoint when isql path is unavailable (wraps individual INSERTs; unsuitable for bulk)
  4. URIBurner upload — when the data is at a URL and URIBurner's sponger is available

If the user names a specific mode, honor that preference.


Step 0 — Connection Detection (Always Run First)

# Check isql availability
which isql

# Quick connectivity test
echo "SELECT 'ok';" | isql <host>:<port> <user> <pass> 2>&1 | head -3

If isql is not found, ask the user for:

  • isql path, OR
  • Virtuoso host:port credentials to try MCP, OR
  • SPARQL endpoint URL for fallback loading

Step 1 — Gather Parameters

Before any load, collect:

ParameterRequiredDefaultNotes
--hostYes—Virtuoso SQL listener hostname
--portNo1111Virtuoso SQL listener port
--userNodbaisql user
--passNodbaisql password
--graphYes—Target named-graph IRI
--dirYes—Server-side directory containing RDF files
--patternNo*.*File pattern for ld_dir (match the format you're loading)
--modeNoappendappend, clear, or replace

--dir must be accessible to the Virtuoso server process — ld_dir registers server-side paths, not client-side paths. For remote Virtuoso, the files must already be on the server.

Recommended Patterns by Format

Loading…Use --pattern
N-Triples*.nt.gz
Turtle*.ttl.gz
RDF/XML*.rdf.gz
N-Quads*.nq.gz
TriG*.trig.gz
JSON-LD*.jsonld.gz
Notation3*.n3.gz
All formats in directory*

Mode Behavior

ModeSQL executed before loadResult
append(none)Triples added to existing graph
clearSPARQL CLEAR GRAPH <iri>Graph emptied, then loaded
replaceSPARQL CLEAR GRAPH <iri> then loadSame as clear + load

Step 2 — Register and Load

The core workflow (two SQL calls):

-- Register files (any supported format)
ld_dir ('/data/rdf/incoming/', '*.nt.gz', 'https://example.com/my-graph');

-- Start the loader (runs asynchronously in the server)
rdf_loader_run ();

Execute via isql:

isql ${HOST}:${PORT} ${USER} ${PASS} <<EOF
ld_dir ('${LOAD_DIR}', '${PATTERN}', '${GRAPH}');
rdf_loader_run ();
CHECKPOINT;
EOF

ld_dir Parameters (Full Signature)

ld_dir (
  in dir         varchar,   -- server-side directory path
  in file_pattern varchar,  -- e.g. '*.nt.gz', '*.ttl.gz', '*.nq.gz', '*'
  in graph_iri   varchar    -- target named graph
);

Key points:

  • ld_dir is idempotent — calling it twice with the same files does not double-load
  • Files are matched by name in DB.DBA.LOAD_LIST — renaming a file after registration re-triggers it
  • rdf_loader_run() can be called multiple times safely; it processes pending entries only
  • The loader auto-detects the RDF format from file content — no format flag required
  • The loader runs on server threads — isql returns immediately

Step 3 — Monitor Progress

Poll DB.DBA.LOAD_LIST until no rows remain in progress:

SELECT
  ll_file,
  ll_graph,
  CASE ll_state
    WHEN 0 THEN 'PENDING'
    WHEN 1 THEN 'LOADING'
    WHEN 2 THEN 'DONE'
    WHEN 3 THEN 'FAILED'
  END AS state,
  ll_started,
  ll_done,
  ll_rows,
  ll_error
FROM DB.DBA.LOAD_LIST;

ll_state Meanings

ll_stateStatusAction
0PendingWaiting — loader will pick it up
1LoadingIn progress — wait
2DoneSuccess — committed
3FailedSee ll_error column

Monitor loop:

while true; do
  STATUS=$(isql ${HOST}:${PORT} ${USER} ${PASS} <<'EOSQL' 2>/dev/null)
    SELECT ll_file, ll_state, ll_error
    FROM DB.DBA.LOAD_LIST
    WHERE ll_state IN (0, 1, 3);
EOSQL
  if echo "$STATUS" | grep -q 'No\. of rows\|0 rows'; then
    echo "All loads complete."
    break
  fi
  echo "$STATUS"
  sleep 5
done

Step 4 — Verify

After loading, check the row count in the target graph:

SPARQL
SELECT (COUNT(*) AS ?triples)
FROM <${GRAPH}>
WHERE { ?s ?p ?o };

Or via isql:

isql ${HOST}:${PORT} ${USER} ${PASS} <<EOF
SPARQL SELECT (COUNT(*) AS ?triples) FROM <${GRAPH}> WHERE { ?s ?p ?o };
EOF

For a spot check, sample 10 triples from the graph:

SPARQL SELECT * FROM <${GRAPH}> WHERE { ?s ?p ?o } LIMIT 10;

Bundled Shell Script

scripts/bulk-load-rdf.sh automates Steps 2–3 for single-file or directory loads. Invoke it from the skill directory:

./scripts/bulk-load-rdf.sh \
    --host     localhost \
    --port     1111 \
    --user     dba \
    --pass     dba \
    --graph    https://example.com/my-graph \
    --dir      /data/rdf/incoming \
    --pattern  "*.nt.gz" \
    dataset.nt.gz
FlagPurpose
--host / --portVirtuoso SQL listener
--user / --passisql credentials
--graphTarget named-graph IRI
--dirServer-side staging directory
--patternFile pattern for ld_dir
--no-copySkip cp — files already in --dir

Troubleshooting

SymptomLikely causeFix
No file(s) found in LOAD_LISTld_dir path not server-accessibleVerify --dir exists on the server; check permissions
ll_state = 3 (FAILED)Malformed data or encoding issueCheck ll_error; validate the RDF file; try one file interactively via DB.DBA.TTLP()
Load stuck at PENDINGLoader not running or blockedCall rdf_loader_run() again; check server error log
Duplicate triplesSame files loaded twiceClear graph, delete from LOAD_LIST, then re-register and re-load
Permission denied on --dirVirtuoso server user can't read directorychmod 755 the directory and chmod 644 the files
.gz files not recognisedPattern doesn't match files on diskUse the correct pattern for the file extension
Wrong graph IRI in N-Quads/TriGFile contains its own graph IRIsN-Quads/TriG files specify graphs inline — ld_dir's graph becomes a default for triples not in a named graph
Out of disk / transaction log fullBulk load exceeds temp spaceIncrease TempSpace / TransactionLog in virtuoso.ini; load in smaller batches

Loading Without ld_dir (Single-File Interactive)

For one-offs where ld_dir isn't practical, pipe a decompressed file directly to isql. DB.DBA.TTLP() auto-detects the format:

gunzip -c data.nt.gz | isql ${HOST}:${PORT} ${USER} ${PASS} <<'EOF'
DB.DBA.TTLP (
  file_to_string_output ('/dev/stdin'),
  '',
  'https://example.com/my-graph',
  0
);
EOF

Prefer ld_dir + rdf_loader_run for everything except single small files — it's parallel, transactional, restartable, and format-agnostic.


Quick Reference

TaskSQL / Command
Register filesld_dir('/path/', '*.nt.gz', 'graph-iri');
Start loaderrdf_loader_run();
Check progressSELECT * FROM DB.DBA.LOAD_LIST WHERE ll_state < 2;
Clear graphSPARQL CLEAR GRAPH <iri>;
Count triplesSPARQL SELECT (COUNT(*) AS ?c) FROM <iri> WHERE {?s ?p ?o};
Remove registrationDELETE FROM DB.DBA.LOAD_LIST WHERE ll_file LIKE '%pattern%';
Force re-loadDelete from LOAD_LIST + ld_dir again + rdf_loader_run
Set parallel loadersrdf_loader_run(max_files => 8);
Check server logtail -f virtuo<port>.log on server

Initialization Sequence

When invoked:

⛔ PRE-BUILD CHECK: Before producing output, re-read the relevant workflow section above and re-read any checklists or verification gates defined in this skill. Confirm each checklist item before writing output. Apply the CLAUDE.md Anti-Drift Protocol: re-read spec section before build, gate-first validation, section-by-section delivery.

  1. Run Step 0 — verify isql is available and Virtuoso is reachable
  2. Ask for: host, port, credentials, graph IRI, server-side directory, file pattern
  3. Determine the RDF format from file extensions — confirm with user
  4. Confirm mode (append / clear / replace)
  5. If mode is clear or replace: warn user, then execute SPARQL CLEAR GRAPH
  6. Run ld_dir to register files with the correct pattern
  7. Run rdf_loader_run to start loading
  8. Poll DB.DBA.LOAD_LIST until all files report ll_state = 2 (done)
  9. Run verification count query — report triples loaded per file
  10. If any files report ll_state = 3 (failed), surface ll_error and troubleshoot

Reference Files

FileContents
references/ldir-parameters.mdFull ld_dir parameter reference, all supported formats and patterns, rdf_loader_run options, LOAD_LIST schema, graph management
scripts/bulk-load-rdf.shShell script automating register → load → monitor

Safe Virtuoso ISQL Error Output

Whenever this workflow invokes OpenLink's native Virtuoso isql client, pass the bare client option COMMAND_TEXT_ON_ERROR=OFF, for example isql {host}:{port} {user} {password} COMMAND_TEXT_ON_ERROR=OFF {script.sql}. Place it before SQL input, script arguments, or -i. OFF maps to the client's internal value 0 and prevents the client from appending the full failed command to error output. The client accepts OFF/ON; literal 0 is rejected. Do not use -u for this setting (-u populates the U argument array). This does not redact secrets from server diagnostics, shell history, or process arguments, so keep credentials out of SQL text and logs. Do not apply this Virtuoso-specific option to unixODBC/iODBC isql clients.

Version

2.0.0 — Format-agnostic. Supports N-Triples, Turtle, RDF/XML, N-Quads, TriG, JSON-LD, Notation3, OWL — gzip-compressed or raw. Same ld_dir + rdf_loader_run pipeline for all.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.