relate clone
The relate clone command that copies a store into a new one, between directory and .rl layouts and between codecs, with its flags, refusals and exit codes.
cli
relate clone SOURCE DEST copies a store A relate database: a directory, a single .rl file, or :memory:, opened with --database. Read more in the glossary into a new store and exits. Each side is a directory store A store kept as a folder of snapshot files, a WAL folder and lock files; the layout for any --database path that is not :memory: and does not end in .rl. Read more in the glossary, or a single-file store A store kept in one .rl file that holds the snapshot and the WAL together; the layout for a --database path ending in .rl. Read more in the glossary when its path ends in .rl, so one command copies, packs a directory into a .rl file, or unpacks one. The destination keeps the source's codec The on-disk encoding of a store's snapshot: Parquet (the default), JSON or markdown, fixed when the store is created. Read more in the glossary unless --to-snapshot-format names another. clone relate clone: the command that copies a store into a new one, optionally in another codec, reading the source without writing it. Read more in the glossary opens the source read-only, copies the state visible when it opened, and never writes the source; it creates the destination, which must not exist yet, and prints nothing on success.
Synopsis
relate clone [--wal-dir DIR] [--to-snapshot-format F] [--markdown-body-properties LIST] SOURCE DEST| Argument | Meaning |
|---|---|
SOURCE | The store to copy: a directory store, or a path ending in .rl. Required. :memory: is refused |
DEST | The new store: a path ending in .rl makes a single-file store, any other path a directory store. Required. It must not exist, and its parent directory must. :memory: is refused |
A directory store can be cloned while another process writes it: the clone takes the state committed when it opened and does not follow later commits. A .rl source is refused while another process holds it for writing. The destination's write-ahead log (WAL) starts at <DEST>/wal for a directory destination; a .rl destination holds its own.
Options
| Flag | Argument | Default | Scope | Since | Description |
|---|---|---|---|---|---|
--wal-dir | DIR | <SOURCE>/wal | relate clone | — | Source WAL of a directory source; fails with a .rl source. |
--to-snapshot-format | parquet|json|markdown | the source codec | relate clone | — | Destination snapshot codec (parquet, json or markdown); transcodes the published checkpoint. |
--markdown-body-properties | LIST | the source's recorded configuration, else body | relate clone | — | Body properties of a markdown destination (LIST or Label=LIST; entries separated by ;). |
A — under Since: the data does not record the release that added the flag.
relate clone takes only these three flags. A query-run flag after clone, such as --read-only or --json, exits 2 with Error: unexpected argument to clone: <flag>; --read-only before clone exits 2 with Error: clone cannot be combined with a query or an access flag (--read-only).
Layouts
Every pairing of the two durable layouts works; the in-memory store is refused on either side. One pairing depends on the codec: in relate 0.1.2 a .rl destination refuses markdown body properties, so a markdown source reaches a .rl path only with --to-snapshot-format parquet or json (Codecs).
| Source | Destination | Result |
|---|---|---|
| Directory store | Directory store | A copy |
| Directory store | .rl path | The store packed into one file |
.rl file | Directory store | The file unpacked into a folder |
.rl file | .rl path | A copy |
:memory: | Any | Exit code 1: Failed to clone database: storage error: clone source must be a native directory or .rl bundle path |
| Any | :memory: | Exit code 1: Failed to clone database: storage error: clone destination must be a native directory or .rl bundle path |
These examples run in a new, empty directory. This first command writes: it creates the directory store ./team with three people, two projects and three WORKS_ON relationships:
relate --database ./team "CREATE (ada:Person {name: 'Ada', role: 'engineer'}),
(grace:Person {name: 'Grace', role: 'engineer'}),
(noor:Person {name: 'Noor', role: 'maintainer'}),
(relate:Project {name: 'relate', language: 'Rust'}),
(tck:Project {name: 'opencypher-tck', language: 'Gherkin'}),
(ada)-[:WORKS_ON {since: 2024}]->(relate),
(grace)-[:WORKS_ON {since: 2023}]->(relate),
(grace)-[:WORKS_ON {since: 2022}]->(tck),
(noor)-[:MAINTAINS]->(relate),
(ada)-[:KNOWS]->(grace)"The four layout pairings, then a read-only count of the relationships in each copy:
relate clone ./team ./team-copy
relate clone ./team ./team.rl
relate clone ./team.rl ./team-unpacked
relate clone ./team.rl ./team-copy.rl
for store in team-copy team.rl team-unpacked team-copy.rl; do
relate --database "./$store" --read-only --mode jsonl "MATCH (:Person)-[w:WORKS_ON]->(:Project) RETURN '$store' AS store, count(w) AS works"
done{"store":"team-copy","works":3}
{"planCache":"miss","timings":{"elapsed_ms":0.387583,"execute_ms":0.2,"lex_parse_ms":0.030125,"lower_ms":0.045166,"parse_ms":0.11820800000000001,"plan_ms":0.059417,"result_generation_ms":0.000292,"rewrite_ms":0.008624999999999999}}
{"store":"team.rl","works":3}
{"planCache":"miss","timings":{"elapsed_ms":0.39399999999999996,"execute_ms":0.208584,"lex_parse_ms":0.03025,"lower_ms":0.04575,"parse_ms":0.116167,"plan_ms":0.060291000000000004,"result_generation_ms":0.00025,"rewrite_ms":0.007833}}
{"store":"team-unpacked","works":3}
{"planCache":"miss","timings":{"elapsed_ms":0.33675000000000005,"execute_ms":0.181292,"lex_parse_ms":0.025791,"lower_ms":0.038542,"parse_ms":0.09875,"plan_ms":0.049499999999999995,"result_generation_ms":0.000292,"rewrite_ms":0.006875}}
{"store":"team-copy.rl","works":3}
{"planCache":"miss","timings":{"elapsed_ms":0.3995,"execute_ms":0.206292,"lex_parse_ms":0.033583,"lower_ms":0.049917,"parse_ms":0.126208,"plan_ms":0.058332999999999996,"result_generation_ms":0.000292,"rewrite_ms":0.008459}}Codecs
Without --to-snapshot-format, the destination keeps the source's codec. With it, clone writes the state visible when it opened, which is the last checkpoint plus the WAL commits after it, in the parquet, json or markdown codec; the layout and the codec are chosen independently.
| Source codec | --to-snapshot-format | Layouts in the example |
|---|---|---|
parquet | json | directory to directory, .rl to .rl |
parquet | markdown | directory to directory, .rl to directory |
parquet | parquet | directory to directory |
json | parquet | directory to directory |
markdown | parquet | directory to directory, directory to .rl |
Any other value exits 2 with Error: invalid --to-snapshot-format: storage error: unknown snapshot codec '<value>' (expected parquet, json, markdown or rkyv). rkyv is not compiled into the release binary: --to-snapshot-format rkyv exits 1 with Failed to clone database: storage error: rkyv snapshot codec is not compiled into this build (enable the "rkyv-codec" feature). Storage formats describes each codec's files.
Each pairing in the table, with the codec relate info reports for every result:
relate clone --to-snapshot-format json ./team ./team-json
relate clone --to-snapshot-format markdown ./team ./team-vault
relate clone --to-snapshot-format parquet ./team ./team-parquet
relate clone --to-snapshot-format parquet ./team-json ./from-json
relate clone --to-snapshot-format parquet ./team-vault ./from-vault
relate clone --to-snapshot-format parquet ./team-vault ./team-vault.rl
relate clone --to-snapshot-format json ./team.rl ./team-json.rl
relate clone --to-snapshot-format markdown ./team.rl ./vault-from-rl
for store in team-json team-vault team-parquet from-json from-vault team-vault.rl team-json.rl vault-from-rl; do
printf '%s: ' "$store"
relate info --database "./$store" | grep '^codec:'
doneteam-json: codec: json
team-vault: codec: markdown
team-parquet: codec: parquet
from-json: codec: parquet
from-vault: codec: parquet
team-vault.rl: codec: parquet
team-json.rl: codec: json
vault-from-rl: codec: markdownThe graph survives a round trip through the text codecs:
relate --database ./from-vault --read-only --mode jsonl "MATCH (p:Person)-[w:WORKS_ON]->(r:Project) RETURN p.name AS person, r.name AS project, w.since AS since ORDER BY since"{"person":"Grace","project":"opencypher-tck","since":2022}
{"person":"Grace","project":"relate","since":2023}
{"person":"Ada","project":"relate","since":2024}
{"planCache":"miss","timings":{"elapsed_ms":0.579875,"execute_ms":0.39808299999999996,"lex_parse_ms":0.030583000000000003,"lower_ms":0.044875,"parse_ms":0.115042,"plan_ms":0.058415999999999996,"result_generation_ms":0.000417,"rewrite_ms":0.008333}}Markdown into a .rl file
In relate 0.1.2 a .rl destination refuses markdown body properties, whether they come from --markdown-body-properties or from the configuration a markdown source records. Cloning a markdown store into a .rl path therefore needs --to-snapshot-format parquet or json; without it, or with --to-snapshot-format markdown, the clone exits 1 and creates nothing. The message names parquet whatever codec the command asked for:
relate clone ./team-vault ./vault-kept.rl
echo "exit=$?"
relate clone --to-snapshot-format markdown ./team-vault ./vault-md.rl
echo "exit=$?"Failed to clone database: semantic error: markdown body properties are set but the store's snapshot codec is parquet, not markdown; the configuration would be ignored
exit=1
Failed to clone database: semantic error: markdown body properties are set but the store's snapshot codec is parquet, not markdown; the configuration would be ignored
exit=1A parquet or json source with no body properties clones into a markdown .rl file: relate clone --to-snapshot-format markdown ./team.rl ./team-md.rl succeeds, and relate info reports codec: markdown for it. A markdown store clones into a directory store with any codec.
Markdown body properties
--markdown-body-properties names the properties a markdown destination writes as each note's body instead of its frontmatter: a comma-separated list for every label, or Label=LIST for one label, with several entries separated by ; (Person=role,bio;Project=language). The first listed property is written as the bare body and the rest as ## sections. Without the flag, the destination uses the source's recorded configuration, else the single property body. On a directory destination whose codec is not markdown, it exits 1 with Failed to clone database: semantic error: markdown body properties are set but the store's snapshot codec is parquet, not markdown; the configuration would be ignored (the codec named is the destination's).
relate clone --to-snapshot-format markdown --markdown-body-properties 'Person=role' ./team ./team-notes
cat team-notes/Person/Ada.md---
type: Person
name: Ada
KNOWS:
- "[[Person/Grace]]"
WORKS_ON:
- since: 2024
target: "[[Project/relate]]"
---
engineerWAL directory
--wal-dir DIR names the source's WAL when it is not at <SOURCE>/wal. It applies to a directory source only; with a .rl source, which holds its own WAL, it exits 1 with Failed to clone database: storage error: wal_dir is not supported for .rl single-file databases (ADR-0074). It does not move the destination's WAL.
What clone refuses
| Input | Exit code | Message |
|---|---|---|
DEST exists | 1 | Failed to clone database: storage error: clone destination already exists |
DEST's parent directory does not exist | 1 | Failed to clone database: storage error: clone destination parent does not exist: <path> |
A directory SOURCE that does not exist | 1 | Failed to clone database: storage error: read-only open requires an existing database at <path> |
A .rl SOURCE that does not exist | 1 | Failed to clone database: storage error: database not found: <path> |
A .rl SOURCE another process holds for writing | 1 | Failed to clone database: storage locked: <path> |
:memory: on either side | 1 | clone source must be a native directory or .rl bundle path, or clone destination …, after Failed to clone database: storage error: |
A .rl DEST with markdown body properties: a markdown SOURCE without --to-snapshot-format parquet or json, or --markdown-body-properties | 1 | Failed to clone database: semantic error: markdown body properties are set but the store's snapshot codec is parquet, not markdown; the configuration would be ignored |
--wal-dir with a .rl source | 1 | Failed to clone database: storage error: wal_dir is not supported for .rl single-file databases (ADR-0074) |
| Fewer than two paths | 2 | Error: clone requires SOURCE and DEST |
| A third path, or a flag clone does not take | 2 | Error: unexpected argument to clone: <argument> |
A runtime refusal (exit code 1) prints one line on stderr and no error object. A usage error (exit code 2) prints the error line, the usage line and Run 'relate clone --help' for more information.
Errors and exit codes
| Exit code | Meaning for relate clone |
|---|---|
| 0 | The destination was created; nothing is printed |
| 1 | The copy did not run or did not finish: a refusal in the table, or a storage or I/O error |
| 2 | Usage error: a missing or extra argument, an unknown flag, or a bad --to-snapshot-format value |
Examples
An existing destination is never overwritten:
relate clone ./team ./team-copy
echo "exit=$?"Failed to clone database: storage error: clone destination already exists
exit=1See also
- Storage formats: the layouts and codecs clone converts between
- Back up and restore a store: clone as a backup that opens
- relate info: the layout, codec and counts of a clone, read without opening it
- CLI flags: the clone flags beside every other command's