Skip to main content

kernel/
config.rs

1mod aggregate;
2mod content_hash;
3mod expression;
4mod field;
5mod filter;
6mod flusso_type;
7mod index_mapping;
8mod join;
9mod projection;
10mod schema;
11mod secret;
12mod soft_delete;
13mod transform;
14
15pub use aggregate::*;
16pub use content_hash::*;
17pub use expression::*;
18pub use field::*;
19pub use filter::*;
20pub use flusso_type::*;
21pub use index_mapping::*;
22pub use join::*;
23pub use schema::*;
24pub use secret::*;
25pub use soft_delete::*;
26pub use transform::*;
27
28use serde::{Deserialize, Serialize};
29
30use crate::common;
31
32/// What the pipeline does when a sink **rejects a document at the item level** —
33/// it accepted the batch but refused a specific document (a mapping conflict, a
34/// malformed value). Distinct from a flush-wide failure, which always stops the
35/// run. Set globally on the config and overridable per index (both live in the
36/// `config` crate's `Config`/`Index`, which assemble this policy).
37#[derive(
38    Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize, schemars::JsonSchema,
39)]
40#[serde(rename_all = "snake_case")]
41pub enum FailurePolicy {
42    /// Stop the run on the first rejected document. The batch is left
43    /// unconfirmed and redelivered on restart, so a persistently-bad document
44    /// halts sync until the data is fixed or the policy is changed. The default,
45    /// because dropping data should be opt-in.
46    #[default]
47    Stop,
48    /// Quarantine each rejected document (surfaced via metrics/status and logs)
49    /// and continue: the rest of the batch is applied, the slot advances, and
50    /// the poison is not redelivered — it simply never lands until its source
51    /// row changes again.
52    Skip,
53}
54
55/// The shape of a single search document: a root table and the fields built
56/// from its columns and related tables.
57#[derive(Debug, Clone, Hash, Serialize, Deserialize)]
58pub struct IndexSchema {
59    pub version: u8,
60    pub table: common::TableName,
61    pub db_schema: DatabaseSchema,
62    #[serde(default, skip_serializing_if = "Option::is_none")]
63    pub primary_key: Option<common::ColumnName>,
64    /// Reserved: the column whose value would become the document `_id`.
65    /// Not honored yet — the schema layer rejects a set `doc_id`, so this is
66    /// always `None`; the `_id` is derived from [`primary_key`](Self::primary_key).
67    #[serde(default, skip_serializing_if = "Option::is_none")]
68    pub doc_id: Option<common::ColumnName>,
69    #[serde(default, skip_serializing_if = "Option::is_none")]
70    pub soft_delete: Option<SoftDelete>,
71    /// Root filters: only root rows matching every filter become documents.
72    /// A row that stops matching emits a tombstone, exactly like
73    /// [`soft_delete`](Self::soft_delete) — both fold into the document
74    /// query's `WHERE`, so "no row came back" means "this document should not
75    /// exist".
76    #[serde(default, skip_serializing_if = "Option::is_none")]
77    pub filters: Option<Vec<Filter>>,
78    pub fields: Vec<Field>,
79}