CREATE TABLE: PostgreSQL source table
View as MarkdownIn Materialize, you can create read-only tables from PostgreSQL sources created using the new syntax.
Syntax
INSERT/UPDATE/DELETE) on
these tables.
To create a read-only table from a source connected (via native connector) to an external PostgreSQL:
CREATE TABLE [IF NOT EXISTS] <table_name> FROM SOURCE <source_name> (REFERENCE <upstream_table>)
[WITH (
TEXT COLUMNS (<column_name> [, ...])
| EXCLUDE COLUMNS (<column_name> [, ...])
| PARTITION BY (<column_name> [, ...])
[, ...]
)]
;
| Syntax element | Description | ||||||||
|---|---|---|---|---|---|---|---|---|---|
| IF NOT EXISTS |
Optional. If specified, do not throw an error if the table with the same name already exists. Instead, issue a notice and skip the table creation.
💡 Tip: The
IF NOT EXISTS option can be useful for idempotent table creation scripts.
However, it only checks whether a table with the same name exists, not whether
the existing table matches the specified table definition. Use with validation
logic to ensure the existing table is the one you intended to create.
|
||||||||
<table_name>
|
The name of the table to create. Names for tables must follow the naming guidelines. | ||||||||
<source_name>
|
The name of the source associated with the reference object from which to create the table. | ||||||||
| (REFERENCE <upstream_table>) |
The name of the upstream table from which to create the table. You can create multiple tables from the same upstream table. To find the upstream tables available in your
source, you can use the following query,
substituting your source name for |
||||||||
| WITH (<with_option>[,…]) |
The following
|
Details
DDL transaction block
For performance, when issuing multiple CREATE TABLE FROM SOURCE... statements,
use within a transaction block.
Source-populated tables and snapshotting
Creating the tables from sources starts the snapshotting process. Snapshotting syncs the currently available data into Materialize. Because the initial snapshot is persisted in the storage layer atomically (i.e., at the same ingestion timestamp), you are not able to query the table until snapshotting is complete.
Supported data types
Materialize natively supports the following PostgreSQL types (including the array type for each of the types):
boolbpcharbyteachardatedaterangefloat4float8int2int2vectorint4int4rangeint8int8rangeintervaljsonjsonbnumericnumrangeoidtexttimetimestamptimestamptztsrangetstzrangeuuidvarchar
Replicating tables that contain unsupported data types is
possible via the TEXT COLUMNS option. The specified columns will be
treated as text; i.e., will not have the expected PostgreSQL type
features. For example:
-
enum: When decoded astext, the implicit ordering of the original PostgreSQLenumtype is not preserved; instead, Materialize will sort values astext. -
money: When decoded astext, resultingtextvalue cannot be cast back tonumeric, since PostgreSQL adds typical currency formatting to the output.
Handling table schema changes
The use of CREATE SOURCE (new syntax) with CREATE TABLE FROM SOURCE allows
for the handling of the upstream DDL changes, specifically adding or dropping
columns in the upstream tables, without downtime. For details, see PostgreSQL:
Handling upstream schema changes with zero
downtime.
See also Handling upstream operations for additional upstream operation considerations.
Inherited tables
When using PostgreSQL table inheritance,
PostgreSQL serves data from SELECTs as if the inheriting tables’ data is
also present in the inherited table. However, both PostgreSQL’s logical
replication and COPY only present data written to the tables themselves,
i.e. the inheriting data is not treated as part of the inherited table.
PostgreSQL sources use logical replication and COPY to ingest table data,
so inheriting tables’ data will only be ingested as part of the inheriting
table, i.e. in Materialize, the data will not be returned when serving
SELECTs from the inherited table.
You can mimic PostgreSQL’s SELECT behavior with inherited tables by
creating a materialized view that unions data from the inherited and
inheriting tables (using UNION ALL). However, if new tables inherit from
the table, data from the inheriting tables will not be available in the
view. You will need to add the inheriting tables via CREATE TABLE .. FROM SOURCE and create a new view (materialized or non-) that unions the new
table.
Handling upstream operations
This section describes how changes to upstream tables that Materialize ingests affect the corresponding Materialize tables.
Adding a column
When you add a new column to your upstream table, Materialize continues to ingest only the existing columns.
To incorporate the new column:
-
If using the new
CREATE SOURCEandCREATE TABLE FROM SOURCEsyntax, create a new table from the source. See Handle upstream column addition. -
If using the legacy
CREATE SOURCE ... FOR ...syntax that creates subsources, useDROP SOURCEto drop the affected subsource, and then add the table back to the source usingALTER SOURCE ... ADD SUBSOURCE. The re-added subsource includes the new column.
Dropping a column
Dropping columns that Materialize does not ingest (for example, columns added after the source was created, or columns that are excluded) is supported. As these columns were never ingested, you can drop them without issue.
If your Materialize source ingests a column, dropping that column from your upstream table puts the affected table into an error state.
-
If using the new
CREATE SOURCEandCREATE TABLE FROM SOURCEsyntax, you can safely drop a column by first ignoring it in Materialize. See Handle upstream column drop. -
If using legacy
CREATE SOURCE ... FOR ...syntax, useDROP SOURCEto drop the affected subsource, and then add the table back to the source usingALTER SOURCE ... ADD SUBSOURCE.
Changing constraints
Materialize ignores the following constraint changes: foreign
key, CHECK, and EXCLUSION.
As such, you can add or drop them without affecting ingestion.
Materialize also ignores NOT NULL, UNIQUE, and PRIMARY KEY constraints that
are added after the Materialize table is created (that is, the table was created
without them). Adding such a constraint, and later dropping it, does not affect
ingestion.
Dropping a NOT NULL, UNIQUE, or PRIMARY KEY constraint that existed when
the table was created puts the affected table into an error state.
Changing a column’s data type
Changing an ingested column’s data type upstream puts the affected
Materialize table into an error state unless the column was ingested as text
via the TEXT COLUMNS option. Ingestion for that table stops, and you must
drop and recreate the table in Materialize to resume ingestion.
Renaming a column
Renaming a column that Materialize ingests puts the affected table into an error state. Ingestion for that table stops, and you must drop and recreate the table in Materialize to resume ingestion.
Table-level operations
The following upstream operations put the affected table into an error state. Ingestion for that table stops, and you must drop and recreate the affected table in Materialize to resume:
- Dropping a table (
DROP TABLE), removing it from the publication (ALTER PUBLICATION ... DROP TABLE), or dropping the publication (DROP PUBLICATION). - Renaming a table or moving it to a different schema.
- Setting a table’s replica identity to anything other than
FULL(ALTER TABLE ... REPLICA IDENTITY). - Truncating a table (
TRUNCATE). To clear a table without putting it into an error state, use an unqualifiedDELETE FROM t;instead.
Privileges
The privileges required to execute this statement are:
CREATEprivileges on the containing schema.USAGEprivileges on all types used in the table definition.USAGEprivileges on the schemas that all types in the statement are contained in.
Examples
Create a table
You must be on v26+ to use the new syntax.
The example assumes you have configured your upstream PostgreSQL 11+ (i.e., enabled logical replication, created the publication for the various tables and replication user, and updated the network configuration).
For details about configuring your upstream system, see the PostgreSQL integration guides.
To create new read-only tables from a source table, use the CREATE TABLE ... FROM SOURCE ... (REFERENCE <upstream_table>) statement in a DDL
transaction block. The following example
creates read-only tables items and orders from the PostgreSQL
source’s public.items and public.orders tables (the schema is public).
-
Although the example creates the tables with the same names as the upstream tables, the tables in Materialize can have names that differ from the referenced table names.
-
For supported PostgreSQL data types, refer to supported types.
/* This example assumes:
- In the upstream PostgreSQL, you have defined:
- replication user and password with the appropriate access.
- a publication named `mz_source` for the `items` and `orders` tables.
- In Materialize:
- You have created a secret for the PostgreSQL password.
- You have defined the connection to the upstream PostgreSQL.
- You have used the connection to create a source.
For example (substitute with your configuration):
CREATE SECRET pgpass AS '<replication user password>'; -- substitute
CREATE CONNECTION pg_connection TO POSTGRES (
HOST '<hostname>', -- substitute
DATABASE <db>, -- substitute
USER <replication user>, -- substitute
PASSWORD SECRET pgpass
-- [, <network security configuration> ]
);
CREATE SOURCE pg_source
FROM POSTGRES CONNECTION pg_connection (
PUBLICATION 'mz_source' -- substitute
);
*/
BEGIN;
CREATE TABLE items
FROM SOURCE pg_source(REFERENCE public.items)
;
CREATE TABLE orders
FROM SOURCE pg_source(REFERENCE public.orders)
;
COMMIT;
Creating the tables from sources starts the snapshotting process. Snapshotting syncs the currently available data into Materialize. Because the initial snapshot is persisted in the storage layer atomically (i.e., at the same ingestion timestamp), you are not able to query the table until snapshotting is complete.
IF NOT EXISTS option can be useful for idempotent table creation scripts.
However, it only checks whether a table with the same name exists, not whether
the existing table matches the specified table definition. Use with validation
logic to ensure the existing table is the one you intended to create.
Once the snapshotting process completes and the table is in the running state, you can query the table:
SELECT * FROM items;