READ_STREAM returns a table with the stream’s declared columns plus source-specific metadata pseudo-columns.
A stream tracks a consumed position (Kafka offsets, a Postgres replication-slot position, or a MongoDB resume token). When consume is true, the position advances with the transaction that reads, so each read returns only new data. Position updates commit atomically with the data they produced, which makes ingestion through READ_STREAM exactly-once. To load the current contents of a Postgres table or MongoDB collection rather than its changes, use READ_SNAPSHOT.
For continuous ingestion of Postgres or MongoDB change feeds, prefer a CDC table, which runs this machinery for you; direct READ_STREAM over CDC streams is most useful for inspection and custom pipelines.
Syntax
Parameters
Setting
consume to true makes the query a write operation. Read-only queries, such as standalone SELECT statements outside of CREATE TABLE AS SELECT or INSERT INTO ... SELECT, cannot consume from a stream. Either set consume => false or use READ_STREAM inside a write statement.Return type
The result has the stream’s declared columns plus pseudo-columns that depend on the source. Pseudo-columns are not included inSELECT *; reference them explicitly, double-quoted (for example SELECT "$op", * FROM ...).
Kafka streams
How raw messages map to columns is set by the stream’s
TYPE (JSON, CSV, RAW_TEXT, BINARY); see Kafka stream types.
Postgres streams
Rows arrive in commit order; uncommitted transactions are never surfaced. One read returns as many whole source transactions as the source has ready, up to
limit_rows, so a busy source is caught up in one statement rather than one statement per transaction. The result is delivered as several row batches; row order within a batch is source order, and batches themselves can arrive out of order, because a read decodes them in parallel by default. Order by $stream_row_number where delivery order matters. See Postgres CDC for delete and key-update row shapes.
Exactly-once holds when the statement consumes everything the read delivers. Do not put a LIMIT above a consuming read: a LIMIT that is satisfied before the read has drained ends it early. The rows already delivered are committed, but the stream’s position is not advanced: nothing is lost, and the next read delivers those rows again, so such a statement is at-least-once. Use limit_rows to bound a read instead; it stops at transaction boundaries and moves the position.
MongoDB streams
Besides the pseudo-columns, a MongoDB stream always has the columns
_id and doc; see CREATE STREAM. A first consuming read starts at the position CREATE STREAM recorded; it does not replay older history.
A read returns changes in the order the source committed them, until timeout_millis has passed or it has limit_rows rows, and then stops at the next transaction boundary. A multi-document transaction is never split across reads. A read can run up to 500 ms past timeout_millis while it waits for new changes. A consuming read that returns no rows still moves the stored position forward, which keeps an idle stream’s position inside the oplog. A drop, rename or invalidation of the collection ends its change stream: the read returns the changes before it, and the next read fails. A statement reads a MongoDB stream at most once, counting READ_STREAM and READ_SNAPSHOT together and a WITH query once per reference, because each read stores the stream’s whole position. A view cannot read a MongoDB stream.
Examples
Peek at a stream without consuming
Inspect change events on a CDC stream
Ingest Kafka messages into a managed table
Filter and transform while reading
Use Kafka pseudo-columns
Managing consumed positions
Kafka offsets are visible ininformation_schema.streams and can be repositioned with ALTER STREAM ... SET PARTITION ... OFFSET .... Postgres streams track their position in the source’s replication slot; MongoDB streams store a resume token. Neither is manually repositionable; see the Postgres CDC and MongoDB CDC operations sections.