use std::error::Error;
use musli::sqlite_jsonb;
use musli::{Decode, Encode};
use sqll::Connection;
#[derive(Debug, Encode, Decode)]
struct Package {
name: String,
version: String,
keywords: Vec<String>,
metadata: Metadata,
}
#[derive(Debug, Encode, Decode)]
struct Metadata {
downloads: u64,
rating: Option<f64>,
description: String,
}
#[derive(Debug, Decode)]
struct Name<'a> {
name: &'a str,
version: &'a str,
}
fn main() -> Result<(), Box<dyn Error>> {
let c = Connection::open_in_memory()?;
c.execute(
"CREATE TABLE packages (
id INTEGER PRIMARY KEY,
doc BLOB NOT NULL
)",
)?;
let mut insert = c.prepare("INSERT INTO packages (doc) VALUES (?)")?;
for package in packages() {
insert.execute(sqlite_jsonb::to_vec(&package)?)?;
insert.reset()?;
}
let mut insert = c.prepare("INSERT INTO packages (doc) VALUES (jsonb(?))")?;
insert.execute(
r#"{
"name": "sqll",
"version": "0.14.3",
"keywords": ["database", "sqlite"],
"metadata": {
"downloads": 1200,
"rating": null,
"description": "Doesn't get in your \"way\""
}
}"#,
)?;
section("What SQLite sees");
let mut stmt = c.prepare(
"SELECT doc ->> '$.name',
doc ->> '$.metadata.downloads',
json_type(doc, '$.metadata.rating'),
json_array_length(doc, '$.keywords')
FROM packages
ORDER BY doc ->> '$.metadata.downloads' DESC",
)?;
while let Some((name, downloads, rating, keywords)) =
stmt.next::<(String, i64, String, i64)>()?
{
println!("{name}: {downloads} downloads, rating {rating}, {keywords} keywords");
}
section("Querying inside the documents");
let mut stmt = c.prepare(
"SELECT DISTINCT packages.doc ->> '$.name'
FROM packages, json_each(packages.doc, '$.keywords')
WHERE json_each.value = ?
ORDER BY 1",
)?;
stmt.bind("serialization")?;
while let Some(name) = stmt.next::<String>()? {
println!("tagged `serialization`: {name}");
}
section("Decoding whole rows");
let mut stmt = c.prepare("SELECT doc FROM packages ORDER BY id")?;
while let Some(doc) = stmt.next::<&[u8]>()? {
let package: Package = sqlite_jsonb::from_slice(doc)?;
println!(
"{} {} ({} bytes): {}",
package.name,
package.version,
doc.len(),
package.metadata.description
);
}
section("Decoding only what is needed");
let mut stmt = c.prepare("SELECT doc FROM packages ORDER BY id")?;
while let Some(doc) = stmt.next::<&[u8]>()? {
let name: Name<'_> = sqlite_jsonb::from_slice(doc)?;
println!("{} {}", name.name, name.version);
}
section("Decoding a sub-document");
let mut stmt = c.prepare(
"SELECT jsonb_extract(doc, '$.metadata')
FROM packages
WHERE doc ->> '$.name' = ?",
)?;
stmt.bind("musli")?;
if let Some(doc) = stmt.next::<&[u8]>()? {
let metadata: Metadata = sqlite_jsonb::from_slice(doc)?;
println!("{metadata:?}");
}
section("The bytes themselves");
let blob = sqlite_jsonb::to_vec(&(1u32, "two", true))?;
let mut stmt = c.prepare("SELECT json(?)")?;
stmt.bind(&blob[..])?;
println!("musli wrote: {}", hex(&blob));
println!(
"SQLite reads it as: {}",
stmt.next::<String>()?.unwrap_or_default()
);
Ok(())
}
fn packages() -> Vec<Package> {
vec![
Package {
name: String::from("musli"),
version: String::from("0.1.5"),
keywords: vec![String::from("serialization"), String::from("no_std")],
metadata: Metadata {
downloads: 42_000,
rating: Some(4.5),
description: String::from("A flexible and efficient serialization framework"),
},
},
Package {
name: String::from("musli-zerocopy"),
version: String::from("0.1.5"),
keywords: vec![String::from("serialization"), String::from("zerocopy")],
metadata: Metadata {
downloads: 7_500,
rating: Some(4.8),
description: String::from("Zero copy \"primitives\"\nfor Müsli"),
},
},
]
}
fn section(title: &str) {
println!();
println!("== {title} ==");
}
fn hex(bytes: &[u8]) -> String {
use std::fmt::Write;
bytes.iter().fold(String::new(), |mut s, b| {
_ = write!(s, "{b:02x}");
s
})
}