with block and CTE rules, see Supported SQL — CTE and WITH syntax.
Why Use CTEs
Consider an app where users belong to organizations. Several tables need to filter by the user’s organizations:auto_subscribe settings or separate subscription parameters), use a global CTE to share the logic across streams instead.
Stream-Level CTEs
Define CTEs in awith block inside a stream. Each CTE has a name and a SELECT query, and is available only within that stream:
Global CTEs
When the same filtering logic is needed across streams that cannot be merged — for example because they have differentauto_subscribe settings or different subscription parameters — define the CTE once in a top-level with block. All streams can then reference it without repeating the definition:
with block. Local CTEs take precedence over global CTEs when names conflict, so you can override a global CTE for a specific stream:
Using CTEs in Queries
Once defined — either in a stream’s localwith block or in the top-level global with block — use the CTE name in the stream’s query or queries. You can use it like a subquery or join it as if it were a table.
Short-hand syntax (when the CTE has exactly one column):
IN cte_name is equivalent to IN (SELECT * FROM cte_name). If the CTE has more than one column, this form is an error; use explicit subquery or join syntax instead.
Explicit subquery syntax (when you need to select specific columns):
INNER JOIN is supported:
Combining with Multiple Queries
CTEs work well with thequeries feature (multiple queries per stream). This lets you share the CTE and keep all query results in one stream: the client only needs to manage one subscription instead of multiple.
Complete Example
A full configuration using both global and stream-level CTEs. Theaccessible_projects logic is shared across streams via a global CTE; user_orgs is only needed by org_and_projects, so it stays stream-level:
Limitations
The following rules apply to CTEs. For the full syntax reference, see Supported SQL — CTE and WITH syntax. CTE names must not shadow source table names. This prevents hard-to-debug situations where a global CTE silently changes the behavior of a stream defined elsewhere in the config. Local CTEs take precedence over global CTEs. If a stream defines a CTE with the same name as a global CTE, the stream-level definition is used within that stream. CTEs cannot reference other CTEs. Each CTE must be self-contained:IN cte_name works only when the CTE has exactly one column. If the CTE has multiple columns, use explicit subquery syntax or join the CTE as a table.
Stream-level CTE names take precedence over table/collection names. If you define a stream-level CTE with the same name as a database table/collection, the CTE is used within that stream. Choose distinct names to avoid confusion.