Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions changelog.d/write-max-bytes.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
- Bounded writes now accept the [`write_max_bytes` session setting][write-max-bytes],
from `1` through `33554432` bytes (the unchanged 32 MiB default), with
`OMNIGRAPH_WRITE_MAX_BYTES` supplying the process default.
Row data and logical Blob payloads have independent allowances, so a Blob exactly
at the limit can accompany scalar metadata that fits its own allowance.
The default now admits up to 32 MiB of row data plus 32 MiB of Blob payload
per operation, where the previous single 32 MiB allowance covered both.
HTTP body, Blob read, compaction and URI metadata limits remain independent.

[write-max-bytes]: ../docs/user/queries/settings.md
23 changes: 20 additions & 3 deletions crates/omnigraph-api-types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,10 @@ pub struct SettingsRequest {
#[serde(default, skip_serializing_if = "Option::is_none")]
#[schema(schema_with = traversal_work_limit_schema)]
pub traversal_work_limit: Option<i64>,
/// `write_max_bytes`: independent row-data and Blob-payload allowances, `1..=33554432`.
#[serde(default, skip_serializing_if = "Option::is_none")]
#[schema(schema_with = write_max_bytes_schema)]
pub write_max_bytes: Option<i64>,
/// `history_release_bytes`: the byte budget of a branch's buffer of
/// unreleased commits for this request's mutate or merge publishes, `1024..=262144`; an
/// `i64` like `traversal_work_limit`.
Expand Down Expand Up @@ -140,6 +144,9 @@ impl SettingsRequest {
if let Some(limit) = self.traversal_work_limit {
assignments.push((SettingId::TraversalWorkLimit, SettingValue::Integer(limit)));
}
if let Some(bytes) = self.write_max_bytes {
assignments.push((SettingId::WriteMaxBytes, SettingValue::Integer(bytes)));
}
if let Some(bytes) = self.history_release_bytes {
assignments.push((SettingId::HistoryReleaseBytes, SettingValue::Integer(bytes)));
}
Expand Down Expand Up @@ -181,6 +188,10 @@ fn traversal_work_limit_schema() -> utoipa::openapi::schema::Object {
setting_schema(SettingId::TraversalWorkLimit)
}

fn write_max_bytes_schema() -> utoipa::openapi::schema::Object {
setting_schema(SettingId::WriteMaxBytes)
}

fn history_release_bytes_schema() -> utoipa::openapi::schema::Object {
setting_schema(SettingId::HistoryReleaseBytes)
}
Expand Down Expand Up @@ -2209,13 +2220,19 @@ mod tests {
merge_lineage: Some(MergeLineage::Off),
ann_nprobes: Some(7),
traversal_work_limit: Some(123),
write_max_bytes: Some(4093),
history_release_bytes: Some(2048),
};
let expected = format!(
"{{\"{}\":\"v2\",\"{}\":\"off\",\"{}\":7,\"{}\":123,\"{}\":2048}}",
request_rows[0], request_rows[1], request_rows[2], request_rows[3], request_rows[4]
"{{\"{}\":\"v2\",\"{}\":\"off\",\"{}\":7,\"{}\":123,\"{}\":4093,\"{}\":2048}}",
request_rows[0],
request_rows[1],
request_rows[2],
request_rows[3],
request_rows[4],
request_rows[5]
);
assert_eq!(request_rows.len(), 5);
assert_eq!(request_rows.len(), 6);
assert_eq!(serde_json::to_string(&populated).unwrap(), expected);
assert_eq!(
populated
Expand Down
14 changes: 10 additions & 4 deletions crates/omnigraph-cli/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,9 @@ impl GraphClient {
request.traversal_work_limit =
Some(given.get(SettingId::TraversalWorkLimit).parse()?)
}
SettingId::WriteMaxBytes => {
request.write_max_bytes = Some(given.get(SettingId::WriteMaxBytes).parse()?)
}
SettingId::HistoryReleaseBytes => {
request.history_release_bytes =
Some(given.get(SettingId::HistoryReleaseBytes).parse()?)
Expand Down Expand Up @@ -2654,9 +2657,12 @@ mod tests {
]);
let client =
GraphClient::managed(&server.origin, "knowledge", "data-credential".into()).unwrap();
let settings =
parse_set_flags(&["merge_lineage=off".to_string(), "ann_nprobes=1".to_string()])
.unwrap();
let settings = parse_set_flags(&[
"merge_lineage=off".to_string(),
"ann_nprobes=1".to_string(),
"write_max_bytes=4093".to_string(),
])
.unwrap();
client
.query(
ReadTarget::branch("main"),
Expand Down Expand Up @@ -2699,7 +2705,7 @@ mod tests {
assert!(requests[4].body.get("query_source").is_none());
assert!(requests[4].body.get("query_name").is_none());
assert!(requests[4].body.get("settings").is_none());
let field = json!({"merge_lineage": "off", "ann_nprobes": 1});
let field = json!({"merge_lineage": "off", "ann_nprobes": 1, "write_max_bytes": 4093});
assert_eq!(requests[0].path, "/graphs/knowledge/query");
assert_eq!(requests[0].body["settings"], field);
assert_eq!(
Expand Down
66 changes: 65 additions & 1 deletion crates/omnigraph-compiler/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,17 @@ pub const DEFINITIONS: &[SettingSpec] = &[
env: "OMNIGRAPH_TRAVERSAL_WORK_LIMIT",
doc: "the finite work budget shared by edge selections in one query; exhaustion terminates the query with an error",
},
SettingSpec {
name: "write_max_bytes",
kind: SettingKind::Integer {
min: 1,
max: Some(33_554_432),
},
default: "33554432",
scope: SettingScope::Request,
env: "OMNIGRAPH_WRITE_MAX_BYTES",
doc: "the byte allowance for bounded writes by mutate, load and branch merge; entity data and materialized Blob payloads each have this independent allowance; lowering it preserves the existing per-type, per-operation and per-chunk scopes",
},
SettingSpec {
name: "history_release_bytes",
kind: SettingKind::Integer {
Expand All @@ -132,6 +143,7 @@ pub enum SettingId {
AnnNprobes,
StageWriteConcurrency,
TraversalWorkLimit,
WriteMaxBytes,
HistoryReleaseBytes,
}

Expand All @@ -144,6 +156,7 @@ impl SettingId {
SettingId::AnnNprobes,
SettingId::StageWriteConcurrency,
SettingId::TraversalWorkLimit,
SettingId::WriteMaxBytes,
SettingId::HistoryReleaseBytes,
];

Expand Down Expand Up @@ -355,6 +368,7 @@ pub struct SessionSettings {
ann_nprobes: Option<usize>,
stage_write_concurrency: usize,
traversal_work_limit: u64,
write_max_bytes: u64,
history_release_bytes: usize,
}

Expand All @@ -370,6 +384,7 @@ impl Default for SessionSettings {
ann_nprobes: None,
stage_write_concurrency: 1,
traversal_work_limit: 1,
write_max_bytes: 1,
history_release_bytes: 1024,
};
for id in SettingId::ALL {
Expand Down Expand Up @@ -436,6 +451,10 @@ impl SessionSettings {
self.stage_write_concurrency =
usize::try_from(width).expect("invariant: the row's range is 1..=64");
}
SettingId::WriteMaxBytes => {
self.write_max_bytes = u64::try_from(parse_integer(id, value)?)
.expect("invariant: the row's range is 1..=33554432");
}
SettingId::HistoryReleaseBytes => {
self.history_release_bytes = usize::try_from(parse_integer(id, value)?)
.expect("invariant: the row's range is 1024..=262144");
Expand All @@ -456,6 +475,7 @@ impl SessionSettings {
SettingId::StageWriteConcurrency => {
self.stage_write_concurrency = from.stage_write_concurrency;
}
SettingId::WriteMaxBytes => self.write_max_bytes = from.write_max_bytes,
SettingId::HistoryReleaseBytes => {
self.history_release_bytes = from.history_release_bytes;
}
Expand All @@ -471,6 +491,7 @@ impl SessionSettings {
SettingId::AnnNprobes => self.ann_nprobes.unwrap_or(0).to_string(),
SettingId::StageWriteConcurrency => self.stage_write_concurrency.to_string(),
SettingId::TraversalWorkLimit => self.traversal_work_limit.to_string(),
SettingId::WriteMaxBytes => self.write_max_bytes.to_string(),
SettingId::HistoryReleaseBytes => self.history_release_bytes.to_string(),
}
}
Expand Down Expand Up @@ -524,6 +545,11 @@ impl SessionSettings {
self.stage_write_concurrency
}

/// The independent row-data and Blob-payload allowances for bounded writes.
pub fn write_max_bytes(&self) -> u64 {
self.write_max_bytes
}

/// The byte budget of a branch's buffer of unreleased commits for this
/// session's publishes; the default is the production budget and the
/// ceiling, so a session may only lower it.
Expand Down Expand Up @@ -730,6 +756,44 @@ mod tests {
assert_eq!(copied, upper);
}

#[test]
fn write_max_bytes_row_bounds_and_environment() {
let id = SettingId::parse("write_max_bytes").unwrap();
let defaults = SessionSettings::default();
assert_eq!(defaults.get(id), "33554432");
id.refuse_from_request().unwrap();
for value in ["1", "4093", "33554432"] {
let changed = defaults.clone().with("write_max_bytes", value).unwrap();
assert_eq!(changed.get(id), value);
let mut copied = defaults.clone();
copied.copy_field(&changed, id);
assert_eq!(copied, changed);
}
for value in ["0", "-1", "33554433", "9223372036854775808"] {
let error = defaults.clone().with("write_max_bytes", value).unwrap_err();
assert!(error.to_string().contains("an integer in 1..=33554432"));
}
for value in ["many", "\"4093\""] {
assert!(defaults.clone().with("write_max_bytes", value).is_err());
}
let (environment, sources) =
from_env_with(|name| (name == "OMNIGRAPH_WRITE_MAX_BYTES").then(|| "4093".to_string()))
.unwrap();
assert_eq!(environment.get(id), "4093");
assert_eq!(sources[id as usize], Source::Env);
assert_eq!(
environment
.with("write_max_bytes", "33554432")
.unwrap()
.get(id),
"33554432"
);
let error =
from_env_with(|name| (name == "OMNIGRAPH_WRITE_MAX_BYTES").then(|| "0".to_string()))
.unwrap_err();
assert!(error.to_string().contains("OMNIGRAPH_WRITE_MAX_BYTES"));
}

#[test]
fn history_release_bytes_row_bounds() {
let defaults = SessionSettings::default();
Expand Down Expand Up @@ -802,7 +866,7 @@ mod tests {
let err = SettingId::parse("merge_linage").unwrap_err();
assert_eq!(
err.to_string(),
"unknown setting `merge_linage`; expected one of engine, rrf_plan, merge_lineage, ann_nprobes, stage_write_concurrency, traversal_work_limit, history_release_bytes"
"unknown setting `merge_linage`; expected one of engine, rrf_plan, merge_lineage, ann_nprobes, stage_write_concurrency, traversal_work_limit, write_max_bytes, history_release_bytes"
);
let err = SessionSettings::default().with("engine", "v1").unwrap_err();
assert_eq!(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# issue: none
# notes: Decoded base64: bytes on an edge property are bounded by the session's write_max_bytes: 4,097 bytes are refused at 4096.
# notes: Exactly one Cites row at the end shows the refused load wrote no edge.
# notes: No omnigraph-server environment: served runs refuse settings steps and the HTTP load routes take no setting.

--- runner
timeout_ms: 10000
environments:
- target: omnigraph-engine
storage: local-filesystem
- target: omnigraph-engine-dst
storage: in-memory-object-store
seeds: [0, 42]

--- schema
node Doc {
slug: String @key
}
edge Cites: Doc -> Doc {
attachment: Blob?
}

--- seed
{"type":"Doc","data":{"slug":"a"}}
{"type":"Doc","data":{"slug":"b"}}

--- mutate
set write_max_bytes = 4096;

--- expect ok

--- load generate: v1 seed: 1 mode: append
tables:
- kind: edge
name: Cites
rows: 1
commits: 1
from: {kind: literal, value: a}
to: {kind: literal, value: b}
columns:
attachment:
kind: literal
value: "base64:QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUE="
--- expect error: decoded blob input bytes

--- load generate: v1 seed: 1 mode: append
tables:
- kind: edge
name: Cites
rows: 1
commits: 1
from: {kind: literal, value: a}
to: {kind: literal, value: b}
columns:
attachment: {kind: literal, value: "base64:QUFB"}
--- expect ok

--- query
query cites() {
match {
$a: Doc
$a cites $b
}
return { $a.slug, $b.slug }
}
--- expect unordered
{"a.slug": "a", "b.slug": "b"}
--- expect shape
a.slug: String
b.slug: String
Loading
Loading