|
|
|
@ -19,7 +19,7 @@ use std::cmp::{max, min}; |
|
|
|
|
use std::ffi::OsStr; |
|
|
|
|
use std::fmt; |
|
|
|
|
use std::fs::File; |
|
|
|
|
use std::io::{self, stdin, BufRead, BufReader, Read, Write}; |
|
|
|
|
use std::io::{self, stdin, stdout, BufRead, BufReader, BufWriter, Read, Write}; |
|
|
|
|
use std::path::{Path, PathBuf}; |
|
|
|
|
use std::rc::Rc; |
|
|
|
|
use std::str; |
|
|
|
@ -116,6 +116,28 @@ enum Command { |
|
|
|
|
#[arg(long)] |
|
|
|
|
graph: Option<String>, |
|
|
|
|
}, |
|
|
|
|
/// Dump the store content into a file.
|
|
|
|
|
Dump { |
|
|
|
|
/// Directory in which the data stored by Oxigraph are persisted.
|
|
|
|
|
#[arg(short, long)] |
|
|
|
|
location: PathBuf, |
|
|
|
|
/// file to dump to.
|
|
|
|
|
///
|
|
|
|
|
/// If no file is given, stdout is used.
|
|
|
|
|
file: Option<PathBuf>, |
|
|
|
|
/// The format of the file(s) to dump.
|
|
|
|
|
///
|
|
|
|
|
/// Can be an extension like "nt" or a MIME type like "application/n-triples".
|
|
|
|
|
///
|
|
|
|
|
/// By default the format is guessed from the target file extension.
|
|
|
|
|
#[arg(long, required_unless_present = "file")] |
|
|
|
|
format: Option<String>, |
|
|
|
|
/// Name of the graph to dump.
|
|
|
|
|
///
|
|
|
|
|
/// By default all graphs are dumped if the output format supports datasets.
|
|
|
|
|
#[arg(long)] |
|
|
|
|
graph: Option<String>, |
|
|
|
|
}, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub fn main() -> anyhow::Result<()> { |
|
|
|
@ -273,6 +295,44 @@ pub fn main() -> anyhow::Result<()> { |
|
|
|
|
Ok(()) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
Command::Dump { |
|
|
|
|
location, |
|
|
|
|
file, |
|
|
|
|
format, |
|
|
|
|
graph, |
|
|
|
|
} => { |
|
|
|
|
let store = Store::open_read_only(&location)?; |
|
|
|
|
let format = if let Some(format) = format { |
|
|
|
|
GraphOrDatasetFormat::from_str(&format)? |
|
|
|
|
} else if let Some(file) = &file { |
|
|
|
|
GraphOrDatasetFormat::from_path(file)? |
|
|
|
|
} else { |
|
|
|
|
bail!("The --format option must be set when writing to stdout") |
|
|
|
|
}; |
|
|
|
|
let graph = if let Some(graph) = &graph { |
|
|
|
|
Some(if graph.eq_ignore_ascii_case("default") { |
|
|
|
|
GraphName::DefaultGraph |
|
|
|
|
} else { |
|
|
|
|
NamedNodeRef::new(graph) |
|
|
|
|
.with_context(|| format!("The target graph name {graph} is invalid"))? |
|
|
|
|
.into() |
|
|
|
|
}) |
|
|
|
|
} else { |
|
|
|
|
None |
|
|
|
|
}; |
|
|
|
|
if let Some(file) = file { |
|
|
|
|
dump( |
|
|
|
|
&store, |
|
|
|
|
BufWriter::new(File::create(&file).map_err(|e| { |
|
|
|
|
anyhow!("Error while opening file {}: {e}", file.display()) |
|
|
|
|
})?), |
|
|
|
|
format, |
|
|
|
|
graph, |
|
|
|
|
) |
|
|
|
|
} else { |
|
|
|
|
dump(&store, stdout().lock(), format, graph) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -300,6 +360,28 @@ fn bulk_load( |
|
|
|
|
Ok(()) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn dump( |
|
|
|
|
store: &Store, |
|
|
|
|
writer: impl Write, |
|
|
|
|
format: GraphOrDatasetFormat, |
|
|
|
|
to_graph_name: Option<GraphName>, |
|
|
|
|
) -> anyhow::Result<()> { |
|
|
|
|
match format { |
|
|
|
|
GraphOrDatasetFormat::Graph(format) => store.dump_graph( |
|
|
|
|
writer, |
|
|
|
|
format, |
|
|
|
|
&to_graph_name.ok_or_else(|| anyhow!("The --graph option is required when writing a graph format like NTriples, Turtle or RDF/XML"))?, |
|
|
|
|
)?, |
|
|
|
|
GraphOrDatasetFormat::Dataset(format) => { |
|
|
|
|
if to_graph_name.is_some() { |
|
|
|
|
bail!("The --graph option is not allowed when writing a dataset format like NQuads or TriG"); |
|
|
|
|
} |
|
|
|
|
store.dump_dataset(writer, format)? |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
Ok(()) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[derive(Copy, Clone)] |
|
|
|
|
enum GraphOrDatasetFormat { |
|
|
|
|
Graph(GraphFormat), |
|
|
|
@ -1239,7 +1321,7 @@ mod tests { |
|
|
|
|
use super::*; |
|
|
|
|
use anyhow::Result; |
|
|
|
|
use assert_cmd::Command; |
|
|
|
|
use assert_fs::prelude::*; |
|
|
|
|
use assert_fs::{prelude::*, NamedTempFile, TempDir}; |
|
|
|
|
use flate2::write::GzEncoder; |
|
|
|
|
use flate2::Compression; |
|
|
|
|
use oxhttp::model::Method; |
|
|
|
@ -1266,38 +1348,66 @@ mod tests { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
fn cli_load_graph() -> Result<()> { |
|
|
|
|
let file = assert_fs::NamedTempFile::new("sample.nt")?; |
|
|
|
|
file.write_str("<http://example.com/s> <http://example.com/p> <http://example.com/o> .")?; |
|
|
|
|
fn cli_load_and_dump_graph() -> Result<()> { |
|
|
|
|
let store_dir = TempDir::new()?; |
|
|
|
|
let input_file = NamedTempFile::new("input.nt")?; |
|
|
|
|
input_file |
|
|
|
|
.write_str("<http://example.com/s> <http://example.com/p> <http://example.com/o> .")?; |
|
|
|
|
cli_command()? |
|
|
|
|
.arg("load") |
|
|
|
|
.arg("--location") |
|
|
|
|
.arg(store_dir.path()) |
|
|
|
|
.arg("-f") |
|
|
|
|
.arg(file.path()) |
|
|
|
|
.arg(input_file.path()) |
|
|
|
|
.assert() |
|
|
|
|
.success() |
|
|
|
|
.stdout("") |
|
|
|
|
.stderr(predicate::str::contains("1 triples loaded")); |
|
|
|
|
.success(); |
|
|
|
|
|
|
|
|
|
let output_file = NamedTempFile::new("output.nt")?; |
|
|
|
|
cli_command()? |
|
|
|
|
.arg("dump") |
|
|
|
|
.arg("--location") |
|
|
|
|
.arg(store_dir.path()) |
|
|
|
|
.arg(output_file.path()) |
|
|
|
|
.arg("--graph") |
|
|
|
|
.arg("default") |
|
|
|
|
.assert() |
|
|
|
|
.success(); |
|
|
|
|
output_file |
|
|
|
|
.assert("<http://example.com/s> <http://example.com/p> <http://example.com/o> .\n"); |
|
|
|
|
Ok(()) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
fn cli_load_dataset() -> Result<()> { |
|
|
|
|
let file = assert_fs::NamedTempFile::new("sample.nq")?; |
|
|
|
|
file.write_str("<http://example.com/s> <http://example.com/p> <http://example.com/o> .")?; |
|
|
|
|
fn cli_load_and_dump_dataset() -> Result<()> { |
|
|
|
|
let store_dir = TempDir::new()?; |
|
|
|
|
let input_file = NamedTempFile::new("input.nq")?; |
|
|
|
|
input_file |
|
|
|
|
.write_str("<http://example.com/s> <http://example.com/p> <http://example.com/o> <http://example.com/g> .")?; |
|
|
|
|
cli_command()? |
|
|
|
|
.arg("load") |
|
|
|
|
.arg("--location") |
|
|
|
|
.arg(store_dir.path()) |
|
|
|
|
.arg("-f") |
|
|
|
|
.arg(file.path()) |
|
|
|
|
.arg(input_file.path()) |
|
|
|
|
.assert() |
|
|
|
|
.success() |
|
|
|
|
.stdout("") |
|
|
|
|
.stderr(predicate::str::contains("1 triples loaded")); |
|
|
|
|
.success(); |
|
|
|
|
|
|
|
|
|
let output_file = NamedTempFile::new("output.nq")?; |
|
|
|
|
cli_command()? |
|
|
|
|
.arg("dump") |
|
|
|
|
.arg("--location") |
|
|
|
|
.arg(store_dir.path()) |
|
|
|
|
.arg(output_file.path()) |
|
|
|
|
.assert() |
|
|
|
|
.success(); |
|
|
|
|
output_file |
|
|
|
|
.assert("<http://example.com/s> <http://example.com/p> <http://example.com/o> <http://example.com/g> .\n"); |
|
|
|
|
Ok(()) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
fn cli_load_gzip_dataset() -> Result<()> { |
|
|
|
|
let file = assert_fs::NamedTempFile::new("sample.nq.gz")?; |
|
|
|
|
let file = NamedTempFile::new("sample.nq.gz")?; |
|
|
|
|
let mut encoder = GzEncoder::new(Vec::new(), Compression::default()); |
|
|
|
|
encoder |
|
|
|
|
.write_all(b"<http://example.com/s> <http://example.com/p> <http://example.com/o> .")?; |
|
|
|
@ -1307,42 +1417,98 @@ mod tests { |
|
|
|
|
.arg("-f") |
|
|
|
|
.arg(file.path()) |
|
|
|
|
.assert() |
|
|
|
|
.success() |
|
|
|
|
.stdout("") |
|
|
|
|
.stderr(predicate::str::contains("1 triples loaded")); |
|
|
|
|
.success(); |
|
|
|
|
Ok(()) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
fn cli_load_with_graph_and_format() -> Result<()> { |
|
|
|
|
let file = assert_fs::NamedTempFile::new("sample")?; |
|
|
|
|
file.write_str("<http://example.com/s> <http://example.com/p> <http://example.com/o> .")?; |
|
|
|
|
fn cli_load_and_dump_named_graph() -> Result<()> { |
|
|
|
|
let store_dir = TempDir::new()?; |
|
|
|
|
let input_file = NamedTempFile::new("input.nt")?; |
|
|
|
|
input_file |
|
|
|
|
.write_str("<http://example.com/s> <http://example.com/p> <http://example.com/o> .")?; |
|
|
|
|
cli_command()? |
|
|
|
|
.arg("load") |
|
|
|
|
.arg("--location") |
|
|
|
|
.arg(store_dir.path()) |
|
|
|
|
.arg("-f") |
|
|
|
|
.arg(file.path()) |
|
|
|
|
.arg(input_file.path()) |
|
|
|
|
.arg("--graph") |
|
|
|
|
.arg("http://example.com/g") |
|
|
|
|
.assert() |
|
|
|
|
.success(); |
|
|
|
|
|
|
|
|
|
let output_file = NamedTempFile::new("output.nt")?; |
|
|
|
|
cli_command()? |
|
|
|
|
.arg("dump") |
|
|
|
|
.arg("--location") |
|
|
|
|
.arg(store_dir.path()) |
|
|
|
|
.arg(output_file.path()) |
|
|
|
|
.arg("--graph") |
|
|
|
|
.arg("http://example.com/g") |
|
|
|
|
.assert() |
|
|
|
|
.success(); |
|
|
|
|
output_file |
|
|
|
|
.assert("<http://example.com/s> <http://example.com/p> <http://example.com/o> .\n"); |
|
|
|
|
Ok(()) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
fn cli_load_and_dump_with_format() -> Result<()> { |
|
|
|
|
let store_dir = TempDir::new()?; |
|
|
|
|
let input_file = NamedTempFile::new("input")?; |
|
|
|
|
input_file |
|
|
|
|
.write_str("<http://example.com/s> <http://example.com/p> <http://example.com/o> .")?; |
|
|
|
|
cli_command()? |
|
|
|
|
.arg("load") |
|
|
|
|
.arg("--location") |
|
|
|
|
.arg(store_dir.path()) |
|
|
|
|
.arg("-f") |
|
|
|
|
.arg(input_file.path()) |
|
|
|
|
.arg("--format") |
|
|
|
|
.arg("nt") |
|
|
|
|
.assert() |
|
|
|
|
.success(); |
|
|
|
|
|
|
|
|
|
let output_file = NamedTempFile::new("output")?; |
|
|
|
|
cli_command()? |
|
|
|
|
.arg("dump") |
|
|
|
|
.arg("--location") |
|
|
|
|
.arg(store_dir.path()) |
|
|
|
|
.arg(output_file.path()) |
|
|
|
|
.arg("--graph") |
|
|
|
|
.arg("http://example.com") |
|
|
|
|
.arg("default") |
|
|
|
|
.arg("--format") |
|
|
|
|
.arg("nt") |
|
|
|
|
.assert() |
|
|
|
|
.success() |
|
|
|
|
.stdout("") |
|
|
|
|
.stderr(predicate::str::contains("1 triples loaded")); |
|
|
|
|
.success(); |
|
|
|
|
output_file |
|
|
|
|
.assert("<http://example.com/s> <http://example.com/p> <http://example.com/o> .\n"); |
|
|
|
|
Ok(()) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
fn cli_load_from_stdin() -> Result<()> { |
|
|
|
|
fn cli_load_from_stdin_and_dump_to_stdout() -> Result<()> { |
|
|
|
|
let store_dir = TempDir::new()?; |
|
|
|
|
cli_command()? |
|
|
|
|
.arg("load") |
|
|
|
|
.arg("--location") |
|
|
|
|
.arg(store_dir.path()) |
|
|
|
|
.arg("--format") |
|
|
|
|
.arg("nq") |
|
|
|
|
.write_stdin("<http://example.com/s> <http://example.com/p> <http://example.com/o> .") |
|
|
|
|
.assert() |
|
|
|
|
.success(); |
|
|
|
|
|
|
|
|
|
cli_command()? |
|
|
|
|
.arg("dump") |
|
|
|
|
.arg("--location") |
|
|
|
|
.arg(store_dir.path()) |
|
|
|
|
.arg("--format") |
|
|
|
|
.arg("nq") |
|
|
|
|
.assert() |
|
|
|
|
.success() |
|
|
|
|
.stdout("") |
|
|
|
|
.stderr(predicate::str::contains("1 triples loaded")); |
|
|
|
|
.stdout("<http://example.com/s> <http://example.com/p> <http://example.com/o> .\n"); |
|
|
|
|
Ok(()) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|