@crvouga/mockingbird-service-postgres 1.3.1 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,6 +1,14 @@
1
1
  # Changelog — @crvouga/mockingbird-service-postgres
2
2
 
3
- ## 1.3.1 (2026-09-30)
3
+ ## 1.4.0 (2026-09-30)
4
+
5
+ ### Features
6
+
7
+ - allow unpopulated materialized views to reference each other ([3ce1d8f](https://github.com/crvouga/mockingbird/commit/3ce1d8f555ed4008dd894aed20ebf64ab0253774))
8
+ - support CREATE EXTENSION pg_trgm ([2296ba2](https://github.com/crvouga/mockingbird/commit/2296ba2f3d1a9ea7ce8ec9f528e979034692ebf1))
9
+ - install pgcrypto digest() ([b5f3ccf](https://github.com/crvouga/mockingbird/commit/b5f3ccf79c6fedcabab157f1ee07af18a7871057))
10
+
11
+ ## 1.3.1 (2026-09-29)
4
12
 
5
13
  ### Fixes and improvements
6
14
 
package/COMPATIBILITY.md CHANGED
@@ -34,7 +34,7 @@ Anything a PostgreSQL application can invoke through SQL against the PGlite **18
34
34
  1. **Snapshot format** — custom binary codec (`PGMM`), not `pg_dump` / on-disk clusters (logical state still round-trips).
35
35
  2. **Deterministic `random()` / `now()`** — seeded PRNG and fixed clock by default (injectable).
36
36
  3. **Single session** — no MVCC across connections, no isolation levels beyond one session, no `25P02` aborted-transaction state (documented divergence).
37
- 4. **NOT APPLICABLE** rows in `compat/coverage.json` (roles, replication, VACUUM internals, LISTEN/NOTIFY, cursors, full PL/pgSQL, extensions).
37
+ 4. **NOT APPLICABLE** rows in `compat/coverage.json` (roles, replication, VACUUM internals, LISTEN/NOTIFY, cursors, full PL/pgSQL, extensions other than `pgcrypto`'s `digest()` and `pg_trgm`'s `similarity` / `<%` / `gin_trgm_ops`).
38
38
 
39
39
  The oracle exposes **2787 builtin functions** and **74 operators** in `pg_catalog`; postgres-mem implements **302 functions** and **41 operators**, and every remaining item is an explicit entry in [`compat/unsupported-register.json`](compat/unsupported-register.json) with a reason (trigger/internal plumbing, admin/monitoring, unsupported type families, …). The gate fails closed on silence.
40
40
 
@@ -62,6 +62,8 @@ The oracle exposes **2787 builtin functions** and **74 operators** in `pg_catalo
62
62
  | Triggers (row-level, LANGUAGE sql-expressible) | PARTIALLY VERIFIED | Creation-order firing, `UPDATE OF` ignored, no INSTEAD OF (documented) |
63
63
  | CREATE FUNCTION LANGUAGE sql / plpgsql-lite | PARTIALLY VERIFIED | Scalar + set-returning; plpgsql subset: DECLARE, EXCEPTION WHEN others, CASE, FOR-IN-SELECT, RETURN NEXT |
64
64
  | Text search (tsvector / tsquery / @@ / ts_rank) | PARTIALLY VERIFIED | `simple`-style config; no ispell/synonym dictionaries |
65
+ | `CREATE EXTENSION pgcrypto` / `digest()` | PARTIALLY VERIFIED | `digest(bytea, text)` and `digest(text, text)` for md5, sha1, sha224, sha256, sha384, sha512. `crypt`, `hmac`, `gen_salt`, and PGP functions are not installed. OpenSSL-only names (sha3, blake2, ripemd160, sm3) are not available. Extensions other than `pgcrypto` and `pg_trgm` still fail loud (`0A000`) |
66
+ | `CREATE EXTENSION pg_trgm` | PARTIALLY VERIFIED | `similarity(text, text)`, `<%` at the default word-similarity threshold 0.6, and a single-column `gin_trgm_ops` index on `text` or `varchar`. `gist_trgm_ops`, multicolumn `gin_trgm_ops`, and non-default thresholds are not installed. |
65
67
  | COPY FROM/TO (text, csv) | VERIFIED | Via `copyFrom` API hook / rows out |
66
68
  | PREPARE / EXECUTE / DEALLOCATE, SET / SHOW / RESET | VERIFIED | GUC subset |
67
69
  | Transactions / savepoints | VERIFIED | No `25P02` aborted state (documented divergence) |
@@ -399,7 +399,7 @@
399
399
  "evidence": [
400
400
  "tests/contract/views/"
401
401
  ],
402
- "notes": "materialized views are eager snapshots refreshed by REFRESH MATERIALIZED VIEW"
402
+ "notes": "eager snapshots; WITH NO DATA sets relispopulated false and may read an unpopulated matview; REFRESH populates; CONCURRENTLY is not modelled"
403
403
  },
404
404
  "sql-createoperator": {
405
405
  "status": "UNSUPPORTED",
@@ -6,6 +6,7 @@ export const CAT_SECTION: CatalogSection = section("CAT", "pg_catalog and inform
6
6
  ["class-01", "user table appears in pg_class with relkind r"],
7
7
  ["class-02", "view appears in pg_class with relkind v"],
8
8
  ["class-03", "sequence appears in pg_class with relkind S"],
9
+ ["class-06", "materialized view appears in pg_class with relkind m"],
9
10
  ["class-04", "pg_class joined to pg_namespace resolves the schema"],
10
11
  ["class-05", "pg_class row disappears after DROP TABLE"],
11
12
  ["attr-01", "pg_attribute lists user columns in order"],
@@ -501,7 +501,8 @@ export interface CreateIndexStmt {
501
501
  expr: Expr;
502
502
  dir: "asc" | "desc" | null;
503
503
  nulls: "first" | "last" | null;
504
- opclass: string | null;
504
+ /** Operator class name, possibly schema-qualified (`public.gin_trgm_ops`). */
505
+ opclass: string[] | null;
505
506
  }>;
506
507
  readonly include: string[];
507
508
  readonly where: Expr | null;
@@ -547,6 +548,14 @@ export interface CreateSchemaStmt {
547
548
  readonly name: string;
548
549
  readonly ifNotExists: boolean;
549
550
  }
551
+ export interface CreateExtensionStmt {
552
+ readonly type: "create_extension";
553
+ readonly name: string;
554
+ readonly ifNotExists: boolean;
555
+ readonly schema: string | null;
556
+ readonly version: string | null;
557
+ readonly cascade: boolean;
558
+ }
550
559
  export interface CreateEnumStmt {
551
560
  readonly type: "create_enum";
552
561
  readonly name: string[];
@@ -809,4 +818,4 @@ export interface DoStmt {
809
818
  readonly language: string;
810
819
  readonly body: string;
811
820
  }
812
- export type Statement = SelectStmt | InsertStmt | UpdateStmt | DeleteStmt | CreateTableStmt | CreateTableAsStmt | CreateIndexStmt | CreateViewStmt | CreateSequenceStmt | AlterSequenceStmt | CreateSchemaStmt | CreateEnumStmt | AlterEnumStmt | CreateDomainStmt | CreateFunctionStmt | CreateTriggerStmt | AlterTableStmt | AlterViewStmt | AlterIndexStmt | AlterSchemaStmt | DropStmt | TruncateStmt | RefreshMaterializedViewStmt | TransactionStmt | SetStmt | ShowStmt | ResetStmt | PrepareStmt | ExecuteStmt | DeallocateStmt | ExplainStmt | CopyStmt | CommentStmt | NoOpStmt | DoStmt;
821
+ export type Statement = SelectStmt | InsertStmt | UpdateStmt | DeleteStmt | CreateTableStmt | CreateTableAsStmt | CreateIndexStmt | CreateViewStmt | CreateSequenceStmt | AlterSequenceStmt | CreateSchemaStmt | CreateExtensionStmt | CreateEnumStmt | AlterEnumStmt | CreateDomainStmt | CreateFunctionStmt | CreateTriggerStmt | AlterTableStmt | AlterViewStmt | AlterIndexStmt | AlterSchemaStmt | DropStmt | TruncateStmt | RefreshMaterializedViewStmt | TransactionStmt | SetStmt | ShowStmt | ResetStmt | PrepareStmt | ExecuteStmt | DeallocateStmt | ExplainStmt | CopyStmt | CommentStmt | NoOpStmt | DoStmt;
@@ -1,4 +1,4 @@
1
- import type { AlterEnumStmt, AlterIndexStmt, AlterSchemaStmt, AlterSequenceStmt, AlterTableStmt, AlterViewStmt, CreateDomainStmt, CreateEnumStmt, CreateFunctionStmt, CreateIndexStmt, CreateSchemaStmt, CreateSequenceStmt, CreateTableAsStmt, CreateTableStmt, CreateTriggerStmt, CreateViewStmt, DropStmt, RefreshMaterializedViewStmt, SequenceOptions, TruncateStmt } from "../ast/nodes.js";
1
+ import type { AlterEnumStmt, AlterIndexStmt, AlterSchemaStmt, AlterSequenceStmt, AlterTableStmt, AlterViewStmt, CreateDomainStmt, CreateEnumStmt, CreateExtensionStmt, CreateFunctionStmt, CreateIndexStmt, CreateSchemaStmt, CreateSequenceStmt, CreateTableAsStmt, CreateTableStmt, CreateTriggerStmt, CreateViewStmt, DropStmt, RefreshMaterializedViewStmt, SequenceOptions, TruncateStmt } from "../ast/nodes.js";
2
2
  import { SchemaData, type SequenceData } from "../storage/database-state.js";
3
3
  import { type ExecEnv, type ExecResult } from "./relation.js";
4
4
  export declare function buildSequence(env: ExecEnv, schema: SchemaData, name: string, options: SequenceOptions, temp: boolean): SequenceData;
@@ -9,6 +9,7 @@ export declare function executeCreateTableAs(env: ExecEnv, stmt: CreateTableAsSt
9
9
  export declare function executeCreateIndex(env: ExecEnv, stmt: CreateIndexStmt): ExecResult;
10
10
  export declare function executeCreateView(env: ExecEnv, stmt: CreateViewStmt): ExecResult;
11
11
  export declare function executeRefreshMatView(env: ExecEnv, stmt: RefreshMaterializedViewStmt): ExecResult;
12
+ export declare function executeCreateExtension(env: ExecEnv, stmt: CreateExtensionStmt): ExecResult;
12
13
  export declare function executeCreateSchema(env: ExecEnv, stmt: CreateSchemaStmt): ExecResult;
13
14
  export declare function executeCreateEnum(env: ExecEnv, stmt: CreateEnumStmt): ExecResult;
14
15
  export declare function executeAlterEnum(env: ExecEnv, stmt: AlterEnumStmt): ExecResult;
@@ -40,6 +40,11 @@ export interface ExecEnv {
40
40
  /** CTE results by name (WITH clause); shadowed inner-to-outer */
41
41
  ctes: Map<string, Relation>;
42
42
  outer: RowScope | null;
43
+ /**
44
+ * CREATE/REFRESH MATERIALIZED VIEW … WITH NO DATA. An unpopulated matview
45
+ * contributes its column shape and no rows, matching PostgreSQL planning.
46
+ */
47
+ allowUnpopulatedMatviews?: boolean;
43
48
  }
44
49
  export declare function childEnv(env: ExecEnv, outer: RowScope | null): ExecEnv;
45
50
  export declare function withCtes(env: ExecEnv, ctes: Map<string, Relation>): ExecEnv;
@@ -0,0 +1,9 @@
1
+ import type { EngineCtx } from "../expressions/context.js";
2
+ import type { DatabaseState, FunctionData } from "../storage/database-state.js";
3
+ import type { TypedValue } from "../types/value.js";
4
+ /** pgcrypto `digest(data, type)` for the algorithms PostgreSQL documents as standard. */
5
+ export declare function digestBytes(data: Uint8Array, algorithm: string): Uint8Array;
6
+ export declare function pgcryptoInstalled(state: DatabaseState): boolean;
7
+ export declare function installPgcrypto(state: DatabaseState, schemaName: string): void;
8
+ export declare function uninstallPgcrypto(state: DatabaseState): boolean;
9
+ export declare function evalPgcryptoFunction(ctx: EngineCtx, fn: FunctionData, args: TypedValue[]): TypedValue;
@@ -0,0 +1,24 @@
1
+ import type { CreateIndexStmt } from "../ast/nodes.js";
2
+ import type { EngineCtx } from "../expressions/context.js";
3
+ import type { DatabaseState, FunctionData, TableData } from "../storage/database-state.js";
4
+ import { type TypedValue } from "../types/value.js";
5
+ export declare function pgTrgmSchema(state: DatabaseState): string | null;
6
+ export declare function pgTrgmInstalled(state: DatabaseState): boolean;
7
+ export declare function installPgTrgm(state: DatabaseState, schemaName: string): void;
8
+ export declare function uninstallPgTrgm(state: DatabaseState): boolean;
9
+ export declare function evalPgTrgmFunction(ctx: EngineCtx, fn: FunctionData, args: TypedValue[]): TypedValue;
10
+ /** `text <% text` — word similarity at the default threshold. Absent until pg_trgm is installed. */
11
+ export declare function evalTrgmWordSimilar(ctx: EngineCtx, left: TypedValue, right: TypedValue): TypedValue;
12
+ /**
13
+ * Reject `gin_trgm_ops` unless pg_trgm is installed for access method gin.
14
+ * A single text/varchar column is stored as an ordinary non-unique index:
15
+ * lookup speed is not modelled.
16
+ */
17
+ export declare function assertGinTrgmIndex(state: DatabaseState, table: TableData, stmt: CreateIndexStmt): void;
18
+ /** `|intersection| / |union|` as float4. Either empty trigram set yields 0. */
19
+ export declare function trigramSimilarity(left: string, right: string): number;
20
+ /**
21
+ * Greatest similarity between the trigram set of `pattern` and any continuous
22
+ * extent of the ordered trigrams of `haystack` (PostgreSQL `word_similarity`).
23
+ */
24
+ export declare function wordSimilarity(pattern: string, haystack: string): number;