pub struct PostgresSink { /* private fields */ }Expand description
A sink that writes JSON records to a PostgreSQL table.
Implementations§
Source§impl PostgresSink
impl PostgresSink
Sourcepub async fn new(config: PostgresSinkConfig) -> Result<Self, FaucetError>
pub async fn new(config: PostgresSinkConfig) -> Result<Self, FaucetError>
Create a new PostgreSQL sink. Establishes a connection pool.
Trait Implementations§
Source§impl Sink for PostgresSink
impl Sink for PostgresSink
Source§fn begin_overwrite<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<(), FaucetError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn begin_overwrite<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<(), FaucetError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Create the staging table as an empty clone of the target’s columns
(CREATE TABLE staging (LIKE target INCLUDING DEFAULTS)), dropping any
leftover staging from a crashed run first. The target must already exist
(the sink never auto-creates it) — overwrite replaces its rows, not its
definition.
Source§fn commit_overwrite<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<(), FaucetError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn commit_overwrite<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<(), FaucetError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Atomically replace the destination in one transaction. Full overwrite:
TRUNCATE target; INSERT INTO target SELECT * FROM staging; DROP staging.
Scoped/windowed overwrite (#518): DELETE FROM target WHERE <scope>; INSERT …; DROP staging — only the in-scope rows are replaced, the rest
preserved. Postgres runs TRUNCATE and DDL transactionally, so a failure
rolls the whole swap back and the prior rows survive.
Source§fn abort_overwrite<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<(), FaucetError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn abort_overwrite<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<(), FaucetError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Drop the staging table so a failed/cancelled overwrite leaves nothing behind. Best-effort — the destination was never touched.
Source§fn current_schema<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<Option<Value>, FaucetError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn current_schema<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<Option<Value>, FaucetError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Read the live destination schema from pg_catalog as an
infer_schema-shaped object ({"type":"object","properties":{…}}), or
None when the target table does not exist yet (issue #194).
Reuses the AutoMap column-discovery query shape (scoped to the exact
relation via to_regclass), additionally reading a.attnotnull so
nullability round-trips through pg_udt_to_json_schema.
Source§fn evolve_schema<'life0, 'life1, 'async_trait>(
&'life0 self,
evolution: &'life1 SchemaEvolution,
) -> Pin<Box<dyn Future<Output = Result<(), FaucetError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn evolve_schema<'life0, 'life1, 'async_trait>(
&'life0 self,
evolution: &'life1 SchemaEvolution,
) -> Pin<Box<dyn Future<Output = Result<(), FaucetError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Apply an additive schema evolution (new columns, lossless widenings,
nullability relaxations) to the destination table. Idempotent —
ADD COLUMN IF NOT EXISTS, and re-running the same TYPE / DROP NOT NULL
is a no-op (issue #194).
Source§fn check<'life0, 'life1, 'async_trait>(
&'life0 self,
ctx: &'life1 CheckContext,
) -> Pin<Box<dyn Future<Output = Result<CheckReport, FaucetError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn check<'life0, 'life1, 'async_trait>(
&'life0 self,
ctx: &'life1 CheckContext,
) -> Pin<Box<dyn Future<Output = Result<CheckReport, FaucetError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Preflight connectivity probe (faucet doctor).
Acquires a connection from the existing pool and runs SELECT 1. This
is non-mutating and idempotent — it validates that the database is
reachable and the credentials are accepted without writing anything.
Source§fn write_batch<'life0, 'life1, 'async_trait>(
&'life0 self,
records: &'life1 [Value],
) -> Pin<Box<dyn Future<Output = Result<usize, FaucetError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn write_batch<'life0, 'life1, 'async_trait>(
&'life0 self,
records: &'life1 [Value],
) -> Pin<Box<dyn Future<Output = Result<usize, FaucetError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Write records to PostgreSQL.
When config.batch_size > 0 and the input slice is larger than
batch_size, the slice is split into chunks of batch_size rows and
each chunk is sent as a separate multi-row INSERT. When
config.batch_size == 0, the entire slice is sent in a single
INSERT — useful when upstream StreamPages are already sized for
Postgres’ per-statement bind-parameter limit (~65 535 / num_columns
in AutoMap mode).
Acquires one connection from the pool and routes all chunks through it. Each INSERT executes as its own autocommit statement — identical observable behaviour to executing directly on the pool, while keeping the same connection for the entire call (avoids repeated pool-checkout overhead on large batches).
Source§fn write_batch_partial<'life0, 'life1, 'async_trait>(
&'life0 self,
records: &'life1 [Value],
) -> Pin<Box<dyn Future<Output = Result<Vec<RowOutcome>, FaucetError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn write_batch_partial<'life0, 'life1, 'async_trait>(
&'life0 self,
records: &'life1 [Value],
) -> Pin<Box<dyn Future<Output = Result<Vec<RowOutcome>, FaucetError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Write a batch and report per-row outcomes.
In append mode this delegates to write_batch and
maps a single success onto an all-Ok(()) vector (the trait default).
In upsert/delete mode the good rows are applied (upserts + deletes), and
only the rows whose key could not be extracted (missing / null key) are
reported as Err so the pipeline routes them to the DLQ per-row instead
of sending the whole page.
Source§fn connector_name(&self) -> &'static str
fn connector_name(&self) -> &'static str
connector label on metrics and the
connector attribute on spans. See Source::connector_name.Source§fn config_schema(&self) -> Value
fn config_schema(&self) -> Value
Source§fn supports_cleanup(&self) -> bool
fn supports_cleanup(&self) -> bool
false; the upsert-capable sinks override it.Source§fn cleanup_scope<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
scope: &'life1 BTreeMap<String, Value>,
seen: &'life2 SeenKeys,
) -> Pin<Box<dyn Future<Output = Result<u64, FaucetError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
fn cleanup_scope<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
scope: &'life1 BTreeMap<String, Value>,
seen: &'life2 SeenKeys,
) -> Pin<Box<dyn Future<Output = Result<u64, FaucetError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Source§fn supported_write_modes(&self) -> &'static [WriteMode]
fn supported_write_modes(&self) -> &'static [WriteMode]
WriteMode::Upsert /
WriteMode::Delete. The CLI rejects a
configured mode that is not in this set at config-load time.Source§fn is_overwrite(&self) -> bool
fn is_overwrite(&self) -> bool
WriteMode::Overwrite). Read moreSource§fn dedups_by_key(&self) -> bool
fn dedups_by_key(&self) -> bool
write_mode: upsert (or delete) with a non-empty key, so
re-applying a record with the same key converges instead of
duplicating. Default: false. Read moreSource§fn supports_schema_evolution(&self) -> bool
fn supports_schema_evolution(&self) -> bool
evolve_schema. Default: false. The CLI rejects
on_drift: evolve against a sink that returns false at config-load.Source§fn dataset_uri(&self) -> String
fn dataset_uri(&self) -> String
Source§fn supports_idempotent_writes(&self) -> bool
fn supports_idempotent_writes(&self) -> bool
false (at-least-once only). Read moreSource§fn last_committed_token<'life0, 'life1, 'async_trait>(
&'life0 self,
scope: &'life1 str,
) -> Pin<Box<dyn Future<Output = Result<Option<String>, FaucetError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn last_committed_token<'life0, 'life1, 'async_trait>(
&'life0 self,
scope: &'life1 str,
) -> Pin<Box<dyn Future<Output = Result<Option<String>, FaucetError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
scope, or None if this sink has
never committed under that scope. Default: None.Source§fn write_batch_idempotent<'life0, 'life1, 'life2, 'life3, 'async_trait>(
&'life0 self,
records: &'life1 [Value],
scope: &'life2 str,
token: &'life3 str,
) -> Pin<Box<dyn Future<Output = Result<usize, FaucetError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
fn write_batch_idempotent<'life0, 'life1, 'life2, 'life3, 'async_trait>(
&'life0 self,
records: &'life1 [Value],
scope: &'life2 str,
token: &'life3 str,
) -> Pin<Box<dyn Future<Output = Result<usize, FaucetError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
Source§fn flush<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<(), FaucetError>> + Send + 'async_trait>>where
'life0: 'async_trait,
Self: 'async_trait,
fn flush<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<(), FaucetError>> + Send + 'async_trait>>where
'life0: 'async_trait,
Self: 'async_trait,
Source§fn sink_guarantee(&self) -> SinkGuarantee
fn sink_guarantee(&self) -> SinkGuarantee
SinkGuarantee. Read moreSource§fn supports_staged_load(&self) -> bool
fn supports_staged_load(&self) -> bool
COPY / COPY INTO / s3() table function) (#528). Default
false; warehouse sinks that honour a StagingSpec
override it. Object-safe (no args, no generics) so Box<dyn Sink> is
unaffected.Auto Trait Implementations§
impl !RefUnwindSafe for PostgresSink
impl !UnwindSafe for PostgresSink
impl Freeze for PostgresSink
impl Send for PostgresSink
impl Sync for PostgresSink
impl Unpin for PostgresSink
impl UnsafeUnpin for PostgresSink
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more