Skip to content

Add support for PostgreSQL-style CREATE TYPE#281

Open
Niols wants to merge 2 commits into
ygrek:masterfrom
Niols:create-type
Open

Add support for PostgreSQL-style CREATE TYPE#281
Niols wants to merge 2 commits into
ygrek:masterfrom
Niols:create-type

Conversation

@Niols

@Niols Niols commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

This PR builds on top of #276. Although they are in practice independent changes, they both aim at improving PostgreSQL support in sqlgg, and I have added the new commit on top of the previous one. When reviewing, I recommend making sure to only read the last commit, or, even better, to wait before #276 is merged before reviewing anything here.

I hope the change is welcome; please let me know if there is anything that you would want me to do differently, I will be happy to update my PR.

Target

PostgreSQL allows declaring a named type with CREATE TYPE, then referring to it elsewhere. This is most useful for enums:

CREATE TYPE "color" AS ENUM ('red', 'green', 'blue');

CREATE TABLE "shirt" (
  "id" INT NOT NULL,
  "color" "color" NOT NULL
);

SELECT "id" FROM "shirt" WHERE "color" = 'red';

and in fact necessary, as inline ENUM is just not accepted. Unlike MySQL's inline ENUM(...) columns, this is a separate statement that introduces a name to be looked up later. It cannot be handled at the leaf of the type grammar alone: it requires a registry that one statement populates and a later statement consults.

Change

The main change is the new module lib/types.ml, similar to lib/tables.ml (albeit much simpler) but for types. Encountering a new CREATE TYPE adds to a global registry found in that module; the counterpart DROP TYPE removes from it, and we look types up during analysis of CREATE TABLE. The rest of the change is what one expects, namely adding those syntaxes to the grammar and following the new types everywhere. I add some Cram-style tests for the feature in test/cram/create_type.t.

Limitations

  • CreateType / DropType carry no source position, so the dialect-mismatch error reads ... at with an empty tail.
  • DROP TYPE IF EXISTS parses, but the flag is discarded, so it still errors on missing names. This matches the pre-existing behaviour of DROP TABLE IF EXISTS.
  • PostgreSQL would actually reject DROP TYPE foo if foo is used in a table somewhere; I did not add such checks in this PR.
  • Only AS ENUM (...) is supported; the other PostgreSQL forms (composite, range, base, shell) are not.
  • The similar CREATE DOMAIN statement, which allows aliasing existing types with some extra constraints is not supported.

Niols added 2 commits June 5, 2026 13:33
PostgreSQL (and standard SQL) uses `ALTER COLUMN` instead of MySQL's `CHANGE COLUMN` / `MODIFY COLUMN` for modifying existing columns. This adds parsing and schema evaluation for the five main sub-commands:

- `ALTER COLUMN col TYPE newtype`
- `ALTER COLUMN col SET NOT NULL`
- `ALTER COLUMN col DROP NOT NULL`
- `ALTER COLUMN col SET DEFAULT expr`
- `ALTER COLUMN col DROP DEFAULT`

These can be combined with other actions in a single `ALTER TABLE` statement, eg.:

```
ALTER TABLE t
  DROP COLUMN old_col,
  ALTER COLUMN col TYPE SMALLINT,
  ALTER COLUMN col SET NOT NULL;
```

Migration generation (inverse actions + SQL fragments) is also supported for all five variants.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant