Skip to content

[BUG]: Real encoding conversions (convert_to, convert_from) silently corrupt the PGlite instance #1076

Description

@racinette

convert_to/convert_from/convert performing a real encoding conversion return zero rows and permanently corrupt the instance

Environment: PGlite 0.5.4 (PostgreSQL 18.3, wasm32), Node 18, in-memory database, UTF8 encoding.

Reproduction

const pg = await PGlite.create();
await pg.query(`SELECT convert_to('abc', 'LATIN1')`);  // resolves with ZERO rows, no error
await pg.query(`SELECT 1`);                            // fails — backend unusable

Deterministic; a single call on a fresh instance suffices.

Observed behavior

SELECT convert_to('abc', <encoding>):

encoding result backend afterwards
'nonsense' clean error: invalid destination encoding name healthy
'UTF8' (identity), 'SQL_ASCII' correct value healthy
'LATIN1' (real conversion) "success" with zero rows broken

convert_from(bytea, name) and convert(bytea, name, name) behave identically whenever an actual conversion would run.

The corruption depends on call context. Direct call — everything fails afterwards:

await pg.query(`SELECT convert_to('abc', 'LATIN1')`);  // zero rows
await pg.query(`SELECT 1`);                            // throws

Inside a plpgsql EXCEPTION block — the backend keeps answering, but lies:

await pg.exec(`
  CREATE FUNCTION probe(expr text) RETURNS text LANGUAGE plpgsql AS $$
  DECLARE r boolean;
  BEGIN
    EXECUTE 'SELECT (' || expr || ') IS NULL' INTO r;
    RETURN CASE WHEN r THEN 'NULL' ELSE 'value' END;
  EXCEPTION WHEN OTHERS THEN RETURN 'error';
  END $$;`);

await pg.query(`SELECT probe('convert_to(''abc'', ''LATIN1'')')`);  // SHORT result (0 rows)
await pg.query(`SELECT 1`);                                          // still works!
await pg.query(`SELECT probe('1')`);                                 // ERRORDATA_STACK_SIZE exceeded
await pg.query(`SELECT x, probe(x::text) FROM generate_series(1, 100) x`);
// can return PARTIAL or empty row sets with no error

The state persists indefinitely; only discarding the instance recovers. The second mode passes SELECT 1-style liveness checks while silently returning wrong results.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions