Download Latest Version 12.27.0 source code.zip (805.0 kB)
Email in envelope

Get an email when there's a new version of pg-boss

Home / 12.27.0
Name Modified Size InfoDownloads / Week
Parent folder
12.27.0 source code.tar.gz 2026-08-03 666.0 kB
12.27.0 source code.zip 2026-08-03 805.0 kB
README.md 2026-08-03 5.6 kB
Totals: 3 Items   1.5 MB 7

What's Changed

This release lets schema be any name PostgreSQL accepts as a quoted identifier, fixes a set of catalog lookups that were silently wrong for schema names PostgreSQL folds to lower case, and adds a startup guard against the config mistake the new spelling makes possible.

Highlights

Quoted schema names

schema may now be passed pre-quoted to reach names that aren't legal bare identifiers — dashes, reserved words, mixed case you want preserved (#700):

:::js
new PgBoss({ schema: '"My-Schema"' })

The value is used verbatim in identifier positions, so the quotes are preserved as written. Quoting is the caller's, not pg-boss's: MySchema and "MySchema" are different schemas, since PostgreSQL folds the unquoted form to myschema. Existing configurations are unaffected — a bare name emits exactly the SQL it does today.

Inside a quoted name, double quotes, single quotes, percent signs, periods, dollar signs, backslashes and control characters are rejected. Each one would either escape the identifier or corrupt the format() / dollar-quoted bodies the schema is interpolated into.

Thanks to @MoazEmad1 for the original PR (#859).

Catalog lookups now use the resolved schema name

PostgreSQL stores the folded name, so every nspname = '<schema>' comparison pg-boss makes had to fold too. It didn't. With a mixed-case bare name such as schema: 'MySchema' (accepted by every prior version, stored by PostgreSQL as myschema), each of these was silently broken:

  • Queue stats partition maintenance — the "does today's partition exist" check never matched, so maintenance re-issued CREATE TABLE … PARTITION OF on a partition that already existed and failed with relation already exists. Retention pruning found no partitions to drop, so old ones accumulated forever.
  • detectSchemaDrift() / pg-boss doctor — reported an entirely empty schema, i.e. every table, index, function, column, constraint and enum value as missing.
  • BAM background index builds — the liveness probe and the invalid-index heal probe both resolved to nothing, so a stalled build was reclaimed on timeout only and an invalid leftover index was never healed.

All of these now resolve the configured value to the name in pg_namespace before comparing.

The notify channel and advisory lock key are deliberately not folded. They're hashes of a string, never compared against the catalog, so they only need to agree between instances — and folding them would change the channel and lock key of every existing bare name, leaving old and new instances unable to coordinate during a rolling upgrade. Only redundant quoting is collapsed ('"pgboss"' and 'pgboss' land on one channel). Derived values stay byte-identical to prior releases.

Guardrails for the two spellings

MySchema and "MySchema" differ by two characters in config, name two different schemas, and look identical in logs. Mis-spelling the quoting was not an error on its own: pg-boss found no installation, created an empty second schema, and every existing job appeared to have vanished. start() now refuses to install into a schema when another one differing from it only by case already holds a pg-boss installation, and names the spelling that reaches the existing data:

pg-boss is not installed in schema MySchema, but is installed in "MySchema", which differs only
in case. PostgreSQL folds unquoted names to lower case and stores quoted names verbatim, so these
are different schemas. To use the existing installation, set schema: '"MySchema"'. To install a
new schema beside it anyway, set allowSchemaCaseVariant: true.

Only pg-boss installations count, so an unrelated schema that happens to share a folded name never blocks an install. The check runs once, on the install path only. Set the new allowSchemaCaseVariant option to true if two installations whose names differ by case are genuinely intended.

Relatedly, the error for a name that isn't a legal bare identifier now hands back the config that works, instead of only naming the rule that was broken:

Schema name "my-schema" can only contain alphanumeric characters or underscores when unquoted.
Pass it quoted to use it verbatim: schema: '"my-schema"'

When quoting wouldn't help either — a$b, say — it says which rule stops it rather than recommending a value that also throws.

Behavior changes

  • start() can now throw where it previously installed. Only when the configured schema has a case variant already holding a pg-boss installation, i.e. exactly the case that used to silently strand your jobs in the other schema. Override with allowSchemaCaseVariant: true.
  • The schema length limit is measured in bytes, not characters (still 50). Identical for bare names, which are ASCII; it only binds on multi-byte characters inside a quoted name. PostgreSQL truncates identifiers past 63 bytes without complaint, which would leave the configured name and the stored name permanently out of sync.
  • An empty schema is now rejected with Name cannot be empty. Previously it passed validation and produced unparseable DDL.

New Contributors

Full Changelog: https://github.com/timgit/pg-boss/compare/12.26.4...12.27.0

Source: README.md, updated 2026-08-03