use faucet_common_redshift::RedshiftConnection;
use faucet_core::{DEFAULT_BATCH_SIZE, FaucetError};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, Default, PartialEq)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum RedshiftReplication {
#[default]
Full,
Incremental {
column: String,
initial_value: Value,
},
}
fn default_max_connections() -> u32 {
10
}
fn default_batch_size() -> usize {
DEFAULT_BATCH_SIZE
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
pub struct RedshiftSourceConfig {
#[serde(flatten)]
pub connection: RedshiftConnection,
#[schemars(extend("x-faucet-sql" = true))]
pub query: String,
#[serde(default)]
pub params: Vec<Value>,
#[serde(default = "default_max_connections")]
pub max_connections: u32,
#[serde(default = "default_batch_size")]
pub batch_size: usize,
#[serde(default)]
pub replication: RedshiftReplication,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub state_key: Option<String>,
}
impl RedshiftSourceConfig {
pub fn validate(&self) -> Result<(), FaucetError> {
if self.query.trim().is_empty() {
return Err(FaucetError::Config(
"redshift: `query` must not be empty".into(),
));
}
faucet_core::validate_batch_size(self.batch_size)?;
let RedshiftReplication::Incremental {
column,
initial_value,
} = &self.replication
else {
return Ok(());
};
if column.trim().is_empty() {
return Err(FaucetError::Config(
"redshift: incremental replication `column` must not be empty".into(),
));
}
if initial_value.is_null() {
return Err(FaucetError::Config(
"redshift: incremental replication requires a non-null `initial_value`".into(),
));
}
if faucet_core::replication::cursor::strict_comparison(&self.query, BOOKMARK_TOKEN) {
return Err(FaucetError::Config(format!(
"redshift: compare `{BOOKMARK_TOKEN}` with `>=`, not `>`: the source re-reads \
the bookmark value and drops the rows it already emitted, and a strict \
comparison skips rows that share it"
)));
}
Ok(())
}
}
pub const BOOKMARK_TOKEN: &str = "${bookmark}";
#[cfg(test)]
mod tests;