MemberJunction / MemberJunction/MJ
PostgreSQL: CodeGen silently skips layered base views instead of throwing, so #3477's loud stop never fires
- Dominant language
- TSQL
- Stars
- 29
- Forks
- 6
- Avg merge
- 1d 8h
- Merged PRs (30d)
- 308
Description
## One sentence
On PostgreSQL, CodeGen **silently skips** the base view of an entity with `GeneratedBaseViewName` set — it emits that entity's CRUD functions and its view *permissions* file but no view at all, reports success, and never reaches `assertLayeredBaseViewSupported()` — so the deliberate hard stop described in #3477 does not fire on the ordinary `mj codegen` path.
## Why this matters
#3477 establishes that layered base views are SQL-Server-only and that the PG provider throws by design, and explicitly calls that *good*: "The failure is loud rather than silent." This issue reports that on the incremental codegen path it is **not** loud. The install is left in the exact state layering was meant to prevent:
- the inner `…Generated` view is never created,
- the outer application-owned `BaseView` (which selects from it) has nothing to wrap,
- `mj codegen` exits reporting success,
- and nothing anywhere says PostgreSQL declined to do the work.
A guard that cannot be reached provides no protection, and the "loud failure" property #3477 relies on is currently not there to rely on.
## Environment
- `@memberjunction/cli@6.1.0-edge.2`, `@memberjunction/codegen-lib@6.1.0-edge.2` (published npm build, not a local tree)
- PostgreSQL 17.10, fresh DB migrated with `mj migrate --tag v6.1.0-edge.2`
- App under test: `MemberJunction/bizapps-common` @ `next`, schema `__mj_bizappscommon`
Both halves of the intended mechanism are present in the published build:
```
dist/Database/sql_codegen.js
generatesView = (entity.BaseViewGenerated || entity.HasLayeredBaseView) && !entity.VirtualEntity
dist/Database/providers/postgresql/PostgreSQLCodeGenProvider.js
assertLayeredBaseViewSupported (2 occurrences)
```
## Repro
1. Fresh PG database; `mj migrate --tag v6.1.0-edge.2`; install `bizapps-common`'s `migrations-pg/`.
2. Apply a migration that flips two entities to layered, i.e. sets `BaseViewGenerated = false` and `GeneratedBaseViewName`:
```sql
SELECT "Name","BaseViewGenerated","GeneratedBaseViewName"
FROM __mj."Entity" WHERE "GeneratedBaseViewName" <> '';
MJ_BizApps_Common: People | f | vwPeopleGenerated
MJ_BizApps_Common: Organizations | f | vwOrganizationsGenerated
```
3. Run `mj codegen` with `DB_PLATFORM=postgresql`.
**Observed:** completes — `MJ CodeGen complete — 385 entities in 60.93s`. No exception, and no occurrence of `layered base views are not supported` anywhere in the output.
The generated-SQL directory shows the skip precisely. Same schema, same run, layered vs non-layered:
| Entity | layered | `vw*.view.generated.sql` | `vw*.view.permissions.generated.sql` |
|---|---|---|---|
| `People` | yes | **missing** | present |
| `Organizations` | yes | **missing** | present |
| `OrganizationTypes` | no | present | present |
| `ContactMethods` | no | present | present |
| `Addresses` | no | present | present |
No emitted file anywhere references `vwPeopleGenerated` or `vwOrganizationsGenerated`, and neither view exists in the database afterwards. CRUD functions (`spCreatePerson`, `spUpdateOrganization`, …) and the trigger functions *were* generated normally, so the entities were unquestionably in scope and processed — only the view was dropped.
**Expected:** either the documented throw from `assertLayeredBaseViewSupported()`, or — if skipping is the intended behaviour on this path — a loud, non-zero-exit diagnostic naming the entities.
## Note on the permissions file
The permissions artifact being emitted for a view that was never created is worth a look on its own: it means the run produced `GRANT` statements against a relation it knew it had not generated.
## Downstream impact
Found while producing PostgreSQL counterparts for `bizapps-common`, whose two newest migrations (2026-08-13/14) adopt layering. On PostgreSQL those migrations produce metadata that says "layered" and a database with no inner view, and the follow-up migration that creates the outer wrapper has nothing to select from. Because codegen reports success, the resulting install looks healthy.
The same `GeneratedBaseViewName` metadata ships to every install on two core entities per #3477, so any install where those are in codegen scope is exposed to the same silent path.
## Related
- #3477 — layered base views are SQL-Server-only and PG CodeGen throws on them (this issue: the throw is not reached on the incremental path)
- #3514 — SS artifacts and the PG bake regenerating from different metadata states
🤖 Filed with [Claude Code](https://claude.com/claude-code)
Contributor guide
Research direction
Start with dist/Database/sql_codegen.js and the mj codegen entry point, then trace the generatesView condition for entities with GeneratedBaseViewName. Compare that path with assertLayeredBaseViewSupported in dist/Database/providers/postgresql/PostgreSQLCodeGenProvider.js. Done means layered PostgreSQL entities no longer silently skip their views and the run either throws or exits nonzero with a diagnostic naming them.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- node.js, postgresql
- Domain
- databases, tooling
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100