Generate malli schemas from an applied PostgreSQL schema.
Config: {:schemas ["public"] ; default ["public"] :out-dir "resources/pgmalli" ; default; one file per schema, <out-dir>/<schema>.edn :overrides {constraint-name malli-schema | {:skip reason}} :db {:host :port :db :user :password :sslmode :psql}} ; optional; psql's environment otherwise
The config, the generated file layout and the fact vocabulary (see pgmalli.impl.pattern) are the stable contract. pgmalli.impl.* may change without notice.
Generate malli schemas from an applied PostgreSQL schema.
Config:
{:schemas ["public"] ; default ["public"]
:out-dir "resources/pgmalli" ; default; one file per schema, <out-dir>/<schema>.edn
:overrides {constraint-name malli-schema | {:skip reason}}
:db {:host :port :db :user :password :sslmode :psql}} ; optional; psql's environment otherwise
The config, the generated file layout and the fact vocabulary (see pgmalli.impl.pattern)
are the stable contract. pgmalli.impl.* may change without notice.Turns expressions as printed by PostgreSQL's deparser (pg_get_constraintdef, pg_get_expr) into HoneySQL-style data. Only that output format is supported, not arbitrary SQL.
Covered: comparison, logical and arithmetic operators; IS [NOT] NULL/TRUE/FALSE; IN; ANY/ALL (ARRAY[...]); BETWEEN; LIKE/ILIKE/~/~*; ::casts; function calls; TRIM(BOTH FROM x); CASE; CURRENT_TIMESTAMP and friends; AT TIME ZONE; (x).field; x[i]; quoted identifiers; string, number, boolean and NULL literals.
Operators whose symbol is not a readable EDN keyword (~, @>, ...) get a named keyword (see edn-safe-ops); ->honeysql maps them back when SQL has to be generated.
Turns expressions as printed by PostgreSQL's deparser (pg_get_constraintdef, pg_get_expr) into HoneySQL-style data. Only that output format is supported, not arbitrary SQL. Covered: comparison, logical and arithmetic operators; IS [NOT] NULL/TRUE/FALSE; IN; ANY/ALL (ARRAY[...]); BETWEEN; LIKE/ILIKE/~/~*; ::casts; function calls; TRIM(BOTH FROM x); CASE; CURRENT_TIMESTAMP and friends; AT TIME ZONE; (x).field; x[i]; quoted identifiers; string, number, boolean and NULL literals. Operators whose symbol is not a readable EDN keyword (~, @>, ...) get a named keyword (see edn-safe-ops); ->honeysql maps them back when SQL has to be generated.
Config -> generated EDN files, one per schema.
Config: {:schemas ["public"] ; default ["public"] :out-dir "resources/pgmalli" ; default; files are <out-dir>/<schema>.edn :overrides {constraint-name schema-or-{:skip reason}} :db {:host :port :db :user :password :sslmode :psql}} ; optional; psql's environment otherwise
File contents: {:schema "public" :database-version "PostgreSQL 17.x ..." :registry {name schema} :unrendered [fact ...] :skipped [fact ...]}
Output is deterministic (sorted maps, pprint). Overrides that are not EDN (functions) are rejected at generation time.
Config -> generated EDN files, one per schema.
Config:
{:schemas ["public"] ; default ["public"]
:out-dir "resources/pgmalli" ; default; files are <out-dir>/<schema>.edn
:overrides {constraint-name schema-or-{:skip reason}}
:db {:host :port :db :user :password :sslmode :psql}} ; optional; psql's environment otherwise
File contents:
{:schema "public" :database-version "PostgreSQL 17.x ..."
:registry {name schema} :unrendered [fact ...] :skipped [fact ...]}
Output is deterministic (sorted maps, pprint). Overrides that are not EDN (functions)
are rejected at generation time.Reads the structure of one schema from pg_catalog through psql.
Connection settings are psql's own (PGHOST, PGPORT, PGDATABASE, PGUSER, PGPASSWORD, PGSSLMODE, ~/.pgpass); entries in the db map override them for the call.
Result shape: {:name "public" :database_version "PostgreSQL 17.x ..." :tables {"users" {:name "users" :columns [{:name :position :data_type :type_schema :is_nullable :default_value :generated_expr :identity :max_length :precision :scale}] :constraints {"users_age_check" {:name :type "CHECK" :check_clause :is_valid}}}} :types {"mood" {:kind "ENUM" :enum_values [...]} "email" {:kind "DOMAIN" :base_type :not_null :default :constraints [{:name :definition}]}}} Maps keyed by object name (:tables, :constraints, :types) keep string keys; everything else is keywordized. Expressions are the strings PostgreSQL's deparser produces.
Reads the structure of one schema from pg_catalog through psql.
Connection settings are psql's own (PGHOST, PGPORT, PGDATABASE, PGUSER, PGPASSWORD,
PGSSLMODE, ~/.pgpass); entries in the db map override them for the call.
Result shape:
{:name "public" :database_version "PostgreSQL 17.x ..."
:tables {"users" {:name "users"
:columns [{:name :position :data_type :type_schema :is_nullable
:default_value :generated_expr :identity :max_length :precision :scale}]
:constraints {"users_age_check" {:name :type "CHECK" :check_clause :is_valid}}}}
:types {"mood" {:kind "ENUM" :enum_values [...]}
"email" {:kind "DOMAIN" :base_type :not_null :default :constraints [{:name :definition}]}}}
Maps keyed by object name (:tables, :constraints, :types) keep string keys; everything
else is keywordized. Expressions are the strings PostgreSQL's deparser produces.Facts about a schema, derived from the structure read by pgmalli.impl.ir.
A fact is a map with :fact (the kind), :schema, and :table / :column / :constraint where they apply. Column-local CHECK constraints are matched against a fixed set of patterns; anything else is kept as :table-check with the expression data, never dropped.
Kinds: :enum-type an enum type of the schema {:type-name :values} :column a column {:type :position :nullable? :default :identity? :generated} :enum column of an enum type {:type-name :values} :unknown-type type outside the mapping table {:type} :max-length varchar(n) {:max} :numeric numeric(p,s) {:precision :scale} :in-set col IN (...) / col = v {:values} :range col >= a AND col <= b, BETWEEN, ... {:min :max :min-exclusive? :max-exclusive?} :non-blank length(trim(col)) > 0 / col <> '' {:trim?} :length length(col) <= n / octet_length = n {:fn :min :max :exact} :json-type jsonb_typeof(col) = 'object' {:json-type} :regex col ~ 're' {:re :case-insensitive?} :not-null col IS NOT NULL :when-present col IS NULL OR <column pattern> {:fact-when-present} :table-check CHECK that matched no pattern {:expr :columns :valid?} :unparsed expression that could not be read {:input :error}
One CHECK can yield several facts (column patterns joined by AND). NOT VALID constraints are never matched, since existing rows may violate them.
Facts about a schema, derived from the structure read by pgmalli.impl.ir.
A fact is a map with :fact (the kind), :schema, and :table / :column / :constraint where they
apply. Column-local CHECK constraints are matched against a fixed set of patterns; anything
else is kept as :table-check with the expression data, never dropped.
Kinds:
:enum-type an enum type of the schema {:type-name :values}
:column a column {:type :position :nullable? :default :identity? :generated}
:enum column of an enum type {:type-name :values}
:unknown-type type outside the mapping table {:type}
:max-length varchar(n) {:max}
:numeric numeric(p,s) {:precision :scale}
:in-set col IN (...) / col = v {:values}
:range col >= a AND col <= b, BETWEEN, ... {:min :max :min-exclusive? :max-exclusive?}
:non-blank length(trim(col)) > 0 / col <> '' {:trim?}
:length length(col) <= n / octet_length = n {:fn :min :max :exact}
:json-type jsonb_typeof(col) = 'object' {:json-type}
:regex col ~ 're' {:re :case-insensitive?}
:not-null col IS NOT NULL
:when-present col IS NULL OR <column pattern> {:fact-when-present}
:table-check CHECK that matched no pattern {:expr :columns :valid?}
:unparsed expression that could not be read {:input :error}
One CHECK can yield several facts (column patterns joined by AND). NOT VALID constraints
are never matched, since existing rows may violate them.Facts -> malli schemas. Every fact kind has exactly one rendering; there are no options.
registry returns {:registry {name schema} :unrendered [fact] :skipped [fact]}. Names: enum types as :pg.<schema>/<type>, tables as :pg.<schema>/<table>. A table schema describes one row as read from the database: every column present, nullable ones as [:maybe ...]. Column properties record provenance: :pg/type, :pg/default, :pg/constraint.
Identifiers that are not plain names (spaces, punctuation) become string keys so the result stays readable EDN.
overrides is {constraint-name schema-or-{:skip reason}}. A schema is added with [:and ...] to the column (column-local constraint) or the table (table constraint); :skip drops the fact from :unrendered into :skipped.
Facts -> malli schemas. Every fact kind has exactly one rendering; there are no options.
registry returns {:registry {name schema} :unrendered [fact] :skipped [fact]}.
Names: enum types as :pg.<schema>/<type>, tables as :pg.<schema>/<table>. A table schema
describes one row as read from the database: every column present, nullable ones as
[:maybe ...]. Column properties record provenance: :pg/type, :pg/default, :pg/constraint.
Identifiers that are not plain names (spaces, punctuation) become string keys so the
result stays readable EDN.
overrides is {constraint-name schema-or-{:skip reason}}. A schema is added with [:and ...]
to the column (column-local constraint) or the table (table constraint); :skip drops the
fact from :unrendered into :skipped.Command line entry point. Config comes from pgmalli.edn in the working directory or the path given as second argument.
clojure -M -m pgmalli.main generate [pgmalli.edn] clojure -M -m pgmalli.main check [pgmalli.edn] ; exit 1 when files are stale bb -m pgmalli.main ...
Command line entry point. Config comes from pgmalli.edn in the working directory or the path given as second argument. clojure -M -m pgmalli.main generate [pgmalli.edn] clojure -M -m pgmalli.main check [pgmalli.edn] ; exit 1 when files are stale bb -m pgmalli.main ...
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |