module Noir::PostgresDdlParser

Overview

Minimal PostgreSQL DDL reader, enough to recover the table/column shape a PostgREST-style API is generated from.

Migrations are a sequence, not a set: a column added in the third file and dropped in the fifth is not part of the final surface. So statements are applied onto a State that callers thread through every file in order.

The parse is three passes:

  1. Mask comments and string bodies, preserving byte positions and newlines so later passes can still slice and count lines.
  2. Split on top-level ; (paren depth 0).
  3. Match statement headers, and split the column list on top-level commas so numeric(10,2), check (a > 0) and references t(id) survive.

Dollar-quoted bodies ($$ ... $$) are masked in pass 1 — function bodies routinely contain both create table text and semicolons, and without this a single function definition would shred the split.

Extended Modules

Defined in:

miniparsers/postgres_ddl_parser.cr

Constant Summary

ADD_COLUMN = /\Aadd\s+(?:column\s+)?(?:if\s+not\s+exists\s+)?(.+)\z/im
ALTER_TABLE = /\A\s*alter\s+table\s+(?:if\s+exists\s+)?(?:only\s+)?([^\s(]+)\s+(.*)\z/im
CONSTRAINT_KEYWORDS = Set {"primary", "foreign", "unique", "check", "constraint", "exclude", "like", "inherits", "partition"}

Table-level constraints, not columns.

CREATE_FUNC = /\A\s*create\s+(?:or\s+replace\s+)?function\s+([^\s(]+)\s*\(/i
CREATE_TABLE = /\A\s*create\s+(?:global\s+|local\s+|temp(?:orary)?\s+|unlogged\s+)*table\s+(?:if\s+not\s+exists\s+)?([^\s(]+)/i
CREATE_VIEW = /\A\s*create\s+(?:or\s+replace\s+)?(?:materialized\s+)?view\s+(?:if\s+not\s+exists\s+)?([^\s(]+)/i
DEFAULT_SCHEMA = "public"
DROP_COLUMN = /\Adrop\s+(?:column\s+)?(?:if\s+exists\s+)?([^\s,]+)/i
DROP_TABLE = /\A\s*drop\s+table\s+(?:if\s+exists\s+)?(.+?)(?:\s+cascade|\s+restrict)?\s*\z/im
RENAME_COLUMN = /\Arename\s+(?:column\s+)?([^\s]+)\s+to\s+([^\s,]+)/i
RENAME_TABLE = /\Arename\s+to\s+([^\s,]+)/i
TYPE_HINTS = {"text" => "string", "varchar" => "string", "character" => "string", "char" => "string", "uuid" => "string", "citext" => "string", "name" => "string", "bytea" => "string", "inet" => "string", "cidr" => "string", "macaddr" => "string", "xml" => "string", "int" => "int", "int2" => "int", "int4" => "int", "int8" => "int", "integer" => "int", "smallint" => "int", "bigint" => "int", "serial" => "int", "bigserial" => "int", "smallserial" => "int", "numeric" => "number", "decimal" => "number", "real" => "number", "double" => "number", "float" => "number", "float4" => "number", "float8" => "number", "money" => "number", "bool" => "boolean", "boolean" => "boolean", "timestamp" => "datetime", "timestamptz" => "datetime", "date" => "datetime", "time" => "datetime", "timetz" => "datetime", "interval" => "datetime", "json" => "object", "jsonb" => "object"}
TYPE_TERMINATORS = Set {"not", "null", "default", "primary", "references", "unique", "check", "generated", "collate", "constraint", "on", "deferrable", "identity", "always", "storage", "compression"}

Terminate the type when one of these begins the column's constraints.

Instance Method Summary

Instance Method Detail

def apply(content : String, source : String, state : State) : Nil #

Applies every statement in content onto state.


[View source]
def parse(content : String, source : String = "") : State #

Convenience for a single self-contained document.


[View source]