diff --git a/content/compatibility/sql_features.md b/content/compatibility/sql_features.md
index d3d5dd9d..827c11e0 100644
--- a/content/compatibility/sql_features.md
+++ b/content/compatibility/sql_features.md
@@ -17,150 +17,230 @@ the [system table compatibility](../system_table) page.
### Table Creation & Deletion
-| **Feature** | **Support State** | **Details** |
-|--------------------------------|-------------------|----------------------------------------------------------------------------------------------------------------|
-| CREATE TABLE | Yes | [Documentation](/docs/references/objects/tables/) |
-| DROP TABLE | Yes | |
-| Default Values | Yes | |
-| GENERATED AS IDENTITY | Yes | Both `ALWAYS` and `BY DEFAULT` variants |
-| GENERATED ALWAYS AS (computed) | No | |
-| Check Constraints | Yes | Only at CREATE TABLE time. [Documentation](/docs/references/objects/tables/#constraints) |
-| Not-Null Constraints | Yes | [Documentation](/docs/references/objects/tables/) |
-| Unique Constraints | Yes | [Documentation](/docs/references/objects/tables/) |
-| Primary Keys | Yes | [Documentation](/docs/references/objects/tables/) |
-| Foreign Keys | Yes | [Documentation](/docs/references/objects/tables/) |
-| FK ON DELETE CASCADE | Yes | |
-| FK ON DELETE RESTRICT | Yes | |
-| FK ON DELETE NO ACTION | Yes | |
-| FK ON UPDATE CASCADE | Yes | |
-| FK ON DELETE SET NULL | No | |
-| FK ON DELETE SET DEFAULT | No | |
-| Named Constraints | Yes | For PRIMARY KEY, UNIQUE, FOREIGN KEY, and CHECK. [Documentation](/docs/references/objects/tables/#constraints) |
-| Exclusion Constraints | No | |
-| System Columns | Yes | Only meaningful for tableoid and ctid |
-| UNLOGGED TABLE | No | |
-| CREATE TABLE LIKE | Partial | Copies column names and types. `INCLUDING` options not supported |
-| DROP TABLE CASCADE | No | |
+| **Feature** | **Support State** | **Details** |
+|-------------------------------------|-------------------|----------------------------------------------------------------------------------------------------------------|
+| CREATE TABLE | Yes | [Documentation](/docs/references/objects/tables/) |
+| DROP TABLE | Yes | [Documentation](/docs/references/objects/tables/#drop-table) |
+| CREATE TABLE AS / SELECT INTO | Yes | [Documentation](/docs/references/objects/tables/#create-table-as) |
+| TEMPORARY TABLE | Yes | [Documentation](/docs/references/objects/tables/#temporary-tables) |
+| ON COMMIT (temporary tables) | Yes | [Documentation](/docs/references/objects/tables/#on-commit) |
+| Default Values | Yes | |
+| GENERATED AS IDENTITY | Yes | Both `ALWAYS` and `BY DEFAULT` variants |
+| GENERATED ALWAYS AS (computed) | No | |
+| Check Constraints | Yes | [Documentation](/docs/references/objects/tables/#constraints) |
+| Not-Null Constraints | Yes | [Documentation](/docs/references/objects/tables/) |
+| Unique Constraints | Yes | [Documentation](/docs/references/objects/tables/) |
+| Primary Keys | Yes | [Documentation](/docs/references/objects/tables/) |
+| Foreign Keys | Yes | [Documentation](/docs/references/objects/tables/) |
+| FK ON DELETE CASCADE | Yes | |
+| FK ON DELETE RESTRICT | Yes | |
+| FK ON DELETE NO ACTION | Yes | |
+| FK ON UPDATE CASCADE | Yes | |
+| FK ON DELETE SET NULL | Yes | [Documentation](/docs/references/objects/tables/#foreign-key-actions) |
+| FK ON DELETE SET DEFAULT | Yes | [Documentation](/docs/references/objects/tables/#foreign-key-actions) |
+| FK ON UPDATE SET NULL / SET DEFAULT | Yes | [Documentation](/docs/references/objects/tables/#foreign-key-actions) |
+| DEFERRABLE Constraints | No | |
+| Named Constraints | Yes | For PRIMARY KEY, UNIQUE, FOREIGN KEY, and CHECK. [Documentation](/docs/references/objects/tables/#constraints) |
+| Exclusion Constraints | No | |
+| System Columns | Yes | Only meaningful for tableoid and ctid |
+| UNLOGGED TABLE | Partial | Accepted for compatibility, behaves like a regular table |
+| CREATE TABLE LIKE | Partial | [Documentation](/docs/references/objects/tables/#options) |
+| DROP TABLE CASCADE | Yes | [Documentation](/docs/references/objects/tables/#drop-table) |
### Table Modification (ALTER TABLE)
-| **Feature** | **Support State** | **Details** |
-|-----------------------------------|-------------------|---------------------------------------------------|
-| ADD COLUMN | Yes | |
-| ADD COLUMN IF NOT EXISTS | Yes | |
-| DROP COLUMN | Yes | |
-| DROP COLUMN IF EXISTS | Yes | |
-| DROP COLUMN CASCADE | Yes | |
-| RENAME COLUMN | Yes | |
-| RENAME TO | Yes | |
-| ADD CHECK | No | Only at CREATE TABLE time |
-| ADD CONSTRAINT (PRIMARY KEY) | Yes | [Documentation](/docs/references/objects/tables/) |
-| ADD CONSTRAINT (UNIQUE) | Yes | [Documentation](/docs/references/objects/tables/) |
-| ADD CONSTRAINT (FOREIGN KEY) | Yes | [Documentation](/docs/references/objects/tables/) |
-| DROP CONSTRAINT | Yes | [Documentation](/docs/references/objects/tables/) |
-| DROP CONSTRAINT IF EXISTS | Yes | |
-| DROP CONSTRAINT CASCADE | Yes | |
-| RENAME CONSTRAINT | No | |
-| ALTER COLUMN SET/DROP DEFAULT | No | |
-| ALTER COLUMN SET/DROP NOT NULL | No | |
-| ALTER COLUMN TYPE | No | |
-| SET SCHEMA | No | |
-| OWNER TO | Yes | |
-| ENABLE/DISABLE ROW LEVEL SECURITY | Yes | CREATE POLICY requires an enterprise license |
-| FORCE/NO FORCE ROW LEVEL SECURITY | Yes | |
-| SET TABLESPACE | No | |
-| CLUSTER ON / SET WITHOUT CLUSTER | No | |
-| ATTACH / DETACH PARTITION | No | |
-| ENABLE/DISABLE TRIGGER | No | Triggers are not implemented |
-| VALIDATE CONSTRAINT | No | |
+| **Feature** | **Support State** | **Details** |
+|-----------------------------------|-------------------|-----------------------------------------------------------------------------------------|
+| ADD COLUMN | Yes | |
+| ADD COLUMN IF NOT EXISTS | Yes | |
+| DROP COLUMN | Yes | |
+| DROP COLUMN IF EXISTS | Yes | |
+| DROP COLUMN CASCADE | Yes | |
+| RENAME COLUMN | Yes | |
+| RENAME TO | Yes | |
+| ADD CHECK | No | |
+| ADD CONSTRAINT (PRIMARY KEY) | Yes | [Documentation](/docs/references/objects/tables/) |
+| ADD CONSTRAINT (UNIQUE) | Yes | [Documentation](/docs/references/objects/tables/) |
+| ADD CONSTRAINT (FOREIGN KEY) | Yes | [Documentation](/docs/references/objects/tables/) |
+| DROP CONSTRAINT | Yes | [Documentation](/docs/references/objects/tables/) |
+| DROP CONSTRAINT IF EXISTS | Yes | |
+| DROP CONSTRAINT CASCADE | Yes | |
+| RENAME CONSTRAINT | Yes | [Documentation](/docs/references/objects/tables/#rename-constraint) |
+| ALTER COLUMN SET/DROP DEFAULT | Yes | [Documentation](/docs/references/objects/tables/#set-default) |
+| ALTER COLUMN SET/DROP NOT NULL | Yes | [Documentation](/docs/references/objects/tables/#set-not-null) |
+| ALTER COLUMN TYPE | Yes | [Documentation](/docs/references/objects/tables/#alter-column-type) |
+| ALTER COLUMN ADD/DROP IDENTITY | Yes | [Documentation](/docs/references/objects/tables/#identity-columns) |
+| SET SCHEMA | Yes | [Documentation](/docs/references/objects/tables/#set-schema) |
+| OWNER TO | Yes | [Documentation](/docs/references/objects/tables/#ownership) |
+| ENABLE/DISABLE ROW LEVEL SECURITY | Yes | [Documentation](/docs/references/objects/policies/#enable-and-force-row-level-security) |
+| FORCE/NO FORCE ROW LEVEL SECURITY | Yes | [Documentation](/docs/references/objects/policies/#enable-and-force-row-level-security) |
+| SET TABLESPACE | No | |
+| CLUSTER ON / SET WITHOUT CLUSTER | No | |
+| ATTACH / DETACH PARTITION | No | |
+| ENABLE/DISABLE TRIGGER | No | Triggers are not implemented |
+| VALIDATE CONSTRAINT | No | |
### Privileges
-| **Feature** | **Support State** | **Details** |
-|-----------------------|-------------------|--------------------------------------------------|
-| CREATE ROLE | Yes | [Documentation](/docs/references/objects/roles) |
-| OWNER TO | Yes | |
-| ALTER ROLE | Yes | [Documentation](/docs/references/objects/roles) |
-| GRANT | Yes | Requires an enterprise license |
-| REVOKE | Yes | Requires an enterprise license |
-| SET ROLE | Yes | Requires an enterprise license |
-| INHERIT | Yes | [Documentation](/docs/references/objects/roles/) |
-| Row Security Policies | Yes | Requires an enterprise license |
+| **Feature** | **Support State** | **Details** |
+|---------------------------|-------------------|-----------------------------------------------------------------------------|
+| CREATE ROLE | Yes | [Documentation](/docs/references/objects/roles) |
+| OWNER TO | Yes | |
+| ALTER ROLE | Yes | [Documentation](/docs/references/objects/roles) |
+| DROP ROLE | Yes | [Documentation](/docs/references/objects/roles/#drop-role) |
+| GRANT | Enterprise | [Documentation](/docs/references/objects/roles/#object-privileges) |
+| WITH GRANT OPTION | Enterprise | [Documentation](/docs/references/objects/roles/#grant-options-and-grantors) |
+| ALTER DEFAULT PRIVILEGES | Enterprise | [Documentation](/docs/references/objects/roles/#alter-default-privileges) |
+| REVOKE | Enterprise | [Documentation](/docs/references/objects/roles/#revoking-privileges) |
+| SET ROLE | Yes | [Documentation](/docs/references/objects/roles/#set-role) |
+| SET SESSION AUTHORIZATION | Yes | [Documentation](/docs/references/objects/roles/#set-role) |
+| INHERIT | Yes | [Documentation](/docs/references/objects/roles/) |
+| Column Privileges | No | |
+| REASSIGN OWNED | No | |
+| DROP OWNED | No | |
+| Row Security Policies | Enterprise | [Documentation](/docs/references/objects/policies/) |
+| BYPASSRLS | Yes | [Documentation](/docs/references/objects/policies/#bypassrls) |
### Indexes
-| **Feature** | **Support State** | **Details** |
-|------------------------|-------------------|------------------------------------------------------------------------|
-| CREATE INDEX | Yes | Only B-Tree Indexes [Documentation](/docs/references/objects/indexes/) |
-| GIN | No | |
-| BRIN | No | |
-| Multicolumn Indexes | Yes | [Documentation](/docs/references/objects/indexes/) |
-| Ordered Indexes | Yes | [Documentation](/docs/references/objects/indexes/#column-order) |
-| Unique Indexes | Yes | |
-| Indexes on Expressions | No | |
-| Partial Indexes | No | |
+| **Feature** | **Support State** | **Details** |
+|---------------------------|-------------------|-------------------------------------------------------------------|
+| CREATE INDEX | Yes | [Documentation](/docs/references/objects/indexes/) |
+| DROP INDEX | Yes | [Documentation](/docs/references/objects/indexes/#drop-index) |
+| ALTER INDEX | No | |
+| REINDEX | No | |
+| B-Tree | Yes | [Documentation](/docs/references/objects/indexes/#b-tree-lookups) |
+| Hash | Partial | Accepted for compatibility, converted to a B-Tree index |
+| GIN | No | |
+| GiST | No | |
+| SP-GiST | No | |
+| BRIN | No | |
+| Multicolumn Indexes | Yes | [Documentation](/docs/references/objects/indexes/) |
+| Ordered Indexes | Yes | [Documentation](/docs/references/objects/indexes/#column-order) |
+| Unique Indexes | Yes | [Documentation](/docs/references/objects/indexes/#unique-indexes) |
+| Indexes on Expressions | No | |
+| Partial Indexes | No | |
+| INCLUDE Columns | No | |
+| CREATE INDEX CONCURRENTLY | Partial | [Documentation](/docs/references/objects/indexes/#create-index) |
### Misc
-| **Feature** | **Support State** | **Details** |
-|------------------------|-------------------|------------------------------------------------------------------------------------------------------------------------------|
-| CREATE SCHEMA | Yes | [Documentation](/docs/references/objects/schemas/) |
-| DROP SCHEMA | Yes | Only if the schema is empty |
-| search_path | Yes | [Documentation](/docs/references/objects/schemas/#using-schemas) |
-| Table Inheritance | No | |
-| Table Partitioning | Partial | Only hash partitioning, partitions are created automatically. [Documentation](/docs/references/objects/tables/#partitioning) |
-| Foreign Data Wrappers | No | |
-| Views | Yes | [Documentation](/docs/references/objects/views/) |
-| Materialized Views | Yes | [Documentation](/docs/references/objects/materialized_views/) |
-| Databases | Yes | [Documentation](/docs/references/objects/databases/) |
-| Functions & Procedures | Yes | [Documentation](/docs/references/objects/functions/) Also in cedar_script language |
-| Custom Types | No | |
-| Triggers | No | |
-| Prepared Statements | Yes | |
-| Advisory Locks | Yes | [Documentation](/docs/references/functions/system/#advisory-locks) |
-| ALTER SYSTEM | Yes | [Documentation](/docs/references/sessions/altersystem/) |
+| **Feature** | **Support State** | **Details** |
+|---------------------------------|-------------------|------------------------------------------------------------------------------------------------------------------------------|
+| CREATE SCHEMA | Yes | [Documentation](/docs/references/objects/schemas/) |
+| ALTER SCHEMA | Yes | [Documentation](/docs/references/objects/schemas/#alter-schema) |
+| DROP SCHEMA | Yes | [Documentation](/docs/references/objects/schemas/#drop-schema) |
+| search_path | Yes | [Documentation](/docs/references/objects/schemas/#using-schemas) |
+| Table Inheritance | No | |
+| Table Partitioning | Partial | Only hash partitioning, partitions are created automatically. [Documentation](/docs/references/objects/tables/#partitioning) |
+| Foreign Data Wrappers | No | |
+| Views | Yes | [Documentation](/docs/references/objects/views/) |
+| Temporary Views | Yes | [Documentation](/docs/references/objects/views/#temporary-views) |
+| Updatable Views | No | |
+| WITH CHECK OPTION | No | |
+| Recursive Views | No | |
+| Materialized Views | Yes | [Documentation](/docs/references/objects/materialized_views/) |
+| Databases | Yes | [Documentation](/docs/references/objects/databases/) |
+| Functions & Procedures | Yes | [Documentation](/docs/references/objects/functions/) Also in cedarscript language |
+| Table Functions (RETURNS TABLE) | Yes | [Documentation](/docs/references/objects/functions/#table-functions) |
+| RETURNS SETOF | No | |
+| VARIADIC / OUT Parameters | No | |
+| SECURITY DEFINER | Yes | [Documentation](/docs/references/objects/functions/#security-definer) |
+| Temporary Functions | Yes | [Documentation](/docs/references/objects/functions/) |
+| PL/pgSQL | No | |
+| DO | No | |
+| Sequences | Yes | [Documentation](/docs/references/objects/sequences/) |
+| Temporary Sequences | Yes | [Documentation](/docs/references/objects/sequences/#temporary-sequences) |
+| Custom Types | Partial | [Documentation](/docs/references/objects/types/) |
+| Enum Types | Yes | [Documentation](/docs/references/objects/types/) |
+| Composite Types | No | |
+| Range Types (user-defined) | No | |
+| Domains | No | |
+| Triggers | No | |
+| Prepared Statements | Yes | [Documentation](/docs/references/advanced/prepare/) |
+| Cursors (DECLARE / FETCH) | No | |
+| LISTEN / NOTIFY | No | |
+| SET / SHOW / RESET | Yes | [Documentation](/docs/references/sessions/settings/) |
+| SET LOCAL | No | |
+| statement_timeout | Yes | [Documentation](/docs/references/sessions/settings/#timeouts) |
+| DISCARD | Yes | [Documentation](/docs/references/sessions/discard/) |
+| EXPLAIN | Yes | [Documentation](/docs/references/utility/explain/) |
+| ANALYZE | Yes | [Documentation](/docs/references/utility/analyze/) |
+| VACUUM | Yes | |
+| CHECKPOINT | No | |
+| COMMENT ON | No | |
+| Advisory Locks | Yes | [Documentation](/docs/references/functions/system/#advisory-locks) |
+| ALTER SYSTEM | Yes | [Documentation](/docs/references/sessions/altersystem/) |
## Data Manipulation
-| **Feature** | **Support State** | **Details** |
-|-------------|-------------------|---------------------------------------------------------|
-| INSERT | Yes | [Documentation](/docs/references/dml/insert/) |
-| UPDATE | Yes | [Documentation](/docs/references/dml/update/) |
-| DELETE | Yes | [Documentation](/docs/references/dml/delete/) |
-| TRUNCATE | Yes | [Documentation](/docs/references/dml/truncate/) |
-| RETURNING | Yes | [Documentation](/docs/references/dml/insert/#returning) |
-| COPY FROM | Yes | [Documentation](/docs/references/dml/copy/) |
-| COPY TO | Yes | [Documentation](/docs/references/dml/copy/) |
-| ON CONFLICT | Yes | [Documentation](/docs/references/dml/upsert/) |
+| **Feature** | **Support State** | **Details** |
+|---------------------------|-------------------|---------------------------------------------------------|
+| INSERT | Yes | [Documentation](/docs/references/dml/insert/) |
+| UPDATE | Yes | [Documentation](/docs/references/dml/update/) |
+| DELETE | Yes | [Documentation](/docs/references/dml/delete/) |
+| TRUNCATE | Yes | [Documentation](/docs/references/dml/truncate/) |
+| RETURNING | Yes | [Documentation](/docs/references/dml/insert/#returning) |
+| COPY FROM | Yes | [Documentation](/docs/references/dml/copy/) |
+| COPY TO | Yes | [Documentation](/docs/references/dml/copy/) |
+| ON CONFLICT | Yes | [Documentation](/docs/references/dml/upsert/) |
+| MERGE | No | |
+| WITH (data-modifying CTE) | Partial | [Documentation](/docs/references/queries/with/) |
+
+## Transactions
+
+| **Feature** | **Support State** | **Details** |
+|-----------------------------------|-------------------|------------------------------------------------------------------------|
+| BEGIN / COMMIT / ROLLBACK | Yes | [Documentation](/docs/references/transactions/) |
+| SAVEPOINT / RELEASE / ROLLBACK TO | Yes | [Documentation](/docs/references/transactions/#savepoints) |
+| READ ONLY Transactions | Yes | [Documentation](/docs/references/transactions/#begin) |
+| SET TRANSACTION | Yes | [Documentation](/docs/references/transactions/#set-transaction) |
+| REPEATABLE READ | Yes | [Documentation](/docs/references/transactions/#transaction-semantics) |
+| READ COMMITTED | Partial | [Documentation](/docs/references/transactions/#postgresql-differences) |
+| SERIALIZABLE | No | |
+| LOCK TABLE | Partial | [Documentation](/docs/references/transactions/#postgresql-differences) |
+| SET CONSTRAINTS | No | |
+| COMMIT / ROLLBACK AND CHAIN | No | |
+| PREPARE TRANSACTION | No | |
## Queries
-| **Feature** | **Support State** | **Details** |
-|---------------------------|-------------------|----------------------------------------------------|
-| Table & View References | Yes | |
-| Inner Joins | Yes | [Documentation](/docs/references/queries/from/) |
-| Outer Joins | Yes | [Documentation](/docs/references/queries/from/) |
-| Semijoins | Yes | [Documentation](/docs/references/queries/from/) |
-| Antijoins | Yes | |
-| Table Functions | Yes | |
-| Lateral Subqueries | Yes | |
-| User-Specified Aliases | Yes | |
-| GROUP BY | Yes | [Documentation](/docs/references/queries/groupby/) |
-| HAVING | Yes | [Documentation](/docs/references/queries/groupby/) |
-| GROUPING SETS | Yes | |
-| CUBE | Yes | |
-| ROLLUP | Yes | |
-| WINDOW Functions | Yes | [Documentation](/docs/references/queries/window/) |
-| WITH | Yes | [Documentation](/docs/references/queries/with/) |
-| WITH RECURSIVE | Yes | |
-| UNION | Yes | |
-| UNION ALL | Yes | |
-| INTERSECT | Yes | |
-| EXCEPT | Yes | |
-| ORDER BY | Yes | |
-| LIMIT | Yes | |
-| OFFSET | Yes | |
-| Table Generating Function | Yes | |
+| **Feature** | **Support State** | **Details** |
+|-----------------------------------|-------------------|----------------------------------------------------------------------------------|
+| Table & View References | Yes | |
+| Inner Joins | Yes | [Documentation](/docs/references/queries/from/) |
+| Outer Joins | Yes | [Documentation](/docs/references/queries/from/) |
+| Semijoins | Yes | [Documentation](/docs/references/queries/from/) |
+| Antijoins | Yes | |
+| Table Functions | Yes | |
+| Lateral Subqueries | Yes | [Documentation](/docs/references/queries/from/#lateral) |
+| User-Specified Aliases | Yes | |
+| GROUP BY | Yes | [Documentation](/docs/references/queries/groupby/) |
+| HAVING | Yes | [Documentation](/docs/references/queries/groupby/) |
+| GROUPING SETS | Yes | [Documentation](/docs/references/queries/groupby/#grouping-sets-rollup-and-cube) |
+| CUBE | Yes | [Documentation](/docs/references/queries/groupby/#grouping-sets-rollup-and-cube) |
+| ROLLUP | Yes | [Documentation](/docs/references/queries/groupby/#grouping-sets-rollup-and-cube) |
+| WINDOW Functions | Yes | [Documentation](/docs/references/queries/window/) |
+| WITH | Yes | [Documentation](/docs/references/queries/with/) |
+| WITH RECURSIVE | Yes | [Documentation](/docs/references/queries/with/#recursive-ctes) |
+| UNION | Yes | [Documentation](/docs/references/queries/setops/) |
+| UNION ALL | Yes | [Documentation](/docs/references/queries/setops/) |
+| INTERSECT | Yes | [Documentation](/docs/references/queries/setops/) |
+| EXCEPT | Yes | [Documentation](/docs/references/queries/setops/) |
+| ORDER BY | Yes | [Documentation](/docs/references/queries/orderby/) |
+| LIMIT | Yes | [Documentation](/docs/references/queries/orderby/) |
+| OFFSET | Yes | [Documentation](/docs/references/queries/orderby/) |
+| Table Generating Function | Yes | |
+| DISTINCT ON | Yes | [Documentation](/docs/references/queries/select/#distinct) |
+| FETCH FIRST | Yes | [Documentation](/docs/references/queries/orderby/) |
+| FETCH FIRST ... WITH TIES | No | |
+| Row Locking (FOR UPDATE/SHARE) | Partial | [Documentation](/docs/references/transactions/#row-locks) |
+| WITH ORDINALITY | Yes | [Documentation](/docs/references/queries/from/#table-functions) |
+| ROWS FROM | No | |
+| Window Frames (ROWS/RANGE/GROUPS) | Yes | [Documentation](/docs/references/queries/window/#window-frames) |
+| Aggregate FILTER | Yes | [Documentation](/docs/references/queries/groupby/#filter) |
+| INTERSECT ALL / EXCEPT ALL | Yes | [Documentation](/docs/references/queries/setops/) |
+| CTE SEARCH / CYCLE | No | |
## Data Types
@@ -181,8 +261,11 @@ the [system table compatibility](../system_table) page.
| cidr | No | |
| circle | No | |
| date | Yes | [Date Documentation](/docs/references/datatypes/date) |
+| daterange | Yes | [Range Documentation](/docs/references/datatypes/range) |
| double precision | Yes | [Double Documentation](/docs/references/datatypes/float) |
| inet | Partial | [Inet Documentation](/docs/references/datatypes/inet) |
+| int4range | Yes | [Range Documentation](/docs/references/datatypes/range) |
+| int8range | Yes | [Range Documentation](/docs/references/datatypes/range) |
| integer | Yes | [Integer Documentation](/docs/references/datatypes/integer) |
| interval [ fields ] [ (p) ] | Yes | [Interval Documentation](/docs/references/datatypes/interval) |
| json | Yes | [JSON Documentation](/docs/references/datatypes/json) |
@@ -193,6 +276,7 @@ the [system table compatibility](../system_table) page.
| macaddr8 | No | |
| money | No | |
| numeric [ (p, s) ] | Yes | [Numeric Documentation](/docs/references/datatypes/numeric) |
+| numrange | Yes | [Range Documentation](/docs/references/datatypes/range) |
| path | No | |
| pg_lsn | No | |
| pg_snapshot | No | |
@@ -204,10 +288,12 @@ the [system table compatibility](../system_table) page.
| serial | Yes | |
| text | Yes | [Text Documentation](/docs/references/datatypes/text) |
| time [ (p) ] [ without time zone ] | Yes | [Time Documentation](/docs/references/datatypes/time) |
-| time [ (p) ] with time zone | Yes | [Time Documentation](/docs/references/datatypes/time) |
+| time [ (p) ] with time zone | No | |
| timestamp [ (p) ] [ without time zone ] | Yes | [Timestamp Documentation](/docs/references/datatypes/timestamp) |
| timestamp [ (p) ] with time zone | Yes | [Timestamp Documentation](/docs/references/datatypes/timestamp) |
| tsquery | No | |
+| tsrange | Yes | [Range Documentation](/docs/references/datatypes/range) |
+| tstzrange | Yes | [Range Documentation](/docs/references/datatypes/range) |
| tsvector | No | |
| txid_snapshot | No | |
| uuid | Yes | [UUID Documentation](/docs/references/datatypes/uuid) |
@@ -263,7 +349,7 @@ the [system table compatibility](../system_table) page.
| & | Yes | |
| \| | Yes | |
| # | Yes | |
-| ~ | Yes | |
+| ~ | No | |
| \<\< | Yes | |
| \>\> | Yes | |
| abs | Yes | |
@@ -274,13 +360,13 @@ the [system table compatibility](../system_table) page.
| erf | No | |
| erfc | No | |
| exp | Yes | |
-| factorial | Yes | Also exists as ! operand |
+| factorial | Yes | Also available as the `!!` prefix operator |
| floor | Yes | |
| gcd | Yes | Missing support for numeric input type. Supports int4, uint8, int8. |
| lcm | No | |
| ln | Yes | |
| log | Yes | |
-| log10 | Yes | |
+| log10 | No | |
| min_scale | No | |
| mod | Yes | |
| pi | Yes | |
@@ -330,7 +416,7 @@ the [system table compatibility](../system_table) page.
| lower | Yes | |
| lpad | Yes | |
| ltrim | Yes | |
-| normalize | No | |
+| normalize | Yes | |
| octet_length | Yes | |
| overlay | Yes | |
| position | Yes | |
@@ -383,7 +469,7 @@ the [system table compatibility](../system_table) page.
| substring | Yes | |
| trim | Yes | Only for BOTH direction |
| bit_count | No | |
-| get_bit | No | |
+| get_bit | Yes | |
| get_byte | Yes | |
| length | Yes | |
| md5 | Yes | |
@@ -403,16 +489,16 @@ the [system table compatibility](../system_table) page.
| **Feature** | **Support State** | **Details** |
|--------------|-------------------|------------:|
-| \|\| | No | |
-| & | No | |
-| \| | No | |
-| # | No | |
+| \|\| | Yes | |
+| & | Yes | |
+| \| | Yes | |
+| # | Yes | |
| ~ | No | |
| \<\< | No | |
| \>\> | No | |
| bit_count | Yes | |
| bit_length | Yes | |
-| length | No | |
+| length | Yes | |
| octet_length | Yes | |
| overlay | Yes | |
| position | Yes | |
@@ -441,9 +527,9 @@ the [system table compatibility](../system_table) page.
| **Feature** | **Support State** | **Details** |
|--------------|-------------------|--------------------|
| to_char | Yes | Most used patterns |
-| to_date | No | |
+| to_date | Yes | |
| to_number | No | |
-| to_timestamp | No | |
+| to_timestamp | Yes | |
#### Date/Time
@@ -459,7 +545,7 @@ the [system table compatibility](../system_table) page.
| current_time | Yes | |
| current_timestamp | Yes | |
| date_add | No | possible with + |
-| date_bin | No | |
+| date_bin | Yes | |
| date_part | Yes | |
| date_subtract | No | possible with - |
| date_trunc | Yes | Without timezone |
@@ -471,10 +557,10 @@ the [system table compatibility](../system_table) page.
| localtime | Yes | |
| localtimestamp | Yes | |
| make_date | No | |
-| make_interval | No | |
+| make_interval | Yes | |
| make_time | No | |
| make_timestamp | No | |
-| make_timestamptz | Yes | |
+| make_timestamptz | No | |
| now | Yes | |
| statement_timestamp | No | |
| timeofday | No | |
@@ -561,15 +647,15 @@ the [system table compatibility](../system_table) page.
| + | No | |
| - | No | |
| abbrev | No | |
-| broadcast | No | |
+| broadcast | Yes | |
| family | No | |
-| host | No | |
+| host | Yes | |
| hostmask | No | |
| inet_merge | No | |
| inet_same_family | No | |
| masklen | No | |
-| netmask | No | |
-| network | No | |
+| netmask | Yes | |
+| network | Yes | |
| set_masklen | No | |
| text | No | |
@@ -616,9 +702,11 @@ the [system table compatibility](../system_table) page.
| **Feature** | **Support State** | **Details** |
|------------------------|-------------------|-------------|
-| get_random_uuid | Yes | |
-| uuid_extract_timestamp | No | |
-| uuid_extract_version | No | |
+| gen_random_uuid | Yes | |
+| uuid_extract_timestamp | Yes | |
+| uuid_extract_version | Yes | |
+| uuidv4 | Yes | |
+| uuidv7 | Yes | |
#### XML
@@ -642,70 +730,70 @@ the [system table compatibility](../system_table) page.
#### JSON
-| **Feature** | **Support State** | **Details** |
-|---------------------------|-------------------|-------------|
-| -> | Yes | |
-| -\>\> | Yes | |
-| #> | No | |
-| #\>\> | No | |
-| @> | Yes | |
-| <@ | Yes | |
-| ? | Yes | |
-| ?\| | Yes | |
-| ?& | Yes | |
-| \|\| | Yes | |
-| - | No | |
-| #- | No | |
-| @? | No | |
-| @@ | No | |
-| to_json | Yes | |
-| to_jsonb | No | |
-| array_to_json | No | |
-| json_array | No | |
-| row_to_json | Yes | |
-| json_build_array | Yes | |
-| jsonb_build_array | Yes | |
-| json_build_object | Yes | |
-| jsonb_build_object | No | |
-| json_object | No | |
-| jsonb_object | No | |
-| IS JSON | No | |
-| json_array_elements | Yes | |
-| jsonb_array_elements | Yes | |
-| json_array_elements_text | No | |
-| jsonb_array_elements_text | No | |
-| json_array_length | Yes | |
-| jsonb_array_length | No | |
-| json_each | No | |
-| jsonb_each | No | |
-| json_each_text | No | |
-| jsonb_each_text | No | |
-| json_object_keys | No | |
-| jsonb_object_keys | No | |
-| json_populate_record | No | |
-| jsonb_populate_record | No | |
-| json_populate_recordset | No | |
-| jsonb_populate_recordset | No | |
-| json_to_record | No | |
-| jsonb_to_record | No | |
-| json_to_recordset | No | |
-| jsonb_to_recordset | No | |
-| jsonb_set | No | |
-| jsonb_set_lax | No | |
-| jsonb_insert | No | |
-| json_strip_nulls | No | |
-| jsonb_strip_nulls | No | |
-| json_path | No | |
+| **Feature** | **Support State** | **Details** |
+|---------------------------|-------------------|----------------------------------------------------------------|
+| -> | Yes | |
+| -\>\> | Yes | |
+| #> | Yes | |
+| #\>\> | Yes | |
+| @> | Yes | |
+| <@ | Yes | |
+| ? | Yes | |
+| ?\| | Yes | |
+| ?& | Yes | |
+| \|\| | Yes | |
+| - | No | |
+| #- | No | |
+| @? | No | |
+| @@ | No | |
+| to_json | Yes | |
+| to_jsonb | No | |
+| array_to_json | Yes | |
+| json_array | No | |
+| row_to_json | Partial | [Documentation](/docs/references/functions/json/#construction) |
+| json_build_array | Yes | |
+| jsonb_build_array | Yes | |
+| json_build_object | Yes | |
+| jsonb_build_object | Yes | |
+| json_object | No | |
+| jsonb_object | No | |
+| IS JSON | No | |
+| json_array_elements | Yes | |
+| jsonb_array_elements | Yes | |
+| json_array_elements_text | No | |
+| jsonb_array_elements_text | No | |
+| json_array_length | Yes | |
+| jsonb_array_length | Yes | |
+| json_each | No | |
+| jsonb_each | No | |
+| json_each_text | No | |
+| jsonb_each_text | No | |
+| json_object_keys | No | |
+| jsonb_object_keys | No | |
+| json_populate_record | No | |
+| jsonb_populate_record | No | |
+| json_populate_recordset | No | |
+| jsonb_populate_recordset | No | |
+| json_to_record | No | |
+| jsonb_to_record | No | |
+| json_to_recordset | No | |
+| jsonb_to_recordset | No | |
+| jsonb_set | No | |
+| jsonb_set_lax | No | |
+| jsonb_insert | No | |
+| json_strip_nulls | No | |
+| jsonb_strip_nulls | No | |
+| json_path | No | |
#### Sequence Manipulation
-| **Feature** | **Support State** | **Details** |
-|------------------------|-------------------|-------------|
-| nextval | Yes | |
-| setval | Yes | |
-| currval | No | |
-| lastval | No | |
-| pg_get_serial_sequence | Yes | |
+| **Feature** | **Support State** | **Details** |
+|------------------------|-------------------|-------------------------------------------------------------------------|
+| nextval | Yes | [Documentation](/docs/references/objects/sequences/#sequence-functions) |
+| setval | Yes | [Documentation](/docs/references/objects/sequences/#sequence-functions) |
+| currval | No | |
+| lastval | No | |
+| pg_get_serial_sequence | Yes | |
#### Conditional
@@ -719,30 +807,30 @@ the [system table compatibility](../system_table) page.
#### Array
-| **Feature** | **Support State** | **Details** |
-|-------------------|-------------------|--------------------------|
-| @> | Yes | |
-| <@ | Yes | |
-| && | No | |
-| \|\| | Yes | Not for multidimensional |
-| array_append | Yes | |
-| array_cat | Yes | |
-| array_dims | Yes | |
-| array_fill | Yes | |
-| array_lower | Yes | |
-| array_ndims | Yes | |
-| array_position | Yes | |
-| array_positions | Yes | |
-| array_prepend | Yes | |
-| array_remove | Yes | |
-| array_replace | Yes | |
-| array_sample | No | |
-| array_shuffle | No | |
-| array_to_string | Yes | |
-| array_upper | Yes | |
-| array_cardinality | Yes | |
-| trim_array | Yes | |
-| unnest | Yes | No multi-array expansion |
+| **Feature** | **Support State** | **Details** |
+|-----------------|-------------------|-------------|
+| @> | Yes | |
+| <@ | Yes | |
+| && | Yes | |
+| \|\| | Yes | |
+| array_append | Yes | |
+| array_cat | Yes | |
+| array_dims | Yes | |
+| array_fill | Yes | |
+| array_lower | Yes | |
+| array_ndims | Yes | |
+| array_position | Yes | |
+| array_positions | Yes | |
+| array_prepend | Yes | |
+| array_remove | Yes | |
+| array_replace | Yes | |
+| array_sample | No | |
+| array_shuffle | No | |
+| array_to_string | Yes | |
+| array_upper | Yes | |
+| cardinality | Yes | |
+| trim_array | Yes | |
+| unnest | Yes | |
#### Range
@@ -753,8 +841,8 @@ the [system table compatibility](../system_table) page.
| && | Yes | |
| \<\< | Yes | |
| \>\> | Yes | |
-| &< | Yes | |
-| &> | Yes | |
+| &< | No | |
+| &> | No | |
| -\|- | No | |
| + | No | |
| * | No | |
@@ -786,10 +874,10 @@ the [system table compatibility](../system_table) page.
| bool_or | Yes | [Aggregate Function Documentation](/docs/references/functions/aggregation/#general-purpose-functions) |
| count(*) | Yes | [Aggregate Function Documentation](/docs/references/functions/aggregation/#general-purpose-functions) |
| count("any") | Yes | [Aggregate Function Documentation](/docs/references/functions/aggregation/#general-purpose-functions) |
-| json(b)_agg | No | |
+| json(b)_agg | Partial | [JSON Function Documentation](/docs/references/functions/json/#construction) |
| json(b)_objectagg | No | |
| json(b)_object_agg | No | |
-| json_arrayagg | No | |
+| json_arrayagg | Yes | [JSON Function Documentation](/docs/references/functions/json/#construction) |
| max | Yes | [Aggregate Function Documentation](/docs/references/functions/aggregation/#general-purpose-functions) |
| min | Yes | [Aggregate Function Documentation](/docs/references/functions/aggregation/#general-purpose-functions) |
| range(_intersect)_agg | No | |
@@ -811,7 +899,7 @@ the [system table compatibility](../system_table) page.
| regr_r2 | Yes | [Aggregate Function Documentation](/docs/references/functions/aggregation/#statistical-aggregates) |
| regr_slope | Yes | [Aggregate Function Documentation](/docs/references/functions/aggregation/#statistical-aggregates) |
| regr_sxx | Yes | [Aggregate Function Documentation](/docs/references/functions/aggregation/#statistical-aggregates) |
-| regr_sxy | Yes | [Aggregate Function Documentation](/docs/references/functions/aggregation/#statistical-aggregates) |
+| regr_sxy | No | |
| regr_syy | No | |
| stddev | Yes | [Aggregate Function Documentation](/docs/references/functions/aggregation/#statistical-aggregates) |
| stddev_pop | Yes | [Aggregate Function Documentation](/docs/references/functions/aggregation/#statistical-aggregates) |
@@ -822,11 +910,11 @@ the [system table compatibility](../system_table) page.
##### Ordered-Set
-| **Feature** | **Support State** | **Details** |
-|-----------------|-------------------|-------------------------------------------------------------------------------------------------------------:|
-| mode | Yes | [Aggregate Function Documentation](/docs/references/functions/aggregation//#ordered-set-aggregate-functions) |
-| percentile_cont | Yes | [Aggregate Function Documentation](/docs/references/functions/aggregation//#ordered-set-aggregate-functions) |
-| percentile_disc | Yes | [Aggregate Function Documentation](/docs/references/functions/aggregation//#ordered-set-aggregate-functions) |
+| **Feature** | **Support State** | **Details** |
+|-----------------|-------------------|------------------------------------------------------------------------------------------------------------:|
+| mode | Yes | [Aggregate Function Documentation](/docs/references/functions/aggregation/#ordered-set-aggregate-functions) |
+| percentile_cont | Yes | [Aggregate Function Documentation](/docs/references/functions/aggregation/#ordered-set-aggregate-functions) |
+| percentile_disc | Yes | [Aggregate Function Documentation](/docs/references/functions/aggregation/#ordered-set-aggregate-functions) |
#### Window
diff --git a/content/compatibility/system_table.md b/content/compatibility/system_table.md
index 73414192..2572f856 100644
--- a/content/compatibility/system_table.md
+++ b/content/compatibility/system_table.md
@@ -36,7 +36,7 @@ built-in [system views](#system-views), or the SQL-standard [information schema]
| [pg_attrdef](https://www.postgresql.org/docs/current/catalog-pg-attrdef.html) | 🟢 | Stores column default values. |
| [pg_attribute](https://www.postgresql.org/docs/current/catalog-pg-attribute.html) | 🟢 | Contains information about table columns. |
| [pg_authid](https://www.postgresql.org/docs/current/catalog-pg-authid.html) | 🟢 | Stores information about database roles. |
-| [pg_auth_members](https://www.postgresql.org/docs/current/catalog-pg-auth-members.html) | 🟡 | Tracks role memberships. |
+| [pg_auth_members](https://www.postgresql.org/docs/current/catalog-pg-auth-members.html) | 🟢 | Tracks role memberships. |
| [pg_cast](https://www.postgresql.org/docs/current/catalog-pg-cast.html) | 🟡 | Contains information about type casts. |
| [pg_class](https://www.postgresql.org/docs/current/catalog-pg-class.html) | 🟢 | Stores information about tables, indexes, sequences, and other relations. |
| [pg_collation](https://www.postgresql.org/docs/current/catalog-pg-collation.html) | 🟢 | Contains information about collations. |
@@ -44,19 +44,19 @@ built-in [system views](#system-views), or the SQL-standard [information schema]
| [pg_conversion](https://www.postgresql.org/docs/current/catalog-pg-conversion.html) | 🟡 | Contains information about encoding conversions. |
| [pg_database](https://www.postgresql.org/docs/current/catalog-pg-database.html) | 🟢 | Stores information about databases. |
| [pg_db_role_setting](https://www.postgresql.org/docs/current/catalog-pg-db-role-setting.html) | 🟢 | Contains per-role and per-database configuration settings. |
-| [pg_default_acl](https://www.postgresql.org/docs/current/catalog-pg-default-acl.html) | 🟡 | Stores default access privileges. |
-| [pg_depend](https://www.postgresql.org/docs/current/catalog-pg-depend.html) | 🟡 | Tracks dependencies between database objects. |
-| [pg_description](https://www.postgresql.org/docs/current/catalog-pg-description.html) | 🟢 | Stores optional descriptions (comments) for database objects. |
-| [pg_enum](https://www.postgresql.org/docs/current/catalog-pg-enum.html) | 🟡 | Contains information about enum types. |
+| [pg_default_acl](https://www.postgresql.org/docs/current/catalog-pg-default-acl.html) | 🟢 | Stores default access privileges. |
+| [pg_depend](https://www.postgresql.org/docs/current/catalog-pg-depend.html) | 🟢 | Tracks dependencies between database objects; views reference pg_class, not pg_rewrite. |
+| [pg_description](https://www.postgresql.org/docs/current/catalog-pg-description.html) | 🟡 | Stores optional descriptions (comments) for database objects. |
+| [pg_enum](https://www.postgresql.org/docs/current/catalog-pg-enum.html) | 🟢 | Contains information about enum types. |
| [pg_event_trigger](https://www.postgresql.org/docs/current/catalog-pg-event-trigger.html) | 🟡 | Stores information about event triggers. |
| [pg_extension](https://www.postgresql.org/docs/current/catalog-pg-extension.html) | 🟡 | Contains information about installed extensions. |
-| [pg_foreign_data_wrapper](https://www.postgresql.org/docs/current/catalog-pg-foreign-data-wrapper.html) | 🟡 | Stores information about foreign-data wrappers. |
-| [pg_foreign_server](https://www.postgresql.org/docs/current/catalog-pg-foreign-server.html) | 🟡 | Contains information about foreign servers. |
-| [pg_foreign_table](https://www.postgresql.org/docs/current/catalog-pg-foreign-table.html) | 🟡 | Stores information about foreign tables. |
+| [pg_foreign_data_wrapper](https://www.postgresql.org/docs/current/catalog-pg-foreign-data-wrapper.html) | 🟢 | Stores information about foreign-data wrappers (`s3` and `gs`). |
+| [pg_foreign_server](https://www.postgresql.org/docs/current/catalog-pg-foreign-server.html) | 🟢 | Contains information about servers created with CREATE SERVER. |
+| [pg_foreign_table](https://www.postgresql.org/docs/current/catalog-pg-foreign-table.html) | 🟢 | Maps tables stored on S3 or Google Cloud Storage to their server. |
| [pg_index](https://www.postgresql.org/docs/current/catalog-pg-index.html) | 🟢 | Contains information about indexes. |
| [pg_inherits](https://www.postgresql.org/docs/current/catalog-pg-inherits.html) | 🟡 | Tracks table inheritance hierarchies. |
| [pg_init_privs](https://www.postgresql.org/docs/current/catalog-pg-init-privs.html) | 🟡 | Stores initial privileges of database objects. |
-| [pg_language](https://www.postgresql.org/docs/current/catalog-pg-language.html) | 🟡 | Contains information about procedural languages. |
+| [pg_language](https://www.postgresql.org/docs/current/catalog-pg-language.html) | 🟢 | Contains information about procedural languages. |
| [pg_largeobject](https://www.postgresql.org/docs/current/catalog-pg-largeobject.html) | 🟡 | Stores large object data. |
| [pg_largeobject_metadata](https://www.postgresql.org/docs/current/catalog-pg-largeobject-metadata.html) | 🟡 | Contains metadata for large objects. |
| [pg_namespace](https://www.postgresql.org/docs/current/catalog-pg-namespace.html) | 🟢 | Stores information about schemas. |
@@ -65,17 +65,17 @@ built-in [system views](#system-views), or the SQL-standard [information schema]
| [pg_opfamily](https://www.postgresql.org/docs/current/catalog-pg-opfamily.html) | 🟡 | Contains information about operator families. |
| [pg_parameter_acl](https://www.postgresql.org/docs/current/catalog-pg-parameter-acl.html) | 🟡 | Stores access privileges for server parameters. |
| [pg_partitioned_table](https://www.postgresql.org/docs/current/catalog-pg-partitioned-table.html) | 🟡 | Contains information about partitioned tables. |
-| [pg_policy](https://www.postgresql.org/docs/current/catalog-pg-policy.html) | 🟡 | Stores information about row-level security policies. |
+| [pg_policy](https://www.postgresql.org/docs/current/catalog-pg-policy.html) | 🟢 | Stores information about row-level security policies. |
| [pg_proc](https://www.postgresql.org/docs/current/catalog-pg-proc.html) | 🟢 | Contains information about functions and procedures. |
| [pg_publication](https://www.postgresql.org/docs/current/catalog-pg-publication.html) | 🟡 | Contains all publications created in the database. |
| [pg_publication_namespace](https://www.postgresql.org/docs/current/catalog-pg-publication-namespace.html) | 🟡 | Maps schemas to publications, supporting a many-to-many relationship. |
| [pg_publication_rel](https://www.postgresql.org/docs/current/catalog-pg-publication-rel.html) | 🟡 | Maps relations (tables) to publications, supporting a many-to-many relationship. |
-| [pg_range](https://www.postgresql.org/docs/current/catalog-pg-range.html) | 🟡 | Stores information about range types, supplementing entries in pg_type. |
+| [pg_range](https://www.postgresql.org/docs/current/catalog-pg-range.html) | 🟢 | Stores information about range types, supplementing entries in pg_type. |
| [pg_replication_origin](https://www.postgresql.org/docs/current/catalog-pg-replication-origin.html) | 🟡 | Contains all replication origins created, shared across all databases in a cluster. |
| [pg_rewrite](https://www.postgresql.org/docs/current/catalog-pg-rewrite.html) | 🟡 | Stores rewrite rules for tables and views. |
| [pg_seclabel](https://www.postgresql.org/docs/current/catalog-pg-seclabel.html) | 🟡 | Stores security labels on database objects, manipulable with the SECURITY LABEL command. |
| [pg_sequence](https://www.postgresql.org/docs/current/catalog-pg-sequence.html) | 🟢 | Contains information about sequences, with additional details in pg_class. |
-| [pg_shdepend](https://www.postgresql.org/docs/current/catalog-pg-shdepend.html) | 🟡 | Records dependency relationships between database objects and shared objects, like roles. |
+| [pg_shdepend](https://www.postgresql.org/docs/current/catalog-pg-shdepend.html) | 🟢 | Records dependency relationships between database objects and shared objects, like roles. |
| [pg_shdescription](https://www.postgresql.org/docs/current/catalog-pg-shdescription.html) | 🟡 | Stores optional descriptions (comments) for shared database objects. |
| [pg_shseclabel](https://www.postgresql.org/docs/current/catalog-pg-shseclabel.html) | 🟡 | Stores security labels for shared database objects. |
| [pg_statistic](https://www.postgresql.org/docs/current/catalog-pg-statistic.html) | 🟡 | Stores statistical data about the contents of the database, used by the query planner. |
@@ -141,48 +141,62 @@ Note the two rows for `ps_comment`: CedarDB split this column across two differe
Also note that `generalcompression` is only `zstd` when applying it reduces the on-disk size by a large enough margin.
Otherwise CedarDB skips the extra compression pass and stores the lightweight-encoded data as is (`generalcompression = none`, `compressedsize = encodedsize`), as is the case for `tid` here.
+#### Load history tables
+
+The system tables `cedardb_connection_load_history`, `cedardb_cpu_load_history`, `cedardb_disk_load_history`, `cedardb_memory_load_history`, and `cedardb_network_load_history` record connection, CPU, disk, memory, and network metrics at a regular interval.
+Querying them requires an enterprise license.
+For their columns and configuration, see [Load History System Tables](/docs/references/advanced/loadhistory).
+
## System Views
System views provide convenient access to system information.
System tables often contain numeric identifiers, e.g., for the owner of tables.
The views instead use more human-readable symbolic names.
-| Feature | Support State | Details |
-|----------------------------------------------------------------------------------------------------------------------|---------------|---------------------------------------------------------------------------------|
-| [pg_available_extensions](https://www.postgresql.org/docs/current/view-pg-available-extensions.html) | 🟡 | Lists available extensions. |
-| [pg_available_extension_versions](https://www.postgresql.org/docs/current/view-pg-available-extension-versions.html) | 🟡 | Shows available versions of extensions. |
-| [pg_backend_memory_contexts](https://www.postgresql.org/docs/current/view-pg-backend-memory-contexts.html) | 🟡 | Displays memory contexts of the backend. |
-| [pg_config](https://www.postgresql.org/docs/current/view-pg-config.html) | 🔴 | Provides access to compile-time configuration parameters. |
-| [pg_cursors](https://www.postgresql.org/docs/current/view-pg-cursors.html) | 🔴 | Lists open cursors. |
-| [pg_file_settings](https://www.postgresql.org/docs/current/view-pg-file-settings.html) | 🔴 | Summarizes contents of configuration files. |
-| [pg_group](https://www.postgresql.org/docs/current/view-pg-group.html) | 🟢 | Displays groups of database users. |
-| [pg_hba_file_rules](https://www.postgresql.org/docs/current/view-pg-hba-file-rules.html) | 🔴 | Summarizes client authentication configuration. |
-| [pg_ident_file_mappings](https://www.postgresql.org/docs/current/view-pg-ident-file-mappings.html) | 🟡 | Summarizes client user name mapping configuration. |
-| [pg_indexes](https://www.postgresql.org/docs/current/view-pg-indexes.html) | 🟢 | Shows information about indexes. |
-| [pg_locks](https://www.postgresql.org/docs/current/view-pg-locks.html) | 🟡 | Displays locks currently held or awaited. |
-| [pg_matviews](https://www.postgresql.org/docs/current/view-pg-matviews.html) | 🟢 | Lists materialized views. |
-| [pg_policies](https://www.postgresql.org/docs/current/view-pg-policies.html) | 🟡 | Displays information about policies. |
-| [pg_prepared_statements](https://www.postgresql.org/docs/current/view-pg-prepared-statements.html) | 🟡 | Lists prepared statements. |
-| [pg_prepared_xacts](https://www.postgresql.org/docs/current/view-pg-prepared-xacts.html) | 🔴 | Shows prepared transactions. |
-| [pg_publication_tables](https://www.postgresql.org/docs/current/view-pg-publication-tables.html) | 🔴 | Displays publications and their associated tables. |
-| [pg_replication_origin_status](https://www.postgresql.org/docs/current/view-pg-replication-origin-status.html) | 🔴 | Provides information about replication origins, including replication progress. |
-| [pg_replication_slots](https://www.postgresql.org/docs/current/view-pg-replication-slots.html) | 🔴 | Displays replication slot information. |
-| [pg_roles](https://www.postgresql.org/docs/current/view-pg-roles.html) | 🟢 | Lists database roles. |
-| [pg_rules](https://www.postgresql.org/docs/current/view-pg-rules.html) | 🟡 | Shows information about rules. |
-| [pg_seclabels](https://www.postgresql.org/docs/current/view-pg-seclabels.html) | 🟡 | Displays security labels. |
-| [pg_sequences](https://www.postgresql.org/docs/current/view-pg-sequences.html) | 🟡 | Lists sequences. |
-| [pg_settings](https://www.postgresql.org/docs/current/view-pg-settings.html) | 🟢 | Provides access to parameter settings. |
-| [pg_shadow](https://www.postgresql.org/docs/current/view-pg-shadow.html) | 🟢 | Displays database users. |
-| [pg_shmem_allocations](https://www.postgresql.org/docs/current/view-pg-shmem-allocations.html) | 🟡 | Shows shared memory allocations. |
-| [pg_stats](https://www.postgresql.org/docs/current/view-pg-stats.html) | 🔴 | Provides planner statistics. |
-| [pg_stats_ext](https://www.postgresql.org/docs/current/view-pg-stats-ext.html) | 🟡 | Displays extended planner statistics. |
-| [pg_stats_ext_exprs](https://www.postgresql.org/docs/current/view-pg-stats-ext-exprs.html) | 🟡 | Shows extended planner statistics for expressions. |
-| [pg_tables](https://www.postgresql.org/docs/current/view-pg-tables.html) | 🟢 | Lists tables. |
-| [pg_timezone_abbrevs](https://www.postgresql.org/docs/current/view-pg-timezone-abbrevs.html) | 🟢 | Displays time zone abbreviations. |
-| [pg_timezone_names](https://www.postgresql.org/docs/current/view-pg-timezone-names.html) | 🟢 | Lists time zone names. |
-| [pg_user](https://www.postgresql.org/docs/current/view-pg-user.html) | 🟢 | Shows database users. |
-| [pg_user_mappings](https://www.postgresql.org/docs/current/view-pg-user-mappings.html) | 🟡 | Displays user mappings. |
-| [pg_views](https://www.postgresql.org/docs/current/view-pg-views.html) | 🟢 | Lists views. |
+| Feature | Support State | Details |
+|----------------------------------------------------------------------------------------------------------------------|---------------|------------------------------------------------------------------------------------------------------|
+| [pg_available_extensions](https://www.postgresql.org/docs/current/view-pg-available-extensions.html) | 🟡 | Lists available extensions. |
+| [pg_available_extension_versions](https://www.postgresql.org/docs/current/view-pg-available-extension-versions.html) | 🟡 | Shows available versions of extensions. |
+| [pg_backend_memory_contexts](https://www.postgresql.org/docs/current/view-pg-backend-memory-contexts.html) | 🟡 | Displays memory contexts of the backend. |
+| [pg_config](https://www.postgresql.org/docs/current/view-pg-config.html) | 🟡 | Provides access to compile-time configuration parameters. |
+| [pg_cursors](https://www.postgresql.org/docs/current/view-pg-cursors.html) | 🟡 | Lists open cursors. |
+| [pg_file_settings](https://www.postgresql.org/docs/current/view-pg-file-settings.html) | 🟡 | Summarizes contents of configuration files. |
+| [pg_group](https://www.postgresql.org/docs/current/view-pg-group.html) | 🟢 | Displays groups of database users. |
+| [pg_hba_file_rules](https://www.postgresql.org/docs/current/view-pg-hba-file-rules.html) | 🟡 | Summarizes client authentication configuration. |
+| [pg_ident_file_mappings](https://www.postgresql.org/docs/current/view-pg-ident-file-mappings.html) | 🟡 | Summarizes client user name mapping configuration. |
+| [pg_indexes](https://www.postgresql.org/docs/current/view-pg-indexes.html) | 🟢 | Shows information about indexes. |
+| [pg_locks](https://www.postgresql.org/docs/current/view-pg-locks.html) | 🟡 | Displays locks currently held or awaited. |
+| [pg_matviews](https://www.postgresql.org/docs/current/view-pg-matviews.html) | 🟢 | Lists materialized views. |
+| [pg_policies](https://www.postgresql.org/docs/current/view-pg-policies.html) | 🟢 | Displays information about policies. |
+| [pg_prepared_statements](https://www.postgresql.org/docs/current/view-pg-prepared-statements.html) | 🟢 | Lists prepared statements. |
+| [pg_prepared_xacts](https://www.postgresql.org/docs/current/view-pg-prepared-xacts.html) | 🟡 | Shows prepared transactions. |
+| [pg_publication_tables](https://www.postgresql.org/docs/current/view-pg-publication-tables.html) | 🟡 | Displays publications and their associated tables. |
+| [pg_replication_origin_status](https://www.postgresql.org/docs/current/view-pg-replication-origin-status.html) | 🟡 | Provides information about replication origins, including replication progress. |
+| [pg_replication_slots](https://www.postgresql.org/docs/current/view-pg-replication-slots.html) | 🟡 | Displays replication slot information. |
+| [pg_roles](https://www.postgresql.org/docs/current/view-pg-roles.html) | 🟢 | Lists database roles. |
+| [pg_rules](https://www.postgresql.org/docs/current/view-pg-rules.html) | 🟡 | Shows information about rules. |
+| [pg_seclabels](https://www.postgresql.org/docs/current/view-pg-seclabels.html) | 🟡 | Displays security labels. |
+| [pg_sequences](https://www.postgresql.org/docs/current/view-pg-sequences.html) | 🟢 | Lists sequences. |
+| [pg_settings](https://www.postgresql.org/docs/current/view-pg-settings.html) | 🟢 | Provides access to parameter settings. |
+| [pg_shadow](https://www.postgresql.org/docs/current/view-pg-shadow.html) | 🟢 | Displays database users. |
+| [pg_shmem_allocations](https://www.postgresql.org/docs/current/view-pg-shmem-allocations.html) | 🟡 | Shows shared memory allocations. |
+| pg_stat_activity | 🟢 | One row per session. Requires an enterprise license; without one, it returns only a placeholder row. |
+| pg_stat_all_indexes | 🟡 | Index statistics for all tables. The counters are not populated. |
+| pg_stat_all_tables | 🟡 | Table statistics. Only `n_live_tup` is populated. |
+| pg_stat_database | 🟢 | Database-wide statistics. Requires an enterprise license; without one, only `numbackends` is set. |
+| pg_stat_gssapi | 🟡 | One row per session. Requires an enterprise license; without one, it returns no rows. |
+| pg_stat_ssl | 🟡 | One row per session; `ssl` is false. Requires an enterprise license; without one, no rows. |
+| pg_stat_user_indexes | 🟡 | Index statistics for user tables. The counters are not populated. |
+| pg_stat_user_tables | 🟡 | Table statistics for user tables. Only `n_live_tup` is populated. |
+| [pg_stats](https://www.postgresql.org/docs/current/view-pg-stats.html) | 🟡 | Provides planner statistics. |
+| [pg_stats_ext](https://www.postgresql.org/docs/current/view-pg-stats-ext.html) | 🟡 | Displays extended planner statistics. |
+| [pg_stats_ext_exprs](https://www.postgresql.org/docs/current/view-pg-stats-ext-exprs.html) | 🟡 | Shows extended planner statistics for expressions. |
+| [pg_tables](https://www.postgresql.org/docs/current/view-pg-tables.html) | 🟢 | Lists tables. |
+| [pg_timezone_abbrevs](https://www.postgresql.org/docs/current/view-pg-timezone-abbrevs.html) | 🟢 | Displays time zone abbreviations. |
+| [pg_timezone_names](https://www.postgresql.org/docs/current/view-pg-timezone-names.html) | 🟢 | Lists time zone names. |
+| [pg_user](https://www.postgresql.org/docs/current/view-pg-user.html) | 🟢 | Shows database users. |
+| [pg_user_mappings](https://www.postgresql.org/docs/current/view-pg-user-mappings.html) | 🟡 | Displays user mappings. |
+| [pg_views](https://www.postgresql.org/docs/current/view-pg-views.html) | 🟢 | Lists views. |
### CedarDB System Views
@@ -247,7 +261,7 @@ select * from information_schema.tables;
| column_column_usage | 🟡 | |
| column_domain_usage | 🟡 | |
| column_options | 🟡 | |
-| column_privileges | 🟡 | |
+| column_privileges | 🟢 | |
| column_udt_usage | 🟢 | |
| columns | 🟢 | |
| constraint_column_usage | 🟢 | |
@@ -259,21 +273,21 @@ select * from information_schema.tables;
| element_types | 🟢 | |
| enabled_roles | 🟢 | |
| foreign_data_wrapper_options | 🟡 | |
-| foreign_data_wrappers | 🟡 | |
-| foreign_server_options | 🟡 | |
-| foreign_servers | 🟡 | |
+| foreign_data_wrappers | 🟢 | |
+| foreign_server_options | 🟢 | |
+| foreign_servers | 🟢 | |
| foreign_table_options | 🟡 | |
| foreign_tables | 🟡 | |
| key_column_usage | 🟢 | |
| parameters | 🟢 | |
| referential_constraints | 🟢 | |
-| role_column_grants | 🟡 | |
-| role_routine_grants | 🟡 | |
-| role_table_grants | 🟡 | |
+| role_column_grants | 🟢 | |
+| role_routine_grants | 🟢 | |
+| role_table_grants | 🟢 | |
| role_udt_grants | 🟡 | |
| role_usage_grants | 🟢 | |
| routine_column_usage | 🟡 | |
-| routine_privileges | 🟡 | |
+| routine_privileges | 🟢 | |
| routine_routine_usage | 🟡 | |
| routine_sequence_usage | 🟡 | |
| routine_table_usage | 🟡 | |
@@ -281,21 +295,21 @@ select * from information_schema.tables;
| schemata | 🟢 | |
| sequences | 🟢 | |
| sql_features | 🟡 | |
-| sql_implementation_info | 🟡 | |
+| sql_implementation_info | 🟢 | |
| sql_parts | 🟡 | |
-| sql_sizing | 🟡 | |
+| sql_sizing | 🟢 | |
| table_constraints | 🟢 | |
-| table_privileges | 🟡 | |
+| table_privileges | 🟢 | |
| tables | 🟢 | |
| transforms | 🟡 | |
| triggered_update_columns | 🟡 | |
| triggers | 🟡 | |
| udt_privileges | 🟡 | |
-| usage_privileges | 🟡 | |
+| usage_privileges | 🟢 | |
| user_defined_types | 🟡 | |
| user_mapping_options | 🟡 | |
| user_mappings | 🟡 | |
-| view_column_usage | 🟡 | |
-| view_routine_usage | 🟡 | |
-| view_table_usage | 🟡 | |
+| view_column_usage | 🟢 | |
+| view_routine_usage | 🟢 | |
+| view_table_usage | 🟢 | |
| views | 🟢 | |
diff --git a/content/references/advanced/asof_join.md b/content/references/advanced/asof_join.md
index ffcde938..3d8cc845 100644
--- a/content/references/advanced/asof_join.md
+++ b/content/references/advanced/asof_join.md
@@ -12,14 +12,27 @@ Usage example:
-- Example schema
create table humidity(measure_time timestamp, value double);
create table temperature(measure_time timestamp, value double);
+insert into humidity values ('2024-06-01 07:00', 40), ('2024-06-01 08:00', 50), ('2024-06-01 09:00', 55);
+insert into temperature values ('2024-06-01 07:30', 20), ('2024-06-01 08:45', 21);
-- Get combined measurements, e.g., to calculate a "feels like" heat index
select h.measure_time, t.measure_time, h.value as humidity, t.value as temperature
from humidity h
asof join temperature t
-on h.measure_time >= t.measure_time;
+on h.measure_time >= t.measure_time
+order by h.measure_time;
```
+```text
+ measure_time | measure_time | humidity | temperature
+---------------------+---------------------+----------+-------------
+ 2024-06-01 08:00:00 | 2024-06-01 07:30:00 | 50 | 20
+ 2024-06-01 09:00:00 | 2024-06-01 08:45:00 | 55 | 21
+(2 rows)
+```
+
+The 07:00 humidity measurement has no earlier temperature reading, so it does not appear in the result.
+
In this example, we get the latest temperature for a humidity measurement, even when the times do not line up precisely.
Expressing this query in standard SQL is more involved and usually results in slower execution, see
[Alternative Implementations](#alternative-implementations).
@@ -34,6 +47,41 @@ In contrast to regular joins, AsOf joins are asymmetric:
AsOf joins produce *at most* one tuple on the left side with the matching tuple from the right.
Tuples that do not find a matching value are filtered out.
+The following example groups matches with an additional equality condition.
+For each tree, it returns the latest soil moisture reading of the same tree at or before the time of the height measurement:
+
+```sql
+create table tree_heights(tree_id int, measured_at int, height_m numeric);
+create table soil_moisture(tree_id int, measured_at int, moisture int);
+insert into tree_heights values (1, 10, 4.2), (2, 10, 3.1), (1, 5, 4.0);
+insert into soil_moisture values (1, 1, 30), (1, 8, 35), (2, 3, 41), (2, 9, 44);
+
+select h.tree_id, h.measured_at, h.height_m, m.measured_at as moisture_at, m.moisture
+from tree_heights h
+asof join soil_moisture m
+ on h.tree_id = m.tree_id and h.measured_at >= m.measured_at
+order by h.tree_id, h.measured_at;
+```
+
+```text
+ tree_id | measured_at | height_m | moisture_at | moisture
+---------+-------------+----------+-------------+----------
+ 1 | 5 | 4.000000 | 1 | 30
+ 1 | 10 | 4.200000 | 8 | 35
+ 2 | 10 | 3.100000 | 9 | 44
+(3 rows)
+```
+
+If several rows of the right side are equally close, CedarDB returns only one of them.
+
+The `on` condition must contain exactly one ordering comparison, optionally combined with equality conditions using `and`.
+
+{{< callout type="warning" >}}
+CedarDB only supports the inner `asof join`.
+The parser also accepts `asof left join`, `asof right join`, and `asof full join`, but these currently behave like an inner `asof join` and drop rows without a match.
+`asof join ... using (...)` without an ordering comparison behaves like a regular equi-join.
+{{< /callout >}}
+
{{< callout type="info" >}}
CedarDB currently does not use any indexes for AsOf joins.
Support for index-supported AsOf joins is planned for a future CedarDB release.
diff --git a/content/references/advanced/benchmarking.md b/content/references/advanced/benchmarking.md
index aada3cdc..3d345d6d 100644
--- a/content/references/advanced/benchmarking.md
+++ b/content/references/advanced/benchmarking.md
@@ -9,13 +9,6 @@ For benchmarking purposes, we support several advanced settings in CedarDB's int
## Repetition of queries
To validate performance, it is important to run queries multiple times.
-To repeat a query, you can either repeat the execution (`e`), the compilation (`c`), or both (`a`).
-To change the repetition mode, simply query this command.
-
-```text
-\set repeatmode 'a'
-```
-
The number of repetitions can be set with the following command.
```text
diff --git a/content/references/advanced/createserver.md b/content/references/advanced/createserver.md
index c873cd3e..88934b67 100644
--- a/content/references/advanced/createserver.md
+++ b/content/references/advanced/createserver.md
@@ -6,6 +6,10 @@ linkTitle: "Create Server"
Create server adds a new remote server to the database catalog.
Those servers can then be used for remote tables (see [create table](/docs/references/objects/tables)).
+{{< callout type="info" >}}
+`CREATE SERVER` requires an enterprise license.
+{{< /callout >}}
+
The following example defines a server that can later be referenced as `server_name`.
```sql
@@ -45,6 +49,55 @@ For example if you want to create a user for us-east-1 region, you can do the fo
3. Click on the user and go to the security credentials tab
4. Create a new access key, which looks similar to AKIA5CBDR…, and note down the secret that is shown
+## Managing servers
+
+`CREATE SERVER IF NOT EXISTS` skips the statement if a server with that name exists.
+
+The `pg_foreign_server` system table lists all servers. CedarDB masks the secret:
+
+```sql
+create server tree_archive foreign data wrapper s3 options (location 's3://tree-archive:eu-central-1', id 'AKIAEXAMPLE', secret 'example-secret');
+select srvname, srvowner::regrole, srvoptions from pg_foreign_server;
+```
+
+```text
+ srvname | srvowner | srvoptions
+--------------+----------+---------------------------------------------------------------------
+ tree_archive | postgres | {location=s3://tree-archive:eu-central-1,id=AKIAEXAMPLE,secret=***}
+(1 row)
+```
+
+You can change the owner of a server and drop it:
+
+```sql
+-- Assumes a role named forester exists
+alter server tree_archive owner to forester;
+drop server tree_archive;
+```
+
+`DROP SERVER` supports `IF EXISTS` and `CASCADE`.
+
## Permissions
-To create a server, you need to have superuser or `createdb` permissions.
+Only superusers can create servers.
+
+The owner of a server can drop it and grant the `USAGE` privilege on it:
+
+```sql
+grant usage on foreign server tree_archive to forester;
+grant usage on foreign server tree_archive to forester with grant option;
+revoke usage on foreign server tree_archive from forester cascade;
+```
+
+CedarDB records the privileges and their grantor in `pg_foreign_server.srvacl`.
+A role without the grant option gets `ERROR: permission denied for remoteserver ''` when it tries to grant `USAGE`.
+
+To create a table on a server, you need the `USAGE` privilege on it, directly or through a role you are a member of.
+The owner of the server has it by default, and superusers bypass the check.
+`USAGE` is only checked when you create a table: revoking it does not affect existing tables on the server.
+
+## PostgreSQL Differences
+
+* Only the foreign data wrappers `s3` and `gs` (see [Tables on Google Cloud Storage](../gs)) exist. `CREATE FOREIGN DATA WRAPPER` raises `CREATE FDW not implemented yet`.
+* `ALTER SERVER ... OPTIONS` raises `ALTER SERVER not implemented yet`. To change the options, drop and re-create the server.
+* `CREATE SERVER` requires superuser rights. PostgreSQL requires `USAGE` on the foreign data wrapper.
diff --git a/content/references/advanced/gs.md b/content/references/advanced/gs.md
index a72a4299..347a008e 100644
--- a/content/references/advanced/gs.md
+++ b/content/references/advanced/gs.md
@@ -14,6 +14,10 @@ For more technical details, you can also read up on our [blog](https://cedardb.c
CedarDB still uses a regular (local) storage device for metadata and recently updated (hot) data.
{{}}
+{{< callout type="info" >}}
+`CREATE SERVER` requires an enterprise license.
+{{< /callout >}}
+
## Creating a Table on Google Cloud Storage
Before creating tables that work on remote data, you need to use the [CREATE SERVER](../../advanced/createserver/) statement to first define the location of the data (e.g., the bucket) and the credentials.
diff --git a/content/references/advanced/loadhistory.md b/content/references/advanced/loadhistory.md
index 92909b54..608ddbe2 100644
--- a/content/references/advanced/loadhistory.md
+++ b/content/references/advanced/loadhistory.md
@@ -44,6 +44,24 @@ Setting this to `0` disables load history collection altogether.
Setting `loadhistory.retention` specifies the number of measurements that are stored before they will be dropped. The default value for this setting is `8640`.
For an interval of 10 seconds, this will retain values for 24 hours.
+Set both values at startup in the [configuration file or as environment variables](../../configuration), for example `LOADHISTORY_INTERVAL=5000`.
+In SQL, the settings appear as `debug.loadhistory.interval` and `debug.loadhistory.retention`:
+
+```sql
+select name, setting from pg_settings where name like 'debug.loadhistory%';
+```
+
+```text
+ name | setting
+-----------------------------+---------
+ debug.loadhistory.interval | 10000
+ debug.loadhistory.retention | 8640
+(2 rows)
+```
+
+Without an enterprise license, querying a `cedardb_*_load_history` table fails with `ERROR: Load history collection requires an enterprise license to be executed.`
+CedarDB only collects measurements if the license is active at startup, i.e., set in the configuration file or as the `LICENSE_KEY` environment variable.
+
## Metrics and Tables
All `cedardb_*_load_history` tables have as their first column the `timestamp` of when the measurement was taken.
diff --git a/content/references/advanced/pgvector.md b/content/references/advanced/pgvector.md
index 055f496b..38c0220a 100644
--- a/content/references/advanced/pgvector.md
+++ b/content/references/advanced/pgvector.md
@@ -121,13 +121,15 @@ select vector_cmp('[1,2,3]'::vector, '[0,1,2]'::vector);
```
Since vectors support basic arithmetic and comparison, you can also use them
-with aggregation functions such as `sum`, `avg`, `min`, and `max`:
+with the aggregation functions `sum`, `min`, and `max`:
```sql
create table my_vectors ( v vector not null );
-select sum(v), avg(v), min(v), max(v) from my_vectors;
+select sum(v), min(v), max(v) from my_vectors;
```
+`avg` is not supported for vectors.
+
## Vector Functions and Operators
When working with vectors, it is very common to compare them according to
@@ -145,6 +147,11 @@ For compatibility with pgvector, the `inner_product` function returns the inner
product of two vectors but the `<#>` operator returns the *negative* inner
product.
+If the two vectors have different dimensions, CedarDB pads the smaller vector
+with zeros, as for arithmetic. For example,
+`l2_distance('[1,2]'::vector, '[1,2,3]'::vector)` returns `3`. The pgvector
+extension raises an error in this case.
+
Here are some examples:
```sql
diff --git a/content/references/advanced/prepare.md b/content/references/advanced/prepare.md
index 54c3ebf1..2571182a 100644
--- a/content/references/advanced/prepare.md
+++ b/content/references/advanced/prepare.md
@@ -125,8 +125,38 @@ In many cases, CedarDB infers parameter types from the surrounding query, and an
However, in some cases it is not possible to infer the data type, which results in an error:
```sql
-prepare echo as select $1;
+prepare isempty as select $1 is null;
ERROR: unable to infer the data type for parameter $1
```
In such cases, you can explicitly cast the parameter to a type, e.g.: `$1::int`.
+If a parameter is only used as an output column, such as in `prepare echo as select $1;`, CedarDB uses the type `text`.
+
+You can list the prepared statements of your session in the `pg_prepared_statements` system view:
+
+```sql
+prepare add as select $1::int + $2::int as sum;
+select name, parameter_types, result_types from pg_prepared_statements where name <> '';
+```
+
+```text
+ name | parameter_types | result_types
+------+-------------------+--------------
+ add | {integer,integer} | {integer}
+(1 row)
+```
+
+## Permissions
+
+`PREPARE` does not check any privileges.
+CedarDB checks the privileges of the prepared statement each time you run it with `EXECUTE`:
+
+```sql
+-- As a role without the SELECT privilege on trees
+prepare findtree as select * from trees where id = $1;
+-- PREPARE succeeds
+execute findtree(1);
+ERROR: permission denied for table 'trees'
+```
+
+`DEALLOCATE` requires no privileges.
diff --git a/content/references/advanced/s3.md b/content/references/advanced/s3.md
index 5e2635ec..638ab390 100644
--- a/content/references/advanced/s3.md
+++ b/content/references/advanced/s3.md
@@ -14,6 +14,10 @@ For more technical details, you can also read up on our [blog](https://cedardb.c
CedarDB still uses a regular (local) storage device for metadata and recently updated (hot) data.
{{}}
+{{< callout type="info" >}}
+`CREATE SERVER` requires an enterprise license.
+{{< /callout >}}
+
## Creating a Table on S3
Before creating tables that work on remote data, you need to use the [CREATE SERVER](../../advanced/createserver/) statement to first define the location of the data (e.g., the bucket) and the credentials.
diff --git a/content/references/configuration.md b/content/references/configuration.md
index ed3f5381..b9f500f1 100644
--- a/content/references/configuration.md
+++ b/content/references/configuration.md
@@ -65,7 +65,7 @@ You can also update the configuration file from a SQL session with the [`ALTER S
statement, without editing the file by hand:
```sql
-ALTER SYSTEM SET buffersize = '8G';
+ALTER SYSTEM SET debug.buffersize = '8G';
```
As with the config file, these changes only take effect after a restart.
diff --git a/content/references/datatypes/array.md b/content/references/datatypes/array.md
index 76be9ea6..bc3d7794 100644
--- a/content/references/datatypes/array.md
+++ b/content/references/datatypes/array.md
@@ -47,11 +47,3 @@ select a[1] as first from data;
CedarDB supports arrays with a maximum of 4 GB of underlying data.
For most data types, this limits array to about one billion elements.
-
-```sql
-select array_fill('x', array[1000000000]);
-```
-
-```text
-ERROR: string length overflow
-```
diff --git a/content/references/datatypes/bit.md b/content/references/datatypes/bit.md
index 478e4cd6..d779d858 100644
--- a/content/references/datatypes/bit.md
+++ b/content/references/datatypes/bit.md
@@ -28,3 +28,18 @@ select * from example;
11001010111111101011101010111110
(4 rows)
```
+
+## Operators
+
+| Operator | Description | Example | Result |
+|----------|-----------------------|-------------------------|--------|
+| `\|\|` | Concatenation | `B'10' \|\| B'01'` | `1001` |
+| `&` | Bitwise AND | `B'1010' & B'0110'` | `0010` |
+| `\|` | Bitwise OR | `B'1010' \| B'0101'` | `1111` |
+| `#` | Bitwise XOR | `B'1010' # B'0110'` | `1100` |
+
+For functions on bit strings, such as `length`, `bit_count`, `get_bit`, and `set_bit`, see [bit string functions](/docs/references/functions/bitstring).
+
+## PostgreSQL Differences
+
+- The bitwise NOT operator `~` and the shift operators `<<` and `>>` are not supported for bit strings.
diff --git a/content/references/datatypes/boolean.md b/content/references/datatypes/boolean.md
index 8a771256..31caf060 100644
--- a/content/references/datatypes/boolean.md
+++ b/content/references/datatypes/boolean.md
@@ -57,7 +57,7 @@ This results in more subtle ternary truth tables for booleans:
```sql
with bools(v) as (values (true), (null), (false))
-select a.v as a, b.v as b, a and b as and, a or b as or
+select a.v as a, b.v as b, a.v and b.v as "and", a.v or b.v as "or"
from bools a, bools b;
```
diff --git a/content/references/datatypes/date.md b/content/references/datatypes/date.md
index 5aeb4314..24577ff3 100644
--- a/content/references/datatypes/date.md
+++ b/content/references/datatypes/date.md
@@ -5,7 +5,7 @@ weight: 14
---
Date is a day-accurate type without time of day references in ISO 8601 `YYYY-MM-DD` format.
-CedarDB also accepts [PostgreSQL notation](https://www.postgresql.org/docs/current/datatype-datetime.html#DATATYPE-DATETIME-DATE-TABLE).
+CedarDB also accepts the common PostgreSQL input formats, such as `January 8, 1999`, `1999-Jan-08`, `19990108`, and `J2451187`.
## Usage Example
@@ -39,15 +39,17 @@ Operations on dates are range checked, so that e.g., overflows will never cause
## Input
In a session, you can change the `DateStyle` setting, which determines the parsing when entering ambiguous dates.
+The default is `ISO, YMD`.
+CedarDB accepts the field orders `DMY`, `MDY`, and `YMD`, optionally prefixed with `ISO` and a comma, e.g., `ISO, DMY`.
```sql
-- The common "little-endian" date style
set DateStyle = 'DMY';
-select '01/02/03';
+select date '01/02/03';
```
```text
- ?column?
+ ?column?
------------
2003-02-01
(1 row)
@@ -56,12 +58,17 @@ select '01/02/03';
```sql
-- US "middle-endian" date style
set DateStyle = 'MDY';
-select '01/02/03';
+select date '01/02/03';
```
```text
- ?column?
+ ?column?
------------
2003-01-02
(1 row)
```
+
+## PostgreSQL Differences
+
+- The special values `infinity` and `-infinity` are not supported.
+- CedarDB always prints dates in ISO format. The `DateStyle` output styles `SQL`, `Postgres`, and `German` are not supported.
diff --git a/content/references/datatypes/float.md b/content/references/datatypes/float.md
index ca419bc3..f6f9fad7 100644
--- a/content/references/datatypes/float.md
+++ b/content/references/datatypes/float.md
@@ -21,18 +21,18 @@ create table constants (
value real
);
insert into constants
- values ('pi', 3.141592653589793238462643383279502884),
+ values ('pi', 3.14159265),
('planck', 6.62607015e-34),
('avogadro', 6.02214076e23);
select * from constants;
```
```text
- name | value
-----------+--------------
- pi | 3.14159
- planck | 6.62607e-34
- avogadro | 6.02214e+23
+ name | value
+----------+---------------
+ pi | 3.1415927
+ planck | 6.62607e-34
+ avogadro | 6.0221406e+23
(3 rows)
```
@@ -67,17 +67,17 @@ Consider, e.g., the following query:
```sql
with x(i) as (
- values (0.1),
- (0.2),
- (-0.3)
-)
-select sum(i::double precision) from x;
+ values (0.1::double precision),
+ (0.2::double precision),
+ (-0.3::double precision)
+)
+select sum(i) from x;
```
```text
- sum
-----------------------
- 5.55111512312578e-17
+ sum
+-----------------------
+ 5.551115123125783e-17
(1 row)
```
@@ -89,9 +89,9 @@ CedarDB executes queries in parallel and thus cannot guarantee the order in whic
For the above query, an equally valid result would be:
```text
- sum
-----------------------
- 2.77555756156289e-17
+ sum
+------------------------
+ 2.7755575615628914e-17
(1 row)
```
diff --git a/content/references/datatypes/inet.md b/content/references/datatypes/inet.md
index 310e4b03..82e1fd91 100644
--- a/content/references/datatypes/inet.md
+++ b/content/references/datatypes/inet.md
@@ -99,6 +99,24 @@ select a from (values
(5 rows)
```
+## Constraints
+
+Two values are equal if they have the same address and netmask length, so `10.0.0.1` and `10.0.0.1/32` are duplicates.
+
+```sql
+create table gateways (
+ address inet primary key,
+ site text
+);
+insert into gateways values ('10.0.0.1', 'greenhouse');
+insert into gateways values ('10.0.0.1/32', 'nursery');
+```
+
+```text
+ERROR: duplicate key value violates unique constraint "gateways_pkey"
+DETAIL: Key (address)=(10.0.0.1) already exists.
+```
+
## Functions
### inet_client_addr
@@ -116,3 +134,10 @@ select inet_client_addr();
192.168.1.42
(1 row)
```
+
+## PostgreSQL Differences
+
+- The `cidr`, `macaddr`, and `macaddr8` types are not supported.
+- The network operators, such as `<<`, `>>`, `&&`, `~`, `&`, `|`, `+`, and `-`, are not supported.
+- Some network functions, such as `masklen`, `family`, and `abbrev`, are not supported.
+- The aggregates `min` and `max`, and arrays of `inet` (`inet[]`), are not supported.
diff --git a/content/references/datatypes/interval.md b/content/references/datatypes/interval.md
index 27972c2b..83ec60b0 100644
--- a/content/references/datatypes/interval.md
+++ b/content/references/datatypes/interval.md
@@ -22,18 +22,19 @@ select * from example;
```
```text
- duration
-----------------
- 90 00:00:00
- 21 00:00:00
- 0-1 1 00:00:00
+ duration
+-------------
+ 90 days
+ 21 days
+ 1 mon 1 day
(3 rows)
```
{{< callout type="info" >}}
-By default, CedarDB uses the SQL standard format, which has a terse syntax.
-If you prefer human-readable output, consider changing the output format to PostgreSQL style:
-`set IntervalStyle to 'postgres';`
+By default, CedarDB prints intervals in the PostgreSQL style shown above.
+You can change the output format with the `IntervalStyle` setting, for example to the terse SQL standard format:
+`set IntervalStyle to 'sql_standard';`
+CedarDB supports the styles `postgres`, `postgres_verbose`, `sql_standard`, and `iso_8601`.
{{< /callout >}}
## Why Intervals?
@@ -62,3 +63,8 @@ select date '2024-02-28' + interval '2' day;
2024-03-01 00:00:00
(1 row)
```
+
+## PostgreSQL Differences
+
+- A fractional value combined with a separate field qualifier, such as `interval '1.5' day`, is rejected. PostgreSQL accepts it.
+- CedarDB accepts a fractional-second precision such as `interval(0)` but ignores it: values always keep microsecond resolution.
diff --git a/content/references/datatypes/numeric.md b/content/references/datatypes/numeric.md
index 55aacb2f..0ff5c06a 100644
--- a/content/references/datatypes/numeric.md
+++ b/content/references/datatypes/numeric.md
@@ -92,7 +92,7 @@ values (power(2, 126)::numeric(38,0));
The following will produce an overflow, since $2^{126} + 2^{126} > 2^{127}-1$.
```sql
-select i + i from integers;
+select i + i from numerics;
```
```text
@@ -116,10 +116,21 @@ select try(i + i) from numerics;
PostgreSQL offers a maximum precision of 131072 and scale of 16383, where CedarDB restricts precision and scale to a
maximum of 38, for performance reasons.
+A `numeric` column or cast without precision and scale uses `numeric(38, 6)`, so values keep at most six fractional digits.
Additionally, PostgreSQL allows `NaN`, `+Infinity`, and `-Infinity` as special numeric values.
Since all operations on numerics are bounds-checked, these values cannot occur during regular operations.
However, PostgreSQL still allows entering them directly.
-
CedarDB forbids entering these values as numeric data types.
See [Float](/docs/references/datatypes/float) for data types supporting those special values.
+
+When CedarDB converts a value to a `numeric` with fewer fractional digits, the result depends on the source and the
+target precision:
+
+| Conversion | Example | Result |
+|------------------------------------------------------------|-----------------------|---------------------------------------------------|
+| Numeric value to precision 18 or less | `1.5::numeric(3,0)` | `2` (rounded, as in PostgreSQL) |
+| Numeric value to precision 19 to 38, or to plain `numeric` | `1.5::numeric(38,0)` | `1` (excess digits are truncated) |
+| String to any `numeric` | `'1.5'::numeric(3,0)` | `ERROR: invalid number format: loss of precision` |
+
+PostgreSQL rounds in all three cases.
diff --git a/content/references/datatypes/range.md b/content/references/datatypes/range.md
index 764a439f..3e4443ae 100644
--- a/content/references/datatypes/range.md
+++ b/content/references/datatypes/range.md
@@ -24,7 +24,7 @@ CREATE TABLE trees (
);
-- Insert some trees
-INSERT INTO tree VALUES
+INSERT INTO trees VALUES
('Cedar', '[15,40)'::int4range),
('Oak', '[30,40)'::int4range),
('Maple', '[35,40)'::int4range);
@@ -74,7 +74,7 @@ INSERT INTO trees VALUES
('Birch', '(10,30)'), -- Both bounds are exclusive
('Sequoia', '[100,)'), -- There is no value for the upper bound, claiming that sequoia trees can reach arbitrary height
('Pine', int4range(15, 25)), -- Using the constructor function with the default bounds '[)'
- ('Appletree', int4range(2, 12, '[]')) -- Using the constructor function with explicit bounds
+ ('Appletree', int4range(2, 12, '[]')); -- Using the constructor function with explicit bounds
-- Get all trees
SELECT * FROM trees;
@@ -86,10 +86,10 @@ species | height_range
Cedar | [15,40)
Oak | [30,40)
Maple | [35,40)
-Birch | [11,30) -- The lower bound has been canonicalized
+Birch | [11,30)
Sequoia | [100,)
Pine | [15,25)
-Appletree | [2,13) -- The upper bound has been canonicalized
+Appletree | [2,13)
```
## PostgreSQL Compatibility
@@ -100,3 +100,12 @@ In CedarDB, this is possible for all range types.
CedarDB restricts the precision and scale of [`numerics`](../numeric/) for performance reason.
As the `numeric` datatype is used for the bound values of `numranges`, the restrictions apply here as well.
In this case, CedarDB stores the bounds as `bignumeric(38,6)`.
+
+CedarDB supports the range operators `@>`, `<@`, `&&`, `<<`, and `>>`, and the functions `lower`, `upper`, `isempty`,
+`lower_inc`, `upper_inc`, `lower_inf`, and `upper_inf`.
+The following PostgreSQL range features are not supported:
+
+- The operators `&<`, `&>`, `-|-`, `+` (union), `*` (intersection), and `-` (difference).
+- The function `range_merge`.
+- Multirange types such as `int4multirange`.
+- User-defined range types (`CREATE TYPE ... AS RANGE`).
diff --git a/content/references/datatypes/text.md b/content/references/datatypes/text.md
index af624055..d75eefae 100644
--- a/content/references/datatypes/text.md
+++ b/content/references/datatypes/text.md
@@ -103,15 +103,15 @@ from strings;
```
```text
- ?column?
-----------
+ s
+-----
foo
(1 row)
```
```text
- ?column?
-----------
+ s
+-----
FOO
(1 row)
```
@@ -119,23 +119,27 @@ from strings;
You can achieve a deterministic result by rewriting the query to output a `min()` aggregate in `binary` collate.
```sql
+with strings(s) as (values ('foo'), ('FOO'))
select min(s collate "binary")
from strings
group by s collate "en_US_ci";
```
+```text
+ min
+-----
+ FOO
+(1 row)
+```
+
### Choose the Right Locale
-The expected ordering of diacritics can depend on the specified collate. French Candians, for example, seem to have a specific preference about the lexicographical order of diacritics:
+The expected ordering of diacritics can depend on the specified collate. French Canadians, for example, seem to have a specific preference about the lexicographical order of diacritics:
```sql
-with strings(s) as (
- values ('cote'),
- ('coté'),
- ('côte'),
- ('côté')
-)
-select s from strings order by s;
+create table words (s text);
+insert into words values ('cote'), ('coté'), ('côte'), ('côté');
+select s from words order by s;
```
```text
@@ -148,8 +152,12 @@ select s from strings order by s;
(4 rows)
```
+To sort by a locale, declare the collation on the column:
+
```sql
-select s from strings order by s collate "fr_CA";
+create table words_fr (s text collate "fr_CA");
+insert into words_fr values ('cote'), ('coté'), ('côte'), ('côté');
+select s from words_fr order by s;
```
```text
@@ -161,3 +169,7 @@ select s from strings order by s collate "fr_CA";
côté
(4 rows)
```
+
+## PostgreSQL Differences
+
+- Inserting a string into a `varchar(n)` column fails if it is longer than `n`, even if the excess characters are spaces. PostgreSQL silently removes the excess trailing spaces. An explicit cast such as `'abc '::varchar(3)` truncates in both systems.
diff --git a/content/references/datatypes/time.md b/content/references/datatypes/time.md
index ebb4edbe..b4ccd052 100644
--- a/content/references/datatypes/time.md
+++ b/content/references/datatypes/time.md
@@ -42,3 +42,8 @@ select time '11:00 pm' + interval '8' hour;
07:00:00
(1 row)
```
+
+## PostgreSQL Differences
+
+- `time with time zone` (`timetz`) is not supported. `current_time` returns a `time` value without time zone.
+- CedarDB accepts a fractional-second precision such as `time(0)` but ignores it: values always keep microsecond resolution.
diff --git a/content/references/datatypes/timestamp.md b/content/references/datatypes/timestamp.md
index 9f236934..bb278388 100644
--- a/content/references/datatypes/timestamp.md
+++ b/content/references/datatypes/timestamp.md
@@ -50,3 +50,9 @@ set timezone to 'UTC';
```
When the time of day is *not* relevant for your use case, e.g., when dealing with birthdates for age verification, consider using the [date](../date) type instead.
+
+## PostgreSQL Differences
+
+- The special values `infinity` and `-infinity` are not supported.
+- CedarDB accepts a fractional-second precision such as `timestamp(0)` or `timestamp(3) with time zone` but ignores it: values always keep microsecond resolution.
+- The shorthand `timestamptz(p)` is not supported. Use `timestamp(p) with time zone` instead.
diff --git a/content/references/dml/copy.md b/content/references/dml/copy.md
index 8e467408..e571a06e 100644
--- a/content/references/dml/copy.md
+++ b/content/references/dml/copy.md
@@ -20,6 +20,7 @@ Copy supports a number of options controlling the format.
* `Format`:
`csv`, `text`, `binary`. Only for copy to: `numpy`.
+ `binary` uses PostgreSQL's binary `COPY` format, which starts with the `PGCOPY` signature, and works with `STDIN` and `STDOUT`.
* `Delimiter`:
The record separator for `text` and `csv` formats.
By default `\t`, and `,`.
@@ -64,3 +65,20 @@ WITH (
ON_ERROR ignore
);
```
+
+## Permissions
+
+`COPY FROM` requires the `INSERT` privilege on the table, and `COPY TO` requires the `SELECT` privilege on the table or the privileges to run the query.
+Both also require `USAGE` on the schema of the table.
+Reading or writing files on the server additionally requires superuser, or membership in `pg_read_server_files` (for `COPY FROM`) or `pg_write_server_files` (for `COPY TO`).
+Otherwise, CedarDB rejects the statement with `file access denied`.
+`COPY ... FROM STDIN` and `COPY ... TO STDOUT`, e.g., via psql's `\copy`, do not need file access.
+
+## PostgreSQL Differences
+
+* `FORMAT binary` is not supported for `COPY TO` a file. `COPY ... TO STDOUT` and `COPY ... FROM STDIN` support it.
+* `COPY ... WHERE`, `FORCE_QUOTE`, the `DEFAULT` option, and `COPY FROM PROGRAM` are not supported.
+ `COPY TO PROGRAM` only prints a warning and does not run the program.
+* `ON_ERROR ignore` skips rows that fail to parse, instead of stopping.
+* `HEADER` has no effect for `COPY TO` in `text` format. `HEADER match` does not skip or check the header line.
+* `COPY TO` and `COPY FROM` accept relative file paths and resolve them against the server's working directory. PostgreSQL rejects relative paths for `COPY TO`.
diff --git a/content/references/dml/delete.md b/content/references/dml/delete.md
index 4ced6197..46243387 100644
--- a/content/references/dml/delete.md
+++ b/content/references/dml/delete.md
@@ -46,3 +46,15 @@ user_id | ?column?
--------+----------
42 | f
```
+
+## Permissions
+
+To delete from a table, you need the `DELETE` privilege on it, and `USAGE` on its schema.
+If the statement reads columns of the table, in `WHERE` or `RETURNING`, you also need the `SELECT` privilege.
+Tables in `USING` require the `SELECT` privilege.
+
+## PostgreSQL Differences
+
+- `RETURNING` requires the `SELECT` privilege on the table, even if it does not reference a column, such as `RETURNING 1`.
+ PostgreSQL only requires `SELECT` on the columns that `RETURNING` references.
+- `WHERE CURRENT OF` is not supported.
diff --git a/content/references/dml/insert.md b/content/references/dml/insert.md
index 767bc0b8..5a65b41e 100644
--- a/content/references/dml/insert.md
+++ b/content/references/dml/insert.md
@@ -32,6 +32,26 @@ While these names can be omitted, we recommend always explicitly specifying the
Without a column list, swapping similarly typed columns (e.g., year and length) can lead to subtle bugs.
In addition, omitted columns will be filled with default generated values.
+## Default Values
+
+Omitted columns get their default value, or null if they have none.
+Use the keyword `DEFAULT` to explicitly request the default value for a column, or `DEFAULT VALUES` to insert a row that consists only of defaults:
+
+```sql
+CREATE TABLE plantings (id int GENERATED ALWAYS AS IDENTITY, species text DEFAULT 'unknown', planted date DEFAULT current_date);
+
+INSERT INTO plantings (species) VALUES ('Oak');
+INSERT INTO plantings (species, planted) VALUES (DEFAULT, '2024-04-01');
+INSERT INTO plantings DEFAULT VALUES;
+```
+
+For an identity column with `GENERATED ALWAYS`, inserting an explicit value fails.
+Use `OVERRIDING SYSTEM VALUE` to insert the given value anyway, or `OVERRIDING USER VALUE` to ignore the given value and use the generated one:
+
+```sql
+INSERT INTO plantings (id, species) OVERRIDING SYSTEM VALUE VALUES (1000, 'Ash');
+```
+
## On Conflict
Inserting duplicate values into a table might cause conflicts when the table has a primary key or unique constraint
@@ -70,3 +90,21 @@ returning (
where product_id = p.id
);
```
+
+## Permissions
+
+To insert into a table, you need the `INSERT` privilege on it, and `USAGE` on its schema.
+Inserting into a `serial` or identity column also requires `USAGE` on the column's [sequence](/docs/references/objects/sequences#sequence-privileges).
+`INSERT ... SELECT` requires the privileges to run the query.
+`ON CONFLICT DO NOTHING` without a conflict target needs no further privilege.
+A conflict target, such as `ON CONFLICT (id)` or `ON CONFLICT ON CONSTRAINT plants_pkey`, requires the `SELECT` privilege, with both `DO NOTHING` and `DO UPDATE`.
+`ON CONFLICT DO UPDATE` also requires the `UPDATE` privilege, and the `SELECT` privilege if it reads columns of the table.
+`RETURNING` requires the `SELECT` privilege.
+
+## PostgreSQL Differences
+
+- `RETURNING` requires the `SELECT` privilege on the table, even if it does not reference a column, such as `RETURNING 1`.
+ PostgreSQL only requires `SELECT` on the columns that `RETURNING` references.
+- `ON CONFLICT ON CONSTRAINT ` requires the `SELECT` privilege on the table.
+ PostgreSQL requires only `INSERT` (and `UPDATE` for `DO UPDATE`) when no column is read.
+- A `WHERE` clause in the conflict target, `ON CONFLICT () WHERE `, is not supported, because CedarDB has no partial indexes.
diff --git a/content/references/dml/truncate.md b/content/references/dml/truncate.md
index fd76bb58..108dd805 100644
--- a/content/references/dml/truncate.md
+++ b/content/references/dml/truncate.md
@@ -13,7 +13,26 @@ truncate movies, stars, starsIn;
This removes all rows from the specified tables, similar to an unqualified
[delete](../delete).
+`TRUNCATE` is transactional: a `ROLLBACK` restores the rows.
+
+```text
+TRUNCATE [ TABLE ] [ ONLY ] [, ...] [ RESTART IDENTITY | CONTINUE IDENTITY ] [ CASCADE | RESTRICT ]
+```
+
+You cannot truncate a table that a foreign key of another table references, even if the referencing table is empty or listed in the same statement, and also not with `CASCADE`.
+Use `DELETE` for such tables.
{{< callout type="warning" >}}
Truncate is a **destructive** operation and will cause all data in the specified tables to be lost.
{{< /callout >}}
+
+## Permissions
+
+To truncate a table, you need the `TRUNCATE` privilege on it.
+
+## PostgreSQL Differences
+
+- `RESTART IDENTITY` is accepted, but identity columns and sequences continue with their current value.
+ Reset them with [`ALTER SEQUENCE
__seq RESTART`](/docs/references/objects/sequences#alter-sequence).
+- Tables referenced by a foreign key cannot be truncated, even with `CASCADE` or when all referencing tables are truncated in the same statement.
+- Truncate is transactional in CedarDB and can be rolled back.
diff --git a/content/references/dml/update.md b/content/references/dml/update.md
index 8a6fc708..9c882b2d 100644
--- a/content/references/dml/update.md
+++ b/content/references/dml/update.md
@@ -33,6 +33,25 @@ set gross_opening_week = (
);
```
+Take values from other tables with `FROM`:
+
+```sql
+CREATE TABLE trees (id int, height_m numeric);
+CREATE TABLE measurements (tree_id int, height_m numeric);
+
+UPDATE trees
+SET height_m = m.height_m
+FROM measurements m
+WHERE trees.id = m.tree_id;
+```
+
+Assign several columns at once, or reset a column to its default value:
+
+```sql
+UPDATE trees SET (height_m, id) = (12.5, 7) WHERE id = 1;
+UPDATE trees SET height_m = DEFAULT WHERE id = 2;
+```
+
## Returning Updated Rows
Update statements can also report the changed values, which might be useful if you update rows with a sub-select:
@@ -59,3 +78,16 @@ CedarDB prohibits such *lost updates* and will abort the second transaction, res
{{< callout type="info" >}}
When updating values concurrently, your application needs to handle transaction aborts.
{{< /callout >}}
+
+## Permissions
+
+To update a table, you need the `UPDATE` privilege on it, and `USAGE` on its schema.
+If the statement reads columns of the table, for example in `WHERE`, in a `SET` expression, or in `RETURNING`, you also need the `SELECT` privilege.
+Subqueries and `FROM` tables require the privileges to read them.
+
+## PostgreSQL Differences
+
+- `RETURNING` requires the `SELECT` privilege on the table, even if it does not reference a column, such as `RETURNING 1`.
+ PostgreSQL only requires `SELECT` on the columns that `RETURNING` references.
+- Assigning a column list from a subquery, `SET (a, b) = (SELECT ...)`, is not supported.
+- `WHERE CURRENT OF` is not supported.
diff --git a/content/references/expressions/try.md b/content/references/expressions/try.md
index 91797c90..bacaf5f4 100644
--- a/content/references/expressions/try.md
+++ b/content/references/expressions/try.md
@@ -59,3 +59,16 @@ select try(1::numeric / str::int) from input;
(3 rows)
```
+
+`try` catches errors that the wrapped expression raises while processing a row, for example:
+
+* Invalid input in casts, such as `'oops'::int`, `'2024-13-45'::date`, or `'{bad'::jsonb`
+* Division by zero, including modulo (`%`)
+* Numeric overflow, such as `2147483647 + 1` or `100000::smallint`
+
+Errors from mathematical functions with invalid arguments, such as `ln(0)` or `sqrt(-1)` on column values, are not caught and still terminate the query.
+Errors that are not raised by the wrapped expression itself, such as a scalar subquery that returns more than one row, are not caught either.
+
+{{< callout type="warning" >}}
+An explicit cast to a length-limited type such as `varchar(3)` truncates longer values outside of `try`, but returns `null` inside `try`.
+{{< /callout >}}
diff --git a/content/references/functions/aggregation.md b/content/references/functions/aggregation.md
index abbc319c..a556c6ac 100644
--- a/content/references/functions/aggregation.md
+++ b/content/references/functions/aggregation.md
@@ -132,9 +132,6 @@ the (x, y) points.
`regr_sxx`
: Compute the *sum of squares* of the `x` variable , i.e., `sum(x^2) - sum(x)^2/count(*)`.
-`regr_sxy`
-: Compute the *sum of products* of `x` times `y`, i.e., `sum(x*y) - sum(x) * sum(y)/count(*)`.
-
`stddev(x)`
: Compute the standard deviation, alias for `stddev_samp`.
@@ -169,3 +166,8 @@ The continuous percentile interpolates between values if necessary.
: Compute the discrete `n`th [percentile](https://en.wikipedia.org/wiki/Percentile) with the given `n` as a fraction
between 0 and 1.
The discrete percentile returns the first value exceeding the percentile.
+
+## PostgreSQL Differences
+
+- `corr`, `regr_sxy`, `regr_syy`, `range_agg`, `range_intersect_agg`, `jsonb_agg`, `json_object_agg`, and `jsonb_object_agg` are not supported.
+- Hypothetical-set aggregates (`rank(...) WITHIN GROUP (...)`, `dense_rank`, `percent_rank`, `cume_dist`) and the array form of `percentile_cont` and `percentile_disc` are not supported.
diff --git a/content/references/functions/bitstring.md b/content/references/functions/bitstring.md
index b0dd6159..60511061 100644
--- a/content/references/functions/bitstring.md
+++ b/content/references/functions/bitstring.md
@@ -9,8 +9,6 @@ linkTitle: "Bit String Functions and Operators"
Bitstring functions ignore `null` values and return `null` when one input is `null`.
{{< /callout >}}
-See [PostgreSQL: Bit string functions and operators](https://www.postgresql.org/docs/current/functions-bitstring.html) for the PostgreSQL documentation.
-
## General-purpose functions
### `bit & bit`
@@ -39,3 +37,18 @@ Example:
```sql
SELECT B'10011' # B'10101'; -> B'00110'
```
+
+### `bit || bit`
+
+Concatenation.
+Example:
+
+```sql
+SELECT B'10001' || B'011'; -> B'10001011'
+```
+
+In addition, `length`, `bit_length`, `octet_length`, `bit_count`, `position`, `substring`, `overlay`, `get_bit`, and `set_bit` work on bit strings, and integers can be cast to and from `bit(n)`.
+
+## PostgreSQL Differences
+
+- The bitwise NOT operator `~` and the shift operators `<<` and `>>` are not supported for bit strings.
diff --git a/content/references/functions/json.md b/content/references/functions/json.md
index c4b3af2a..ea3ef65e 100644
--- a/content/references/functions/json.md
+++ b/content/references/functions/json.md
@@ -74,6 +74,30 @@ select data->>'name' from json_data;
(4 rows)
```
+## Path Access
+
+The `#>` operator follows a path of object keys and array indexes, given as a `text[]`, and returns the `json` element at that path.
+The `#>>` operator does the same, but returns `text`.
+When the path does not exist, both return `null`.
+Negative array indexes in a path count from the end of the array.
+
+```sql
+select data->>'name' as name,
+ data #> '{friends,0}' as first_friend,
+ data #>> '{friends,-1}' as last_friend
+from json_data;
+```
+
+```text
+ name | first_friend | last_friend
+-----------+--------------+-------------
+ philipp | 2 | 3
+ max | 1 | 1
+ moritz | 1 | 4
+ christian | 3 | 3
+(4 rows)
+```
+
## Conversions
`Json` and `jsonb` columns can be converted to and from `text` using standard conversion functions.
@@ -197,14 +221,14 @@ where jsonb_exists_all(data, ARRAY['nick', 'name']);
(1 rows)
```
-For the full semantics, refer to the PostgreSQL documentation: [PostgreSQL JSONB containment and existence](https://www.postgresql.org/docs/17/datatype-json.html#JSON-CONTAINMENT)
+Containment is structural: an object contains another object if it has all of its keys with contained values, and an array contains another array if every element of the second array appears somewhere in the first one, regardless of order or duplicates.
## Concatenation
The `jsonb_concat` operation concatenates two jsonb documents. To use it, call the `jsonb_concat` function or by providing `jsonb` as input to the `||` operator.
```sql
-select data || '{"country": "Germany"}' from jsonb_data.
+select data::jsonb || '{"country": "Germany"}' from json_data;
```
```text
@@ -230,3 +254,55 @@ select (data->'friends') || (data->>'id')::jsonb as me_and_my_friends from json_
[3, 4]
(4 rows)
```
+
+## Construction
+
+Build JSON values from SQL values:
+
+| Function | Description | Example result |
+|--------------------------------------------------------|-------------------------------------------------------------|--------------------------|
+| `json_build_object(k1, v1, ...)`, `jsonb_build_object` | Object from alternating keys and values. | `{"a" : 1}` |
+| `json_build_array(v1, ...)`, `jsonb_build_array` | Array from the arguments. | `[1, "a"]` |
+| `to_json(value)` | JSON representation of any SQL value. | `"x"` |
+| `row_to_json(record)` | Object with one key per column of a row. | `{"f1" : 1, "f2" : "a"}` |
+| `array_to_json(array)` | JSON array from an SQL array. | `[1,2]` |
+| `json_agg(value)` | Aggregate: JSON array of all input values, including nulls. | `[1, null, 3]` |
+| `json_arrayagg(value)` | Aggregate: JSON array of all non-null input values. | `[1, 3]` |
+
+```sql
+CREATE TABLE trees (id int, species text, height_m numeric);
+INSERT INTO trees VALUES (1, 'Oak', 21.5), (2, 'Birch', 9.0);
+
+SELECT json_agg(json_build_object('species', species, 'height', height_m)) FROM trees;
+```
+
+```text
+ json_agg
+-----------------------------------------------------------------------------------------
+ [{"species" : "Oak", "height" : 21.500000}, {"species" : "Birch", "height" : 9.000000}]
+(1 row)
+```
+
+`json_agg` keeps null inputs as JSON `null`. `json_arrayagg` skips them by default; write `json_arrayagg(x ORDER BY x NULL ON NULL)` to keep them. Both aggregates accept an `ORDER BY` clause:
+
+```sql
+SELECT json_arrayagg(species ORDER BY species) FROM trees;
+```
+
+```text
+ ?column?
+------------------
+ ["Birch", "Oak"]
+(1 row)
+```
+
+`jsonb_array_length()` and `jsonb_array_elements()` work like their `json` counterparts.
+
+## PostgreSQL Differences
+
+- The deletion operators `-` and `#-` and the JSON path operators `@?` and `@@` are not supported.
+- `->` and `->>` with a negative array index return `null` instead of counting from the end of the array. Use `#>` or `#>>` with a negative index in the path, e.g., `data #> '{friends,-1}'`.
+- `json_arrayagg` names its result column `?column?` instead of `json_arrayagg`.
+- `jsonb` subscripting, e.g., `data['name']`, is not supported. Use `->` and `->>`.
+- Not supported: `to_jsonb`, `jsonb_agg`, `json_object_agg`, `jsonb_object_agg`, `json_object`, `jsonb_object`, `json_each` and `jsonb_each` (also `_text`), `json_object_keys`, `jsonb_object_keys`, `json_array_elements_text`, `jsonb_array_elements_text`, `json_typeof`, `jsonb_typeof`, `jsonb_set`, `jsonb_set_lax`, `jsonb_insert`, `json_strip_nulls`, `jsonb_strip_nulls`, `jsonb_pretty`, the `json_populate_record` and `json_to_record` families, and the `jsonb_path_*` functions.
+- Of the SQL/JSON constructors and functions, only `JSON_ARRAYAGG` is supported. `JSON_ARRAY`, `JSON_OBJECT`, `JSON_OBJECTAGG`, `IS JSON`, `JSON_QUERY`, `JSON_VALUE`, `JSON_EXISTS`, `JSON_SCALAR`, `JSON_SERIALIZE`, and `JSON_TABLE` are not supported.
diff --git a/content/references/functions/system.md b/content/references/functions/system.md
index f5082bca..281f5b82 100644
--- a/content/references/functions/system.md
+++ b/content/references/functions/system.md
@@ -5,9 +5,86 @@ linkTitle: "PostgreSQL Functions"
CedarDB supports a variety of PostgreSQL functions. This page currently only describes a prominent subset of those functions.
+## Session information
+
+| Function | Returns |
+|-----------------------------------------|--------------------------------------------------------------------|
+| `current_database()`, `current_catalog` | The name of the current database. |
+| `current_schema()` | The first existing schema of the search path. |
+| `current_schemas(include_implicit)` | The schemas of the search path, optionally including `pg_catalog`. |
+| `current_user`, `current_role`, `user` | The current role. |
+| `session_user` | The role you logged in as. |
+| `pg_backend_pid()` | The process ID of the current session. |
+| `pg_my_temp_schema()` | The session's temporary schema as `regnamespace`, or `-` if none. |
+| `pg_postmaster_start_time()` | The time when the server started. |
+| `pg_is_in_recovery()` | Whether the server is a replica in recovery mode. |
+| `inet_client_addr()` | The client's IP address, or null for Unix socket connections. |
+| `txid_current()` | The ID of the current transaction. |
+| `version()` | The CedarDB version string. |
+
+```sql
+SELECT current_database(), current_user, version();
+```
+
+## Object size functions
+
+| Function | Returns |
+|------------------------------------|----------------------------------------------------------|
+| `pg_table_size(regclass)` | Size of a table in bytes, without indexes. |
+| `pg_indexes_size(regclass)` | Size of all indexes of a table in bytes. |
+| `pg_total_relation_size(regclass)` | Size of a table including its indexes in bytes. |
+| `pg_relation_size(regclass)` | Size of a table in bytes. |
+| `pg_database_size(name)` | Size of a database in bytes. |
+| `pg_size_pretty(bigint)` | A size in bytes, formatted with a unit, e.g., `3584 kB`. |
+
+```sql
+CREATE TABLE trees (id int, species text);
+INSERT INTO trees SELECT g, 'Oak' FROM generate_series(1, 100000) g;
+
+SELECT pg_size_pretty(pg_total_relation_size('trees'));
+```
+
+```text
+ pg_size_pretty
+----------------
+ 3584 kB
+(1 row)
+```
+
+Any role can call the size functions, also for tables it cannot read.
+
+## Privilege and visibility functions
+
+`has_table_privilege`, `has_schema_privilege`, `has_database_privilege`, `has_column_privilege`, `has_any_column_privilege`, `has_sequence_privilege`, `has_function_privilege`, `has_type_privilege`, and `pg_has_role` check privileges, e.g., `has_table_privilege('trees', 'SELECT')` for the current role or `has_table_privilege('forester', 'trees', 'SELECT')` for another role; see [Roles](/docs/references/objects/roles#inspecting-privileges).
+Any role can call these functions, also to check the privileges of other roles. Privileges granted through membership in a role, including `pg_read_all_data` and `pg_write_all_data`, count. Because CedarDB does not support column privileges, `has_column_privilege` and `has_any_column_privilege` return the table-level result.
+
+`pg_table_is_visible`, `pg_function_is_visible`, and `pg_type_is_visible` check whether an object is visible in the search path.
+`row_security_active(table)` returns whether row level security applies to the current role; see [Policies](/docs/references/objects/policies).
+
+## Catalog information functions
+
+| Function | Returns |
+|---------------------------------------------------------------------------------------------|---------------------------------------------------------------|
+| `pg_typeof(any)` | The data type of a value. |
+| `format_type(type_oid, typemod)` | The SQL name of a data type. |
+| `pg_get_viewdef(view)` | The query of a view or materialized view. |
+| `pg_get_functiondef(function)` | The `CREATE FUNCTION` statement of a function. |
+| `pg_get_function_arguments(function)`, `pg_get_function_result(function)` | The argument list and result type of a function. |
+| `pg_get_indexdef(index)`, `pg_get_constraintdef(constraint)`, `pg_get_expr(expr, relation)` | Definitions of indexes, constraints, and default expressions. |
+| `pg_get_serial_sequence(table, column)` | The sequence of a `serial` or identity column. |
+| `pg_get_userbyid(role_oid)` | The name of a role. |
+| `to_regtype(text)` | The OID of a type, or null if it doesn't exist. |
+| `obj_description`, `col_description`, `shobj_description` | Always null, because CedarDB does not support comments. |
+
+## PostgreSQL Differences
+
+- `pg_reload_conf`, `pg_sleep`, `pg_blocking_pids`, `pg_conf_load_time`, `pg_current_logfile`, `pg_trigger_depth`, `pg_listening_channels`, `pg_notification_queue_usage`, `inet_client_port`, `inet_server_addr`, `inet_server_port`, `pg_current_xact_id`, `pg_column_size`, `pg_tablespace_size`, `pg_size_bytes`, `pg_describe_object`, and `pg_identify_object` are not supported.
+- `to_regclass`, `to_regproc`, `to_regnamespace`, and `to_regrole` are not supported. Use casts such as `'trees'::regclass` instead.
+- `pg_my_temp_schema()` returns `regnamespace` instead of `oid`. Cast it with `pg_my_temp_schema()::oid` to get the OID; without a temporary schema, the OID is 0.
+
## Advisory Locks
-Similar to [PostgreSQL](https://www.postgresql.org/docs/current/explicit-locking.html#ADVISORY-LOCKS), CedarDB advisory locks provide a means for creating _application-defined locks_.
+Like in PostgreSQL, CedarDB advisory locks provide a means for creating _application-defined locks_.
A common example is their use in database migration tools such as _Flyway_ or _Liquibase_.
When multiple instances of an application start simultaneously, they might all attempt to apply schema migrations at once.
diff --git a/content/references/functions/text.md b/content/references/functions/text.md
index 93b00a9a..cb985fb9 100644
--- a/content/references/functions/text.md
+++ b/content/references/functions/text.md
@@ -132,3 +132,9 @@ regexp_like
t
(1 row)
```
+
+## PostgreSQL Differences
+
+- `substring` does not accept regular expression patterns (`substring(text FROM pattern)`, `SIMILAR ... ESCAPE`). Use `regexp_substr` instead.
+- `regexp_replace` does not accept the `start` and `N` arguments.
+- `^@`, `unistr`, and `to_ascii` are not supported.
diff --git a/content/references/objects/_index.md b/content/references/objects/_index.md
index b99e815c..ca694d09 100644
--- a/content/references/objects/_index.md
+++ b/content/references/objects/_index.md
@@ -14,5 +14,7 @@ Each page covers all operations on that object type: creating, altering, and dro
* [Policies](./policies) — `CREATE`, `ALTER`, `DROP POLICY`, row level security
* [Roles](./roles) — `CREATE`, `ALTER`, `DROP ROLE`, `GRANT`, `REVOKE`
* [Schemas](./schemas) — `CREATE`, `ALTER`, `DROP SCHEMA`, `search_path`
+* [Sequences](./sequences) — `CREATE`, `ALTER`, `DROP SEQUENCE`, `nextval`, `setval`
* [Tables](./tables) — `CREATE`, `ALTER`, `DROP TABLE`, constraints, partitioning
+* [Types](./types) — `CREATE`, `ALTER`, `DROP TYPE` for user-defined enum types
* [Views](./views) — `CREATE`, `DROP VIEW`
diff --git a/content/references/objects/databases.md b/content/references/objects/databases.md
index 10e8f43f..9b1bbb3b 100644
--- a/content/references/objects/databases.md
+++ b/content/references/objects/databases.md
@@ -4,43 +4,188 @@ linkTitle: "Databases"
weight: 50
---
-Create database allows creating a new *logical* database.
-Multiple databases can be used for logical separation of data.
-When connection to CedarDB, you need to specify the database that you want to connect to.
-All queries that execute in this connection can only read and write data in this database.
-
-Usage example:
+A database is a named, logically separate collection of schemas, tables, and other objects on a CedarDB server.
+When you connect to CedarDB, you choose the database to connect to.
+All queries on that connection can only read and write objects in that database.
+Roles are shared by all databases on the server.
```sql
-create database newdb owner postgres;
+CREATE DATABASE forestry;
```
-Afterward, you can connect to the freshly created database:
+Afterward, connect to the new database:
```shell
-psql -h localhost -U postgres -d newdb
+psql -h localhost -U postgres -d forestry
+```
+
+## CREATE DATABASE
+
+`CREATE DATABASE` creates a new, empty database.
+
+```text
+CREATE DATABASE
+ [ [ WITH ] [ OWNER [=] ]
+ [ ENCODING [=] 'UTF8' ]
+ [ TEMPLATE [=] { template0 | template1 | DEFAULT } ] ]
```
-## Session defaults
+| Parameter | Description |
+|------------|-----------------------------------------------------------------------------------------------------------------|
+| `` | The name of the new database. |
+| `OWNER` | The role that owns the new database. Defaults to the role that runs the statement. |
+| `ENCODING` | The character encoding. CedarDB always stores text as UTF-8. |
+| `TEMPLATE` | Accepted for compatibility with `template0`, `template1`, and `DEFAULT` only. The new database is always empty. |
+
+Create a database that belongs to another role:
-`ALTER DATABASE ... SET` stores a default value for a [setting](/docs/references/sessions/settings).
-Every new session connecting to this database starts with this value:
+```sql
+CREATE ROLE forester LOGIN;
+CREATE DATABASE forestry_reports OWNER forester;
+```
```sql
-alter database newdb set statement_timeout = '30s';
+SELECT datname, datdba::regrole AS owner FROM pg_database WHERE datname = 'forestry_reports';
+```
+
+```text
+ datname | owner
+------------------+----------
+ forestry_reports | forester
+```
+
+### Permissions
+
+To create a database, you must be a superuser or have the `CREATEDB` attribute.
+The creator owns the new database, unless you specify `OWNER`.
+With `OWNER`, you must be able to `SET ROLE` to the new owner, i.e., be a member of that role with the `SET` option.
+
+## ALTER DATABASE
+
+`ALTER DATABASE` renames a database, changes its owner, or sets default values for configuration parameters.
+
+```text
+ALTER DATABASE RENAME TO
+ALTER DATABASE OWNER TO { | CURRENT_USER | CURRENT_ROLE | SESSION_USER }
+ALTER DATABASE SET { TO | = } { | DEFAULT }
+ALTER DATABASE RESET { | ALL }
```
-Note that this does not affect currently running sessions, for this use an explicit `SET`.
-[Role defaults](/docs/references/objects/roles#session-defaults) for the same setting, override this default.
+### Rename a database
-`RESET` removes defaults again:
+You cannot rename a database while any session, including your own, is connected to it:
```sql
-alter database newdb reset statement_timeout;
-alter database newdb reset all;
+CREATE DATABASE forestry;
+ALTER DATABASE forestry RENAME TO forestry_archive;
```
-## Permissions
+### Set a default for a [configuration parameter](/docs/references/sessions/settings)
+
+The default applies to every new session that connects to the database.
+It does not affect sessions that are already running. Use an explicit `SET` for those.
+[Role defaults](/docs/references/objects/roles#session-defaults) for the same parameter override the database default:
+
+```sql
+CREATE DATABASE forestry;
+ALTER DATABASE forestry SET search_path = 'inventory';
+```
+
+```sql
+SELECT setconfig FROM pg_db_role_setting;
+```
+
+```text
+ setconfig
+-------------------------
+ {search_path=inventory}
+```
+
+Remove the default again with `RESET`:
+
+```sql
+ALTER DATABASE forestry RESET search_path;
+ALTER DATABASE forestry RESET ALL;
+```
+
+### Permissions
+
+To alter a database, you must own it or be a superuser.
+In addition:
+
+* `RENAME TO` requires the `CREATEDB` attribute.
+* `OWNER TO` requires that you have the `CREATEDB` attribute and are a member of the new owner role with the `SET` option.
+
+Superusers can always rename a database and change its owner.
+
+## DROP DATABASE
+
+`DROP DATABASE` removes a database and all objects in it.
+This cannot be undone after the transaction executing it commited.
+
+```text
+DROP DATABASE [ IF EXISTS ]
+```
+
+```sql
+CREATE DATABASE forestry;
+DROP DATABASE forestry;
+```
+
+Use `IF EXISTS` to avoid an error if the database does not exist:
+
+```sql
+DROP DATABASE IF EXISTS forestry;
+```
+
+You cannot drop the database you are connected to, or a database that other sessions are still connected to.
+
+### Permissions
+
+To drop a database, you must own it or be a superuser.
+
+## Database privileges
+
+Database privileges control who can connect to a database and what they can create in it.
+
+| Privilege | Allows |
+|---------------------|--------------------------------------------------------------------------------|
+| `CONNECT` | Connecting to the database. |
+| `CREATE` | Creating schemas in the database. |
+| `TEMPORARY`, `TEMP` | Creating temporary tables and other temporary objects in the database. |
+| `ALL [PRIVILEGES]` | All of the above. |
+
+By default, every role can connect to a database and create temporary objects in it, because `PUBLIC` holds `CONNECT` and `TEMPORARY`.
+Only the owner can create schemas in it.
+
+{{< callout type="info" >}}
+`GRANT` and `REVOKE` require an enterprise license.
+{{< /callout >}}
+
+Restrict a database to specific roles by revoking `CONNECT` from `PUBLIC` and granting it again to individual roles:
+
+```sql
+CREATE DATABASE forestry;
+CREATE ROLE forester LOGIN;
+
+REVOKE CONNECT ON DATABASE forestry FROM PUBLIC;
+GRANT CONNECT ON DATABASE forestry TO forester;
+```
+
+Other roles now fail to connect with `FATAL: user not permitted to connect to database`.
+
+Without an enterprise license, CedarDB still enforces privileges that were granted earlier.
+
+For the full `GRANT` and `REVOKE` syntax, see [Roles](/docs/references/objects/roles).
+
+## PostgreSQL Differences
-To create a database, you need to have superuser or `createdb` permissions.
-To change the session defaults of a database, you need to be its owner or a superuser.
+* CedarDB does not support the `LOCALE`, `LC_COLLATE`, `LC_CTYPE`, `LOCALE_PROVIDER`, `ICU_LOCALE`, `COLLATION_VERSION`, `STRATEGY`, `OID`, `TABLESPACE`, `CONNECTION LIMIT`, `IS_TEMPLATE`, and `ALLOW_CONNECTIONS` options of `CREATE DATABASE`.
+ Use the [`COLLATE`](/docs/references/datatypes/text/#unicode-collation-support) specifier for per-column collations.
+* `TEMPLATE` only accepts `template0`, `template1`, and `DEFAULT`. CedarDB has no template databases, and new databases are always empty. Other template names fail with `database templates not supported`.
+* `ENCODING` rejects encodings other than UTF-8, such as `LATIN1`.
+* `ALTER DATABASE` does not support `SET TABLESPACE`, `SET FROM CURRENT`, `REFRESH COLLATION VERSION`, or the `WITH` options `CONNECTION LIMIT`, `ALLOW_CONNECTIONS`, and `IS_TEMPLATE`.
+* Per-role defaults for a single database (`ALTER ROLE ... IN DATABASE ... SET`) are not supported.
+* `DROP DATABASE` does not support `WITH (FORCE)`.
+* `CREATE DATABASE` and `DROP DATABASE` are allowed inside a transaction block. A `ROLLBACK` undoes them.
+* `COMMENT ON DATABASE` is not supported.
diff --git a/content/references/objects/functions.md b/content/references/objects/functions.md
index 7b881bd7..ae5101ae 100644
--- a/content/references/objects/functions.md
+++ b/content/references/objects/functions.md
@@ -4,36 +4,18 @@ linkTitle: "Functions"
weight: 70
---
-Create function allows creating user-defined functions.
-Functions can help you to encapsulate common logic that can be reused.
-
-The body of a SQL function is a `select` statement of the form `select `, matching PostgreSQL's syntax for SQL-language functions.
-SQL functions must return the result of one scalar expression and cannot reference tables or subqueries.
-
-```sql
-create function times_two(x int) returns int language sql as
- 'select x * 2';
-
-create function cowsay(t text) returns text language sql as $$
- select ' ' || repeat('_', length(t) + 2) || E'\n' ||
- '< ' || t || E' >\n' ||
- ' ' || repeat('-', length(t) + 2) || E'\n' ||
- E' \\ ^__^\n' ||
- E' \\ (oo)\\_______\n' ||
- E' (__)\\ )\\/\\\n' ||
- E' ||----w |\n' ||
- E' || ||\n'
-$$;
-```
-
-Now you can use these functions in regular SQL queries:
+User-defined functions encapsulate logic that you can reuse in queries.
+Procedures are similar, but you invoke them with `CALL` instead of using them in expressions.
```sql
-select times_two(x::int) from generate_series(1, 3) s(x);
+CREATE FUNCTION times_two(x int) RETURNS int LANGUAGE sql AS
+ 'SELECT x * 2';
+
+SELECT times_two(x::int) FROM generate_series(1, 3) s(x);
```
```text
- times_two
+ times_two
-----------
2
4
@@ -41,21 +23,247 @@ select times_two(x::int) from generate_series(1, 3) s(x);
(3 rows)
```
+## CREATE FUNCTION
+
+```text
+CREATE [ OR REPLACE ] FUNCTION ( [ [ IN | INOUT ] [ DEFAULT ] [, ...] ] )
+ { RETURNS | RETURNS TABLE ( [, ...] ) }
+ LANGUAGE { sql | cedarscript }
+ [ IMMUTABLE | STABLE | VOLATILE ]
+ [ PARALLEL { SAFE | RESTRICTED | UNSAFE } ]
+ [ SECURITY { INVOKER | DEFINER } ]
+ [ COST ]
+ [ ROWS ]
+ AS ''
+```
+
+| Option | Description |
+|-----------------------------------|--------------------------------------------------------------------------|
+| `OR REPLACE` | Replace an existing function with the same name and argument types. |
+| `` | Arguments must be named. Reference them by name in the body. |
+| `DEFAULT ` | A default value. Callers can omit arguments with defaults. |
+| `RETURNS ` | A scalar function that returns one value. |
+| `RETURNS TABLE (...)` | A table function that returns a set of rows. |
+| `LANGUAGE` | `sql` or `cedarscript`. |
+| `IMMUTABLE`, `STABLE`, `VOLATILE` | Volatility of the function. The default is `VOLATILE`. |
+| `SECURITY DEFINER` | Run the function with the privileges of its owner instead of the caller. |
+| `PARALLEL`, `COST`, `ROWS` | Accepted and stored in `pg_proc`. |
+
+Use dollar quoting for bodies that contain quotes:
+
+```sql
+CREATE FUNCTION growth_label(height_m numeric) RETURNS text LANGUAGE sql AS $$
+ SELECT CASE WHEN height_m > 20 THEN 'tall' ELSE 'small' END
+$$;
+
+SELECT growth_label(25.0);
+```
+
+```text
+ growth_label
+--------------
+ tall
+```
+
+Functions can be overloaded: several functions can share a name if their argument types differ.
+CedarDB picks the overload that matches the argument types:
+
+```sql
+CREATE FUNCTION leaf_kind(v int) RETURNS text LANGUAGE sql AS 'SELECT ''whole number''';
+CREATE FUNCTION leaf_kind(v text) RETURNS text LANGUAGE sql AS 'SELECT ''text''';
+
+SELECT leaf_kind(7), leaf_kind('fern');
+```
+
+```text
+ leaf_kind | leaf_kind
+--------------+-----------
+ whole number | text
+```
+
+Cast string literals for parameters of other types, for example `'2024-05-01'::date` or `'{"leaf": 3}'::jsonb`.
+
+After creating a function, you can find it in `pg_proc`, `information_schema.routines`, and `information_schema.parameters`.
+`pg_get_functiondef()` returns the full `CREATE FUNCTION` statement.
+
+### Scalar SQL functions
+
+The body of a scalar SQL function is a single `SELECT `.
+The expression can use the arguments and call other functions, but it cannot read from tables or contain subqueries.
+A function cannot call itself.
+
+### Default arguments and named notation
+
+```sql
+CREATE FUNCTION crown_area(radius_m numeric, pi_approx numeric DEFAULT 3.14159)
+ RETURNS numeric LANGUAGE sql AS 'SELECT pi_approx * radius_m * radius_m';
+
+SELECT crown_area(2), crown_area(2, 3), crown_area(pi_approx => 3, radius_m => 2);
+```
+
+### Table functions
+
+A function with `RETURNS TABLE` returns a set of rows.
+Its body is a single `SELECT` query, which can read from tables.
+The output columns of the query must have the same names as the columns in `RETURNS TABLE`:
+
+```sql
+CREATE TABLE trees (id int, species text, height_m numeric);
+INSERT INTO trees VALUES (1, 'Oak', 21.5), (2, 'Birch', 9.0);
+
+CREATE FUNCTION trees_taller_than(min_height numeric)
+ RETURNS TABLE (id int, species text)
+ LANGUAGE sql AS
+ 'SELECT id, species FROM trees WHERE height_m > min_height';
+
+SELECT * FROM trees_taller_than(10);
+```
+
+```text
+ id | species
+----+---------
+ 1 | Oak
+```
+
+### CedarScript functions
+
+In addition to SQL, CedarDB supports functions in its own language `cedarscript`:
+
+```sql
+CREATE FUNCTION times_four(x int) RETURNS int LANGUAGE cedarscript AS $$
+ return x * 4;
+$$;
+```
+
+### Permissions
+
+To create a function, you need the `CREATE` privilege on the schema.
+The creator owns the function.
+
+## CREATE PROCEDURE and CALL
+
+```text
+CREATE [ OR REPLACE ] PROCEDURE ( [ [, ...] ] )
+ LANGUAGE { sql | cedarscript }
+ [ SECURITY { INVOKER | DEFINER } ]
+ AS ''
+
+CALL ( [ [, ...] ] )
+```
+
```sql
-select cowsay('Hello CedarDB!');
+CREATE PROCEDURE check_tree_count(expected int) LANGUAGE sql AS 'SELECT expected > 0';
+CALL check_tree_count(3);
```
+Calling a procedure with `SELECT`, or a function with `CALL`, fails with ` is a procedure` or ` is not a procedure`.
+
+The body of a SQL procedure is a single `SELECT `, like a scalar SQL function.
+It cannot modify data.
+
+### Permissions
+
+To call a procedure, you need the `EXECUTE` privilege on it.
+
+## ALTER FUNCTION
+
```text
- cowsay
-------------------------------
- ________________ +
- < Hello CedarDB! > +
- ---------------- +
- \ ^__^ +
- \ (oo)\_______ +
- (__)\ )\/\+
- ||----w | +
- || || +
-
-(1 row)
+ALTER { FUNCTION | PROCEDURE | ROUTINE } [ ( [, ...] ) ]
+ { RENAME TO
+ | SET SCHEMA
+ | OWNER TO
+ | { IMMUTABLE | STABLE | VOLATILE }
+ | SECURITY { INVOKER | DEFINER }
+ | PARALLEL { SAFE | RESTRICTED | UNSAFE }
+ | COST
+ | ROWS }
+```
+
+The argument types identify the function if its name is overloaded:
+
+```sql
+CREATE FUNCTION times_two(x int) RETURNS int LANGUAGE sql AS 'SELECT x * 2';
+ALTER FUNCTION times_two(int) RENAME TO double_it;
+```
+
+You cannot rename a function or move it to another schema while a view uses it:
+`cannot alter function "" because other objects depend on it`.
+Other `ALTER FUNCTION` forms work on such functions.
+
+### Permissions
+
+To alter a function, you must own it or be a superuser.
+`RENAME TO` and `SET SCHEMA` require the `CREATE` privilege on the target schema.
+`OWNER TO` requires that you can `SET ROLE` to the new owner, and that the new owner has the `CREATE` privilege on the schema.
+
+## DROP FUNCTION
+
+```text
+DROP { FUNCTION | PROCEDURE | ROUTINE } [ IF EXISTS ] [ ( [, ...] ) ] [, ...] [ CASCADE | RESTRICT ]
+```
+
+```sql
+CREATE FUNCTION times_two(x int) RETURNS int LANGUAGE sql AS 'SELECT x * 2';
+DROP FUNCTION times_two(int);
+```
+
+You cannot drop a function while a view uses it. Use `CASCADE` to drop such views as well.
+
+### Permissions
+
+To drop a function, you must own it, own its schema, or be a superuser.
+
+## Function privileges
+
+The `EXECUTE` privilege allows calling a function or procedure.
+By default, `PUBLIC` has `EXECUTE` on every function and procedure.
+
+{{< callout type="info" >}}
+Granting and revoking privileges on functions requires an enterprise license.
+{{< /callout >}}
+
+Restrict a function to specific roles:
+
+```sql
+CREATE FUNCTION times_two(x int) RETURNS int LANGUAGE sql AS 'SELECT x * 2';
+CREATE ROLE analyst LOGIN;
+
+REVOKE EXECUTE ON FUNCTION times_two(int) FROM PUBLIC;
+GRANT EXECUTE ON FUNCTION times_two(int) TO analyst;
```
+
+Other roles now fail with `permission denied for function 'times_two'`.
+
+Use `ON FUNCTION` for functions, `ON PROCEDURE` for procedures, and `ON ROUTINE` for either.
+`ON ALL FUNCTIONS IN SCHEMA`, `ON ALL PROCEDURES IN SCHEMA`, and `ON ALL ROUTINES IN SCHEMA` affect all existing functions, procedures, or both in a schema.
+
+### SECURITY DEFINER
+
+A function with `SECURITY DEFINER` runs with the privileges of its owner.
+This lets you give roles controlled access to data they cannot read directly:
+
+```sql
+CREATE TABLE ledger (id int, amount int);
+CREATE ROLE clerk LOGIN;
+
+CREATE FUNCTION ledger_rows() RETURNS TABLE (id int, amount int)
+ LANGUAGE sql SECURITY DEFINER AS 'SELECT id, amount FROM ledger';
+
+-- clerk can call ledger_rows(), but cannot SELECT from ledger
+```
+
+The owner of the function needs the `SELECT` privilege on `ledger`.
+Because scalar SQL functions cannot read tables, use a table function (`RETURNS TABLE`) for this.
+Without `SECURITY DEFINER`, the function runs with the privileges of the caller, and `clerk` gets `permission denied for table 'ledger'`.
+
+## PostgreSQL Differences
+
+- PL/pgSQL (`LANGUAGE plpgsql`) and `DO` blocks are not supported.
+- Arguments must be named; positional references (`$1`) are not supported. `VARIADIC` and `OUT` arguments are not supported. `INOUT` arguments are accepted, but the function returns NULL, and `CALL` of a procedure returns no row.
+- `RETURNS SETOF`, `RETURNS void`, SQL-standard bodies (`RETURN ...`, `BEGIN ATOMIC`), the `SET` clause, `LEAKPROOF`, `WINDOW`, and `SUPPORT` are not supported.
+- Scalar SQL functions cannot read from tables or contain subqueries, and cannot call themselves recursively. SQL functions and procedures contain exactly one `SELECT` statement.
+- `STRICT` and `RETURNS NULL ON NULL INPUT` are accepted but not enforced: the function is still called with null arguments.
+- `CREATE OR REPLACE FUNCTION` can change the return type, rename parameters, remove defaults, and replace a procedure with a function.
+- Calling a function with an untyped `NULL` literal fails with `unknown function or overload`. Cast the literal, e.g., `NULL::int`. String literals are only matched to `text` parameters. Cast them for other types, e.g., `'2024-05-01'::date`.
+- Renaming a function, or moving it to another schema, fails while a view uses it.
+- `COMMENT ON FUNCTION` is not supported.
diff --git a/content/references/objects/indexes.md b/content/references/objects/indexes.md
index 64c0d5a3..ab3d4dd7 100644
--- a/content/references/objects/indexes.md
+++ b/content/references/objects/indexes.md
@@ -5,63 +5,200 @@ weight: 20
---
Indexes speed up finding indexed values in a table.
-This is usually very beneficial, since using an index can avoid a full table scan, which improves the asymptotic runtime
-from O(n) to O(log(n)).
-Building and maintaining indexes, however, is not free; the index occupies some space in storage, and write operations
-on indexed tables are a bit slower.
+An index lookup avoids a full table scan, which improves the runtime of point lookups and selective queries.
+Building and maintaining indexes, however, is not free: the index occupies storage, and write operations on indexed tables are a bit slower.
-Usage example:
+```sql
+CREATE TABLE sales (id int, customer_id int, article_id int, amount numeric);
+CREATE INDEX ON sales (customer_id, article_id);
+```
+
+This indexes the `sales` table on customer and article, which helps to quickly find the orders of a customer.
+CedarDB automatically chooses the best index to execute a query.
+Use [`EXPLAIN`](/docs/references/utility/explain) to check whether a query uses an index:
```sql
-create index on sales(customer_id, article_id);
+INSERT INTO sales SELECT i, i % 1000, i % 37, i FROM generate_series(1, 100000) i;
+EXPLAIN SELECT * FROM sales WHERE customer_id = 42;
```
-This indexes the sales table on customer and articles, which might be beneficial to quickly find the corresponding order
-for a customer complaint.
-CedarDB automatically chooses the best index to execute any query.
-We recommend inspecting the query plan if your queries are actually using the index.
+```text
+ plan
+------------------------------------------------------------------------------
+ 🖩 OUTPUT (Estimate: 67) +
+ ⌕ INDEXSCAN using sales_customer_id_article_id on table sales (Estimate: 67)
+(1 row)
+```
After creating an index, you can find it in the `pg_indexes` system view.
-## Index Options
+## CREATE INDEX
+
+```text
+CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] ]
+ ON [ ONLY ] [ USING btree ]
+ ( [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] )
+```
+
+| Parameter | Description |
+|-----------------------------|------------------------------------------------------------------------------------------------------------------|
+| `UNIQUE` | Reject duplicate values. CedarDB checks the existing rows when you create the index. |
+| `CONCURRENTLY` | Accepted for compatibility. CedarDB builds the index non-concurrently and prints a warning. |
+| `IF NOT EXISTS` | Do not throw an error if an index with the same name already exists. |
+| `` | The name of the index. Generated from the table and column names if omitted. |
+| `USING` | The index method. CedarDB supports B-tree indexes. `hash` is accepted and creates a B-tree index with a warning. |
+| `ASC`, `DESC` | The sort order of the column in the index. The default is `ASC`. |
+| `NULLS FIRST`, `NULLS LAST` | Where null values sort in the index. |
+
+Name an index explicitly:
+
+```sql
+CREATE TABLE sales (id int, customer_id int, article_id int, amount numeric);
+CREATE INDEX complaints_index ON sales (customer_id);
+```
+
+If you omit the name, CedarDB joins the table name and the column names with underscores.
+If that name is taken, CedarDB appends a number:
+
+```sql
+CREATE TABLE species (id int, family text, genus text);
+CREATE INDEX ON species (family, genus);
+CREATE INDEX ON species (family, genus);
+SELECT indexname FROM pg_indexes WHERE tablename = 'species';
+```
+
+```text
+ indexname
+-----------------------
+ species_family_genus
+ species_family_genus1
+```
+
+The index is created in the schema of its table, so you cannot schema-qualify the index name.
+
+### B-tree lookups
+
+All indexes are B-tree indexes, so they support equality, range, and prefix lookups.
+The example index on `sales (customer_id, article_id)` can be used for predicates like:
+
+```sql
+... WHERE customer_id = 42;
+... WHERE customer_id BETWEEN 5 AND 10;
+... WHERE customer_id = 42 AND article_id > 100;
+```
+
+### Unique indexes
+
+A unique index rejects rows with duplicate values in the indexed columns:
+
+```sql
+CREATE TABLE plants (id int, code text);
+CREATE UNIQUE INDEX plants_code_idx ON plants (code);
+
+INSERT INTO plants VALUES (1, 'OAK'), (2, 'OAK');
+-- ERROR: duplicate key value violates unique constraint
+```
+
+If the table already contains duplicates, `CREATE UNIQUE INDEX` fails with `unique constraint violation`.
+Null values are not considered equal, so a unique index accepts several rows with a null value.
+`INSERT ... ON CONFLICT ()` can use a unique index as its conflict target.
+
+### Column order
-CedarDB automatically creates readable names for indexes to, e.g., display them in query plans.
-The automatically generated name is based on the table name and the indexed columns,
-e.g., `sales_customer_id_article_id`.
-Alternatively, you can explicitly name indexes:
+Specify the sort order of each column with `ASC` or `DESC`, and the position of null values with `NULLS FIRST` or `NULLS LAST`.
+This helps top-k queries: when the `ORDER BY` of a query matches an index, CedarDB can use the index:
```sql
-create index complaints_index on sales(...);
+CREATE TABLE sales (id int, customer_id int, article_id int, amount numeric);
+CREATE INDEX sales_recent ON sales (customer_id DESC, article_id);
+
+-- this query is eligible to use the index
+SELECT * FROM sales ORDER BY customer_id DESC, article_id LIMIT 10;
```
-CedarDB currently exclusively supports B-tree indexes.
-All indexes, thus, support range and prefix lookup.
-E.g., the example index on sales can be used for queries with predicates like:
+As in `ORDER BY`, null values sort last for `ASC` and first for `DESC`.
```sql
-... where customer_id = 42;
-... where customer_id between 5 and 10;
-... where customer_id = 42 and article_id > 100;
+CREATE TABLE trees (species text, height_m int);
+INSERT INTO trees VALUES ('Oak', 25), ('Birch', 18), ('Seedling', NULL);
+CREATE INDEX trees_height ON trees (height_m DESC);
+SELECT species, height_m FROM trees ORDER BY height_m DESC LIMIT 2;
```
-Additionally, you can declare an index to be `unique`, which will create a corresponding constraint.
+```text
+ species | height_m
+----------+----------
+ Seedling |
+ Oak | 25
+```
+
+### Automatic indexes
+
+CedarDB automatically creates indexes for `PRIMARY KEY`, `UNIQUE`, and `FOREIGN KEY` constraints.
+Primary key and unique indexes appear in `pg_indexes` with the constraint name, e.g., `plants_pkey`.
+Foreign key indexes are named after the constraint, e.g., `trees_forest_id_fkey`.
+They are listed in `pg_index`, but not in `pg_class` or `pg_indexes` for compatibility with PostgreSQL tools.
-## Column Order
+### Indexes on temporary tables and materialized views
-You can also specify the ordering of columns within the index:
+You can create indexes on [temporary tables](/docs/references/objects/tables#temporary-tables).
+These indexes are temporary, too, and CedarDB drops them together with the table.
-* `asc`, `desc`
-* `nulls first`, `nulls last`
+You can also create regular and unique indexes on [materialized views](/docs/references/objects/materialized_views).
+CedarDB maintains them on every `REFRESH MATERIALIZED VIEW`.
+You cannot create indexes on regular views.
-The default matches the order by specification: ascending with nulls last.
-Specifying the sort order is useful to support top-k queries.
-When the order of the top-k query matches an index, CedarDB will use a matching index:
+### Permissions
+
+To create an index, you must own the table and have the `CREATE` privilege on the schema of the table.
+Members of the owning role and superusers can create indexes, too.
+Privileges on the table, such as `ALL` or `WITH GRANT OPTION`, do not allow creating indexes.
+For indexes on temporary tables, you must have the `TEMPORARY` privelege on the database instead fo the `CREATE` privilege on the schema.
+
+The index belongs to the owner of the table.
+If you change the owner of the table with `ALTER TABLE ... OWNER TO`, its indexes move to the new owner.
+Indexes have no privileges of their own.
+
+## DROP INDEX
+
+`DROP INDEX` removes indexes and the corresponding `UNIQUE` or `PRIMARY KEY` constraint if one is backed by it.
+
+```text
+DROP INDEX [ CONCURRENTLY ] [ IF EXISTS ] [, ...] [ CASCADE | RESTRICT ]
+```
```sql
--- this query will be eligible to use the index
-... order by customer_id, article_id limit 10;
+CREATE TABLE sales (id int, customer_id int);
+CREATE INDEX sales_customer ON sales (customer_id);
+DROP INDEX sales_customer;
```
-## Automatic indexes
+Use `IF EXISTS` to avoid an error if the index does not exist.
+`CONCURRENTLY` is accepted for compatibility.
+
+You cannot drop an index that a foreign key depends on, such as the primary key index of a referenced table.
+
+{{< callout type="warning" >}}
+Dropping the index of a `PRIMARY KEY`, `UNIQUE`, or `FOREIGN KEY` constraint also removes the constraint, without an error.
+CedarDB then no longer enforces the constraint.
+To make the intent explicit, use `ALTER TABLE ... DROP CONSTRAINT`, which removes the constraint together with its index.
+{{< /callout >}}
+
+### Permissions
+
+To drop an index, you must own its table, own the schema, or be a superuser.
+You also need the `USAGE` privilege on the schema.
+Privileges on the table do not allow dropping its indexes.
+
+## PostgreSQL Differences
-CedarDB automatically creates indexes for `primary key`, `foreign key`, and `unique` constraints.
+- Only B-tree indexes are supported. `USING hash` falls back to B-tree with a warning, but `pg_indexes.indexdef` still shows `USING hash`.
+ `gist`, `gin`, `brin`, and `spgist` indexes are not supported.
+- Indexes on expressions, partial indexes (`WHERE`), `INCLUDE` columns, storage parameters (`WITH`), and `TABLESPACE` are not supported.
+- Operator classes, such as `text_pattern_ops`, are accepted and ignored.
+- `ALTER INDEX` and `REINDEX` are not supported.
+- Generated index names have no `_idx` suffix, e.g., `species_family_genus` instead of `species_family_genus_idx`.
+- `NULLS NOT DISTINCT` is not supported.
+- `DROP INDEX` on the index of a primary key or unique constraint drops the constraint instead of failing.
+- CedarDB creates an index for every foreign key, and `DROP INDEX` on it drops the foreign key constraint.
+- `COMMENT ON INDEX` is not supported.
diff --git a/content/references/objects/materialized_views.md b/content/references/objects/materialized_views.md
index 4846f640..86e45442 100644
--- a/content/references/objects/materialized_views.md
+++ b/content/references/objects/materialized_views.md
@@ -117,6 +117,7 @@ The `ispopulated` column of `pg_matviews` and the `relispopulated` column of `pg
You can create regular and unique [indexes](/docs/references/objects/indexes) on a materialized view.
CedarDB maintains them on every refresh.
+To create an index, you must own the materialized view (or be a member of the owning role) and have the `CREATE` privilege on its schema.
If a refresh produces rows that violate a unique index, the refresh fails and the view keeps its previous contents:
```sql
@@ -135,6 +136,12 @@ REFRESH MATERIALIZED VIEW tree_species;
To create a materialized view, you need the `CREATE` privilege on the target schema and the `SELECT` privilege on all relations that the query reads.
The creating role becomes the owner of the view.
+Default privileges defined with `ALTER DEFAULT PRIVILEGES ... ON TABLES` also apply to new materialized views.
+For example, let `bi` read every materialized view that `analytics` creates from now on:
+
+```sql
+ALTER DEFAULT PRIVILEGES FOR ROLE analytics GRANT SELECT ON TABLES TO bi;
+```
Other roles only need the `SELECT` privilege on the materialized view itself to read it.
They do not need any privileges on the underlying tables:
@@ -186,8 +193,16 @@ To keep a view up to date, run `REFRESH MATERIALIZED VIEW` periodically or after
### Permissions
-Only the owner of a materialized view can refresh it.
+Only the owner of a materialized view, members of the owning role, and superusers can refresh it.
+To let another role refresh a view, make it a member of the owning role (see [Roles](/docs/references/objects/roles#role-membership)):
+
+```sql
+GRANT analytics TO etl; -- etl can now refresh views owned by analytics
+```
+
The owner needs the `SELECT` privilege on all relations that the query reads.
+The refresh always runs with the privileges of the owner, also when a superuser runs it.
+If the owner loses this privilege, the refresh fails with `permission denied for table ''`, and the view keeps its previous contents.
## ALTER MATERIALIZED VIEW
@@ -224,8 +239,10 @@ It rejects changes that only apply to tables, such as adding columns or constrai
### Permissions
-To alter a materialized view, you must be its owner.
+To alter a materialized view, you must be its owner or a member of the owning role.
Superusers can alter any materialized view.
+Renaming the view also requires the `CREATE` privilege on its schema, and `SET SCHEMA` requires `CREATE` on the new schema.
+For `OWNER TO`, you must be able to `SET ROLE` to the new owner, and the new owner needs `CREATE` on the schema.
## DROP MATERIALIZED VIEW
@@ -263,10 +280,12 @@ To drop a materialized view, you must own it, own its schema, or be a superuser.
A materialized view depends on all tables and views its query reads.
While the materialized view exists, CedarDB refuses to drop these relations or the columns the view reads, unless you use `CASCADE`.
-You also cannot rename these relations or move them to another schema while the materialized view exists.
+You also cannot rename these relations or the columns the view reads, or move the relations to another schema, while the materialized view exists.
+Columns that the view does not read can be renamed and dropped.
## PostgreSQL Differences
- `REFRESH MATERIALIZED VIEW` never blocks readers, as CedarDB uses snapshot isolation.
`CONCURRENTLY` is accepted as a synonym for a regular refresh, does not require a unique index, and can be combined with `WITH NO DATA`.
-- CedarDB refuses to rename or move a table, view, or materialized view to another schema while a materialized view reads from it.
+- CedarDB refuses to rename a table, view, or materialized view, rename a column the view reads, or move a relation to another schema while a materialized view reads from it.
+- CedarDB refuses `ALTER TABLE ... ADD COLUMN` on a table that a view or materialized view reads from.
diff --git a/content/references/objects/policies.md b/content/references/objects/policies.md
index 99a0afea..3cd0e119 100644
--- a/content/references/objects/policies.md
+++ b/content/references/objects/policies.md
@@ -35,12 +35,18 @@ SELECT * FROM secrets;
```
{{< callout type="info" >}}
-Row level security is an enterprise feature and requires an enterprise license to create or alter policies.
+Row level security is an enterprise feature and requires an enterprise license to create policies.
+
+`ALTER TABLE ... ENABLE ROW LEVEL SECURITY`, `ALTER POLICY`, and `DROP POLICY` work without a license, and existing policies stay enforced.
+{{< /callout >}}
+
+{{< callout type="info" >}}
+Without any policy, only the owner (unless RLS is forced), superusers, and roles with `BYPASSRLS` can access a table with RLS enabled.
{{< /callout >}}
## CREATE POLICY
-```sql
+```text
CREATE POLICY name ON table_name
[ AS { PERMISSIVE | RESTRICTIVE } ]
[ FOR { ALL | SELECT | INSERT | UPDATE | DELETE } ]
@@ -55,28 +61,90 @@ The policy name must be unique within the table but can be reused across differe
`ALL`, `SELECT`, `INSERT`, `UPDATE`, and `DELETE` specify which statements the policy applies to.
-If no role is specified, `PUBLIC` is used. The table owner is exempt from policies by default; use `ALTER TABLE ... FORCE ROW LEVEL SECURITY` to override this.
+If no role is specified, `PUBLIC` is used. Since every role is a member of `PUBLIC`, the policy then applies to all roles. This also holds if `PUBLIC` is listed together with other roles. The table owner is exempt from policies by default; use `ALTER TABLE ... FORCE ROW LEVEL SECURITY` to override this.
`USING` expressions filter rows when scanning (`ALL`, `SELECT`, `UPDATE`, `DELETE` policies). `WITH CHECK` expressions validate rows before writing (`ALL`, `INSERT`, `UPDATE` policies).
+Both expressions must return `boolean`.
+Aggregate and window functions are only allowed inside subqueries.
+CedarDB rejects `USING` on `INSERT` policies and `WITH CHECK` on `SELECT` and `DELETE` policies.
-## ALTER POLICY
+A policy without `WITH CHECK` uses its `USING` expression to check new rows.
+An `UPDATE` policy checks the new row of an `UPDATE`, and an `ALL` policy checks new rows of both `INSERT` and `UPDATE`.
+A single `USING` expression on an `ALL` policy therefore restricts reads and writes alike:
```sql
+CREATE TABLE plants (id integer, gardener text);
+ALTER TABLE plants ENABLE ROW LEVEL SECURITY;
+CREATE POLICY own_plants ON plants
+ USING (gardener = current_user);
+GRANT SELECT, INSERT, UPDATE, DELETE ON plants TO PUBLIC;
+```
+
+Add a `WITH CHECK` expression if new rows must satisfy a different condition than visible rows.
+
+A policy depends on the columns, tables, and functions its expressions reference.
+Renaming, dropping, or changing the type of such a column or table fails unless you drop the policy first or use `DROP ... CASCADE`.
+
+After creating a policy, you can find it in the `pg_policies` system view and in `pg_policy`.
+
+### Permissions
+
+To create a policy, you must own the table, be a member of the owning role, or be a superuser.
+You also need the `USAGE` privilege on all schemas that the `USING` and `WITH CHECK` expressions reference.
+
+## ALTER POLICY
+
+```text
ALTER POLICY name ON table_name
[ TO { role_name | PUBLIC | CURRENT_ROLE | CURRENT_USER | SESSION_USER } [, ...] ]
[ USING ( using_expression ) ]
[ WITH CHECK ( check_expression ) ]
+
+ALTER POLICY name ON table_name RENAME TO new_name
```
-Only the role, `USING`, and `WITH CHECK` expressions can be changed with `ALTER POLICY`. To change other attributes, drop and recreate the policy.
+Only the roles, the `USING` and `WITH CHECK` expressions, and the name can be changed with `ALTER POLICY`. To change other attributes, such as `PERMISSIVE`/`RESTRICTIVE` or the command, drop and recreate the policy.
+A clause you omit keeps its current value. `TO` replaces the whole role list.
+The new expressions follow the same rules as in `CREATE POLICY`: CedarDB rejects `USING` on `INSERT` policies and `WITH CHECK` on `SELECT` and `DELETE` policies.
+
+```sql
+ALTER POLICY own_plants ON plants TO PUBLIC USING (gardener = session_user);
+ALTER POLICY own_plants ON plants RENAME TO gardener_plants;
+```
+
+### Permissions
+
+To alter a policy, you must own the table, be a member of the owning role, or be a superuser.
+You also need the `USAGE` privilege on all schemas that the new `USING` and `WITH CHECK` expressions reference.
## DROP POLICY
-```sql
+```text
DROP POLICY [ IF EXISTS ] name ON table_name
```
Dropping the last policy on a table does not disable RLS — the default-deny behavior remains active until `ALTER TABLE ... DISABLE ROW LEVEL SECURITY` is called.
+Dropping a table also drops its policies. `CASCADE` and `RESTRICT` are accepted and have no effect.
+
+### Permissions
+
+To drop a policy, you must own the table, be a member of the owning role, or be a superuser.
+
+## ENABLE and FORCE ROW LEVEL SECURITY
+
+```sql
+ALTER TABLE plants ENABLE ROW LEVEL SECURITY;
+ALTER TABLE plants DISABLE ROW LEVEL SECURITY;
+ALTER TABLE plants FORCE ROW LEVEL SECURITY;
+ALTER TABLE plants NO FORCE ROW LEVEL SECURITY;
+```
+
+`ENABLE` turns on policy checks for the table. `FORCE` also applies them to the table owner.
+The two settings are independent: `DISABLE` keeps the `FORCE` flag.
+These settings are visible in `pg_class.relrowsecurity` and `pg_class.relforcerowsecurity`.
+Row level security works on regular and temporary tables. Views, materialized views, and sequences do not support it.
+
+Only the table owner (or a member of the owning role, or a superuser) can change these settings.
## `row_security_active`
@@ -99,6 +167,25 @@ SELECT row_security_active('secrets');
Setting `row_security = off` causes CedarDB to throw an error if any table in the query has active RLS, rather than silently filtering rows. This is useful for full-database dumps where filtered output would produce an incomplete backup.
+```sql
+SET row_security = off;
+SELECT * FROM secrets;
+-- ERROR: query would be affected by row-level security policy for table "secrets"
+```
+
+For superusers, roles with `BYPASSRLS`, and table owners without forced RLS, the query runs normally.
+`BYPASSRLS` roles are exempt even from forced RLS on tables they own.
+
+## BYPASSRLS
+
+Roles with the `BYPASSRLS` attribute are not subject to row level security, like superusers:
+
+```sql
+CREATE ROLE auditor LOGIN BYPASSRLS;
+```
+
+See [Roles](/docs/references/objects/roles#create-role) for who can grant this attribute.
+
## Evaluation of policies
For each statement, policies are evaluated on tables where `row_security_active` is true.
@@ -129,6 +216,7 @@ CREATE TABLE books (id integer, author text, title text);
ALTER TABLE books ENABLE ROW LEVEL SECURITY;
CREATE POLICY books_select ON books FOR SELECT USING (true);
CREATE POLICY books_insert ON books FOR INSERT WITH CHECK (id NOT IN (SELECT id FROM books));
+GRANT SELECT, INSERT ON books TO normal_user;
SET ROLE normal_user;
-- Both rows share id = 1; the self-referencing check does not catch this
@@ -157,3 +245,16 @@ Update policies may have both a `USING` and a `WITH CHECK` expression. The `USIN
### DELETE policies
Delete policies silently skip rows that the current user is not permitted to delete.
+
+### Other statements and views
+
+- Policies apply only after the table privileges are checked: a role without `INSERT` on the table gets `permission denied`, not a policy violation.
+- `COPY ... TO` returns only the rows that `SELECT` policies allow; `COPY ... FROM` checks `INSERT` policies.
+- `TRUNCATE` is not subject to policies.
+- Expressions in policies run with the privileges of the querying role. A subquery in a policy needs `SELECT` on the referenced table.
+- A query on a view applies the policies for the view owner, not for the querying role. If a superuser owns the view, the view returns all rows.
+
+## PostgreSQL differences
+
+- `DROP POLICY IF EXISTS` does not emit a notice for a missing policy, and fails if the table does not exist.
+- Listing `PUBLIC` together with other roles does not emit a warning.
diff --git a/content/references/objects/roles.md b/content/references/objects/roles.md
index c9bc6746..1e021fd8 100644
--- a/content/references/objects/roles.md
+++ b/content/references/objects/roles.md
@@ -4,85 +4,600 @@ linkTitle: "Roles"
weight: 60
---
-## CREATE ROLE
+A role is a database identity that can own objects and hold privileges.
+Roles with the `LOGIN` attribute act as *users* that can connect to CedarDB.
+Roles without `LOGIN` usually act as *groups* that bundle privileges for their members.
+Roles are shared by all databases on a server.
-Create role allows adding new database *roles*.
-A role in SQL is an abstraction over *users* (which can log in) and *groups* (which bundle permissions).
+```sql
+-- A group role that bundles read access
+CREATE ROLE analysts;
-Usage example:
+-- A user that can log in and is a member of the group (IN ROLE requires an enterprise license)
+CREATE ROLE maple LOGIN PASSWORD 'Leaf!2024x' IN ROLE analysts;
+```
```sql
--- Create a new user
-create role dbuser login with password 'CorrectHorseBatteryStaple';
--- Create user implies a "login" role
-create user admin with createdb createrole password 'CorrectHorseBatteryStaple';
+SELECT rolname, rolcanlogin FROM pg_roles WHERE rolname IN ('analysts', 'maple');
+```
+
+```text
+ rolname | rolcanlogin
+----------+-------------
+ analysts | f
+ maple | t
+```
+
+## CREATE ROLE
+
+`CREATE ROLE` adds a new role.
+`CREATE USER` is the same, but implies `LOGIN`.
+`CREATE GROUP` is an alias for `CREATE ROLE`.
+
+```text
+CREATE ROLE [ [ WITH ]