Reference: Tables
A table stores rows with a fixed set of typed columns. This page covers creating, altering, and dropping tables, temporary tables, and table privileges.
CREATE TABLE trees (
id int GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
species text NOT NULL,
height_m numeric CHECK (height_m > 0)
);
INSERT INTO trees (species, height_m) VALUES ('Oak', 12.4);CREATE TABLE
CREATE TABLE creates a new relation with the specified columns.
Usage example:
CREATE TABLE species (
id int PRIMARY KEY,
common_name text,
botanical_name text NOT NULL,
genus_id int REFERENCES genus(id)
);After executing this statement, you can find the created table in the pg_tables system view.
Columns
Column definitions are specified as: name type constraint.
Column names can be any identifier. However, for arbitrary character sequences, you need to enclose them in double
quotes: "complex name".
You can find a list of supported types in the data types reference.
CedarDB supports the following column-level constraints:
DEFAULT expression: default value used when no value is supplied on INSERT.GENERATED ALWAYS AS IDENTITYandGENERATED BY DEFAULT AS IDENTITY: sequence / serial column.NOT NULL: rejects null values.UNIQUE: enforces uniqueness for this column.PRIMARY KEY: equivalent to UNIQUE and NOT NULL. Only one is allowed per table.REFERENCES other_table(col): foreign key into another table.CHECK (condition): rejects rows that do not satisfy the condition.COLLATE collation: the collation for a text column.
Constraints
The UNIQUE, PRIMARY KEY, FOREIGN KEY, and CHECK constraints can be specified per-column or at the table level (after all column definitions), and can optionally be given a name using the CONSTRAINT keyword:
CREATE TABLE observations (
id int,
species int,
site int,
observed_at date,
CONSTRAINT observations_pk PRIMARY KEY (id),
CONSTRAINT observations_species_fk FOREIGN KEY (species) REFERENCES species (id),
CONSTRAINT observations_site_fk FOREIGN KEY (site) REFERENCES sites (id),
CONSTRAINT observations_unique UNIQUE (species, site, observed_at),
CONSTRAINT observations_id_positive CHECK (id > 0)
);The CONSTRAINT <name> part can be omitted entirely, in which case CedarDB automatically assigns a default name using the same conventions as PostgreSQL:
- Primary key:
tablename_pkey - Unique:
tablename_colname_key - Foreign key:
tablename_colname_fkey - Check:
tablename_colname_check
These default names can be used just like explicit names, e.g., to drop a constraint with ALTER TABLE ... DROP CONSTRAINT <name>.
Naming is not supported for NOT NULL.
Foreign Key Actions
CedarDB supports the following referential actions on foreign keys:
ON DELETE CASCADE: deletes child rows when the referenced row is deleted.ON DELETE RESTRICT: prevents deletion if child rows exist.ON DELETE NO ACTION: same as RESTRICT. This is the default.ON DELETE SET NULL: sets the referencing columns of child rows to null.ON DELETE SET DEFAULT: sets the referencing columns of child rows to their default value.ON UPDATE CASCADE: updates child rows when the referenced key value changes.ON UPDATE RESTRICT: prevents updating the referenced key if child rows exist.ON UPDATE NO ACTION: same as RESTRICT. This is the default.ON UPDATE SET NULLandON UPDATE SET DEFAULT: like theON DELETEvariants, when the referenced key value changes.
CREATE TABLE genus (id int PRIMARY KEY, name text);
CREATE TABLE species (
id int PRIMARY KEY,
genus_id int REFERENCES genus (id) ON DELETE SET NULL
);
INSERT INTO genus VALUES (1, 'Quercus');
INSERT INTO species VALUES (10, 1);
DELETE FROM genus WHERE id = 1;
SELECT * FROM species; id | genus_id
----+----------
10 |For a multi-column foreign key, ON DELETE SET NULL (column, ...) and ON DELETE SET DEFAULT (column, ...) change only the listed columns.
With SET DEFAULT, the default value must exist in the referenced table, otherwise the DELETE or UPDATE fails with a foreign key violation.
A column without a default is set to null.
A foreign key can reference a primary key or a unique constraint of the referenced table. Foreign keys between a temporary and a permanent table are not supported, in either direction.
CedarDB checks foreign keys immediately, at the end of each statement.
Deferred constraint checking is not supported: column constraints with DEFERRABLE will fail and DEFERRABLE / INITIALLY DEFERRED on a table constraint (FOREIGN KEY or UNIQUE) is accepted but will be ignored.
Options
Create a temporary table that exists only for the current session (see Temporary tables):
CREATE TEMP TABLE temp_results (id int, score numeric);Do not throw an error if a table with the same name already exists:
CREATE TABLE IF NOT EXISTS species (id int PRIMARY KEY, common_name text);Create a table using the column layout of an existing table.
This copies the column names, types, and NOT NULL constraints, but not other constraints, defaults, identity columns, or indexes.
INCLUDING options are not supported:
CREATE TABLE species_archive (LIKE species);Create a table that stores all compressed data on a remote server previously created with name remote_storage
(see CREATE SERVER for more information):
CREATE TABLE remote_species (...) WITH (server = remote_storage);Set the compression method used for cold on-disk data, in addition to CedarDB’s lightweight
encoding (see the cedardb_compression_info system table for per-column
compression statistics). Supported values are zstd (the default) and none:
CREATE TABLE species (...) WITH (compression = zstd);
CREATE TABLE species_uncompressed (...) WITH (compression = none);Partitioning
You can create a table partitioned by a hash of one or more columns:
CREATE TABLE observations (
id int,
species int,
site text
) PARTITION BY HASH (id);Unlike PostgreSQL, CedarDB partitions hash-partitioned tables automatically.
So, you don’t need to create individual partitions with CREATE TABLE ... PARTITION OF ... FOR VALUES WITH (MODULUS ..., REMAINDER ...).
Instead, CedarDB creates up to 1024 partitions per table, with lazily created partitions on the first insert into them.
The table behaves like a single table for all queries and modifications.
Partitioning has the following restrictions:
- Only
HASHpartitioning is supported.RANGEandLISTpartitioning are not yet implemented. - The partition key must consist of plain column names. Expressions,
COLLATE, and operator classes are not supported. - Explicit partitions (
PARTITION OF) andALTER TABLE ... ATTACH / DETACH PARTITIONare not supported. - You can’t drop a column that is part of the partition key.
Identity Columns
Identity columns automatically generate unique integer values:
CREATE TABLE specimens (
id int GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
accession text NOT NULL,
collected date
);
-- id is assigned automatically
INSERT INTO specimens (accession, collected) VALUES ('HRB-00142', '2024-03-15');With GENERATED BY DEFAULT AS IDENTITY, you can supply a value explicitly on INSERT.
With GENERATED ALWAYS AS IDENTITY, you need to use OVERRIDING SYSTEM VALUE to override the generated value.
Permissions
To create a table, you need the CREATE privilege on the target schema.
By default, only the database owner has the CREATE privilege on the public schema.
Creating a TEMPORARY table instead requires the TEMPORARY privilege on the current database, which every role has by default.
A foreign key additionally requires the REFERENCES privilege on the referenced table.
A table on a remote server (WITH (server = ...)) additionally requires the USAGE privilege on the server.
CREATE TABLE AS and SELECT INTO additionally require the privileges to run the query.
The creator owns the new table.
CREATE TABLE AS
CREATE TABLE AS creates a table with a schema inferred from the output of a query, and optionally populates it with the query result.
Usage example:
CREATE TABLE recent_observations AS
SELECT *
FROM observations
WHERE observed_at >= now()::date - INTERVAL '1' year;Create the table structure without copying any rows:
CREATE TABLE observations_archive AS
SELECT * FROM observations
WITH NO DATA;Override the output column names:
CREATE TABLE taxon_counts (taxon, cnt) AS
SELECT family, count(*) FROM species GROUP BY family;Create a temporary table that is automatically dropped at the end of the transaction:
BEGIN;
CREATE TEMP TABLE session_results ON COMMIT DROP AS
SELECT id, score FROM compute_scores();
COMMIT;SELECT INTO
CedarDB also supports the alternative SELECT INTO syntax for compatibility with PostgreSQL:
SELECT *
INTO recent_observations
FROM observations
WHERE observed_at >= now()::date - INTERVAL '1' year;Caveats
The data types and names of the created columns can be surprising for queries with multiple expressions. E.g., CedarDB promotes the precision of numeric types to avoid overflows, or infers that columns are guaranteed not-null from query predicates. For more control over the schema, consider using the regular CREATE TABLE statement.
DROP TABLE
DROP TABLE removes a table and all its data.
DROP TABLE observations;Do not throw an error if the table does not exist:
DROP TABLE IF EXISTS staging_data;Drop multiple tables in one statement:
DROP TABLE staging_data, temp_results;By default, CedarDB refuses to drop a table that other objects depend on, such as a foreign key from another table or a view built on it.
Use CASCADE to drop the table together with all objects that depend on it:
DROP TABLE species CASCADE;Dependent foreign key constraints and views are removed along with the table.
RESTRICT is the default behavior and rejects the drop if any object still depends on the table:
DROP TABLE species RESTRICT;Permissions
To drop a table, you must own it, own its schema, or be a superuser. Members of the owning role count as owners.
ALTER TABLE
ALTER TABLE modifies the definition of an existing table.
Column statements
ADD COLUMN
ALTER TABLE species ADD COLUMN iucn_status text;Add a column only if it does not already exist:
ALTER TABLE species ADD COLUMN IF NOT EXISTS iucn_status text;A new column can have a DEFAULT, NOT NULL, UNIQUE, REFERENCES, or identity constraint.
Existing rows get the default value:
ALTER TABLE species ADD COLUMN assessed boolean NOT NULL DEFAULT false;Adding a column with a CHECK constraint is not yet supported.
DROP COLUMN
ALTER TABLE species DROP COLUMN iucn_status;Drop a column, silently ignoring if it does not exist:
ALTER TABLE species DROP COLUMN IF EXISTS iucn_status;Drop a column and cascade to any dependent objects, like other columns that reference it:
ALTER TABLE observations DROP COLUMN raw_notes CASCADE;RENAME COLUMN
ALTER TABLE species RENAME COLUMN botanical_name TO scientific_name;Renaming a column does not rename any constraints whose default name was derived from the old column name.
For example, a unique constraint with the default name species_botanical_name_key keeps that name after the column is renamed.
ALTER COLUMN TYPE
Change the data type of a column:
CREATE TABLE seedlings (id int, height_cm int, label varchar(20));
ALTER TABLE seedlings ALTER COLUMN height_cm TYPE bigint;
ALTER TABLE seedlings ALTER COLUMN label SET DATA TYPE varchar(50);Without a USING clause, CedarDB converts the existing values automatically.
This works when the old type can be converted to the new type without an explicit cast, e.g., from int to bigint, or from text to varchar(n).
Other conversions, such as from text to int or from numeric to int, fail with column "<name>" cannot be cast automatically to type <type>.
For these conversions, specify how to compute the new value with USING, e.g., USING round(amount) to convert a numeric column to int.
The expression can reference all columns of the row:
CREATE TABLE saplings (id int, height text, planted text);
INSERT INTO saplings VALUES (1, '12', 'yes'), (2, '30', 'no');
ALTER TABLE saplings ALTER COLUMN height TYPE int USING height::int;
ALTER TABLE saplings ALTER COLUMN planted TYPE boolean USING planted = 'yes';
SELECT * FROM saplings; id | height | planted
----+--------+---------
1 | 12 | t
2 | 30 | fIf the expression fails for any row, or a new value violates a constraint of the column, the statement fails and the table is unchanged.
You cannot change the type of a column that a view or materialized view uses, and ALTER COLUMN ... TYPE ... COLLATE is not supported.
Identity columns
Turn an existing NOT NULL column into an identity column, or turn an identity column back into a regular column:
CREATE TABLE seedlings (id int NOT NULL, label text);
ALTER TABLE seedlings ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY;
ALTER TABLE seedlings ALTER COLUMN id DROP IDENTITY;DROP IDENTITY IF EXISTS does not throw an error if the column is not an identity column.
The new identity sequence starts at 1, regardless of the values already in the column.
SET DEFAULT
Set the default value applied to a column when no value is supplied on INSERT:
ALTER TABLE species ALTER COLUMN iucn_status SET DEFAULT 'not evaluated';Remove an existing default:
ALTER TABLE species ALTER COLUMN iucn_status DROP DEFAULT;SET NOT NULL
Add a not-null constraint to an existing column. CedarDB validates the current rows and fails if the column already contains null values:
ALTER TABLE species ALTER COLUMN botanical_name SET NOT NULL;Remove a not-null constraint:
ALTER TABLE species ALTER COLUMN botanical_name DROP NOT NULL;RENAME TABLE
ALTER TABLE species RENAME TO plant_species;While a view or materialized view reads from a table, you cannot rename the table or the columns the view uses. Drop the dependent views first, rename, and recreate them.
Constraint statements
ADD CONSTRAINT
Adds a constraint to an existing table.
CedarDB supports adding named PRIMARY KEY, FOREIGN KEY, and UNIQUE constraints:
ALTER TABLE orders ADD CONSTRAINT orders_pk PRIMARY KEY (id);
ALTER TABLE orders ADD CONSTRAINT orders_customer_fk FOREIGN KEY (customer) REFERENCES customers (id);
ALTER TABLE orders ADD CONSTRAINT orders_unique UNIQUE (customer, item);Foreign key actions like ON DELETE CASCADE are also supported when adding a constraint:
ALTER TABLE observations ADD CONSTRAINT obs_species_fk
FOREIGN KEY (species_id) REFERENCES species (id) ON DELETE CASCADE;The CONSTRAINT <name> part can be omitted, in which case CedarDB automatically assigns a default name:
- Primary key:
tablename_pkey - Unique:
tablename_colname_key - Foreign key:
tablename_colname_fkey
When adding a FOREIGN KEY constraint to an existing table, CedarDB validates all existing rows.
If any row would violate the constraint, the statement fails.
Adding a CHECK constraint to an existing table is not yet supported.
CHECK constraints can only be specified at CREATE TABLE time.
When adding a UNIQUE or PRIMARY KEY constraint, CedarDB validates all existing rows as well.
NOT VALID is accepted, but CedarDB still validates the existing rows.
VALIDATE CONSTRAINT and ALTER CONSTRAINT are not supported.
DROP CONSTRAINT
Removes a constraint by name:
ALTER TABLE orders DROP CONSTRAINT orders_unique;
ALTER TABLE orders DROP CONSTRAINT orders_pkey;Silently ignore if the constraint does not exist:
ALTER TABLE orders DROP CONSTRAINT IF EXISTS orders_unique;Drop a constraint and cascade to dependent constraints:
ALTER TABLE child_table DROP CONSTRAINT fk_constraint CASCADE;RENAME CONSTRAINT
Rename an existing constraint, using either its explicit name or its default name:
ALTER TABLE orders RENAME CONSTRAINT orders_pkey TO orders_primary;Storage Options
Change the general-purpose compression codec used for on-disk column data.
Supported values are zstd (the default) and none.
This takes effect the next time affected data is written to disk:
ALTER TABLE species SET (compression = zstd);
ALTER TABLE species SET (compression = none);SET SCHEMA
Move a table to a different schema:
ALTER TABLE species SET SCHEMA taxonomy;The target schema must already exist, and you need the CREATE privilege on it.
While a view or materialized view reads from the table, SET SCHEMA will fail.
Ownership
ALTER TABLE species OWNER TO botanist_role;Row Level Security
Enable row-level security on a table. Policies must be created separately to actually filter rows:
ALTER TABLE observations ENABLE ROW LEVEL SECURITY;
ALTER TABLE observations DISABLE ROW LEVEL SECURITY;Force row-level security to apply even to the table owner:
ALTER TABLE observations FORCE ROW LEVEL SECURITY;
ALTER TABLE observations NO FORCE ROW LEVEL SECURITY;Row-level security policies are created with CREATE POLICY.
This feature requires an enterprise license.
Permissions
To alter a table, you must own it or be a superuser.
Members of the owning role count as owners.
In addition, you need the USAGE privilege on the schema of the table, and:
SET SCHEMArequires theCREATEprivilege on the destination schema.OWNER TOrequires that you canSET ROLEto the new owner, and that the new owner has theCREATEprivilege on the schema of the table.ADD CONSTRAINT ... FOREIGN KEYrequires theREFERENCESprivilege on the referenced table.
Temporary tables
A temporary table exists only in the session that created it. CedarDB drops it automatically when the session ends. Other sessions cannot see it, so several sessions can each have a temporary table with the same name.
CREATE TEMP TABLE seedling_batch (
id int PRIMARY KEY,
species text NOT NULL,
sown date DEFAULT current_date
);
INSERT INTO seedling_batch (id, species) VALUES (1, 'Birch');TEMP and TEMPORARY are synonyms.
CedarDB accepts LOCAL and GLOBAL for compatibility and ignores them.
Temporary tables support the same column definitions, constraints, indexes, and ALTER TABLE statements as regular tables.
You can also create them with CREATE TEMP TABLE ... AS, CREATE TEMP TABLE ... (LIKE ...), and SELECT ... INTO TEMP.
ON COMMIT
The ON COMMIT clause controls what happens to a temporary table at the end of each transaction:
| Option | Behavior |
|---|---|
ON COMMIT PRESERVE ROWS | Keep the rows. This is the default. |
ON COMMIT DELETE ROWS | Delete all rows at every commit. |
ON COMMIT DROP | Drop the table when the current transaction commits. |
BEGIN;
CREATE TEMP TABLE todays_plantings (id int, species text) ON COMMIT DROP;
INSERT INTO todays_plantings VALUES (1, 'Maple');
SELECT count(*) FROM todays_plantings;
COMMIT;
-- todays_plantings no longer existsOutside of an explicit transaction block, each statement is its own transaction.
Then a table with ON COMMIT DROP is dropped right after CREATE, and ON COMMIT DELETE ROWS deletes rows right after each INSERT.
ON COMMIT is only allowed for temporary tables.
Creating and dropping temporary tables is transactional: a ROLLBACK undoes the CREATE TEMP TABLE or DROP TABLE.
Name resolution
Temporary tables live in a session-specific schema named pg_temp_<n>.
pg_my_temp_schema() returns its OID, and pg_temp always refers to it.
Unless you list pg_temp in search_path explicitly, CedarDB searches it first.
A temporary table therefore hides a permanent table with the same name:
CREATE TABLE forests (id int, name text);
CREATE TEMP TABLE forests (id int, name text);
SELECT * FROM forests; -- the temporary table
SELECT * FROM public.forests; -- the permanent table
SELECT * FROM pg_temp.forests; -- the temporary tableYou cannot create a temporary table in a regular schema (CREATE TEMP TABLE public.x fails), and you cannot move a temporary table to another schema with SET SCHEMA.
CREATE TABLE pg_temp.x (...) creates a temporary table.
DISCARD TEMP and DISCARD ALL drop all temporary tables of the session.
In pg_class, temporary tables have relpersistence = 't'.
information_schema.tables shows them with table_type = 'LOCAL TEMPORARY'.
Permissions
To create a temporary table, you need the TEMPORARY privilege on the current database.
PUBLIC has this privilege by default.
The creator owns the temporary table.
Table privileges
Table privileges control what other roles can do with a table. The owner of a table has all privileges on it.
| Privilege | Allows |
|---|---|
SELECT | Reading rows. |
INSERT | Inserting rows, also with COPY FROM. |
UPDATE | Updating rows. |
DELETE | Deleting rows. |
TRUNCATE | Emptying the table with TRUNCATE. |
REFERENCES | Creating foreign keys that reference the table. |
TRIGGER | Accepted for compatibility. CedarDB does not support triggers. |
ALL [PRIVILEGES] | All of the above. |
A role also needs the USAGE privilege on the schema of the table.
GRANT and REVOKE statements on tables, including ALTER DEFAULT PRIVILEGES, require an enterprise license.CREATE TABLE trees (id int PRIMARY KEY, species text);
CREATE ROLE forester LOGIN;
GRANT SELECT, INSERT ON trees TO forester;
REVOKE INSERT ON trees FROM forester;Grant a privilege on all existing tables, views, and materialized views of a schema:
GRANT SELECT ON ALL TABLES IN SCHEMA public TO forester;This does not affect tables that you create later.
To grant privileges on future tables automatically, use ALTER DEFAULT PRIVILEGES:
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO forester;Privileges on individual columns, such as GRANT SELECT (species) ON trees, are not supported.
PostgreSQL Differences
- Generated columns (
GENERATED ALWAYS AS (...) STORED) are not supported. Identity columns are. UNIQUE NULLS NOT DISTINCTis not supported.- Table partitioning only supports
PARTITION BY HASH, and you do not need to create or attach individual partitions.PARTITION BY RANGEandPARTITION BY LISTare not supported. - Table inheritance (
INHERITS), typed tables (OF type),TABLESPACE, and exclusion constraints are not supported. - Unknown storage parameters in
WITH (...), such asfillfactor, are ignored with a warning onCREATE TABLE, and rejected onALTER TABLE ... SET. ALTER TABLE ... ADD COLUMNfails while any view or materialized view reads the table. Renaming a table or a column that a view uses, and changing the type of such a column, also fail.DEFERRABLEconstraints are not supported. On a table constraint,DEFERRABLEandINITIALLY DEFERREDare accepted but ignored, and CedarDB checks the constraint immediately.- Any
RETURNINGclause requires theSELECTprivilege on the table, even one that does not reference a column, such asRETURNING 1. PostgreSQL only requiresSELECTon the columns thatRETURNINGreferences. - Foreign keys between temporary and permanent tables are not supported. PostgreSQL allows a temporary table to reference a permanent table.
- Temporary tables of other sessions do not appear in
pg_class. - Column privileges,
COMMENT ON TABLE, andCOMMENT ON COLUMNare not supported.