MemberJunction / MemberJunction/MJ
Numeric and bit IN(...) CHECK constraints produce no value list, so the field loses both its dropdown and its validation
- Dominant language
- TSQL
- Stars
- 29
- Forks
- 6
- Avg merge
- 2d 1h
- Merged PRs (30d)
- 323
Description
## Summary
A numeric or `bit` `IN (…)` CHECK constraint produces **no `EntityFieldValue` rows and no
`ValueListType='List'`**, because SQL Server renders those constraints with *unquoted* literals and
`parseCheckConstraintValues` only matches quoted ones. The consequence is not just missing
validation — the field also loses its **dropdown in Explorer**, since `PossibleValues` reads the
same `EntityFieldValue` rows. And when AI codegen is off, such a constraint yields *nothing at all*:
no value list, no generated validator.
- Status: OPEN
- Found: 2026-08-20, while implementing #3969 (MJ 6.1.0-edge.2)
- Suspected layer: `@memberjunction/codegen-lib` — `ManageMetadataBase.parseCheckConstraintValues`
## Repro — how to recreate
The shapes below are what SQL Server actually stores; I captured them by creating a temp table with
one `IN`-list CHECK per data type and reading `tempdb.sys.check_constraints`, then ran each
definition through CodeGen's own `parseCheckConstraintValues` regex:
```
CREATE TABLE #t (
S nvarchar(20) CHECK (S IN ('Active','Inactive')),
C nchar(10) CHECK (C IN ('Input','Output')),
L int CHECK (L IN (1,2,3)),
M money CHECK (M IN (0.50, 1.00)),
B bit CHECK (B IN (0,1)),
D date CHECK (D IN ('2026-01-01','2026-07-01')),
T datetime2 CHECK (T IN ('2026-01-01T00:00:00')),
G uniqueidentifier CHECK (G IN ('11111111-1111-1111-1111-111111111111'))
);
```
| Column type | `sys.check_constraints.definition` | Parser result |
| --- | --- | --- |
| `nvarchar` / `nchar` (≥2 values) | `([S]='Inactive' OR [S]='Active')` | ✅ value list |
| `date` (≥2 values) | `([D]='2026-07-01' OR [D]='2026-01-01')` | ✅ value list |
| `uniqueidentifier` (≥2 values) | quoted GUIDs | ✅ value list |
| `int` | `([L]=(3) OR [L]=(2) OR [L]=(1))` | ❌ not parsed |
| `money` / `decimal` | `([M]=(1.00) OR [M]=(0.50))` | ❌ not parsed |
| `bit` | `([B]=(1) OR [B]=(0))` | ❌ not parsed |
| any type, **single** value | `([T]='2026-01-01T00:00:00')` | ❌ not parsed |
The cause is in the regexes, which require a quoted literal on the right-hand side:
```ts
const structureRegex = new RegExp(`^\\(${quotedField}='[^']+'(?: OR ${quotedField}='[^']+?')+(?: OR ${quotedField} IS NULL)?\\)$`);
const valueRegex = new RegExp(`${quotedField}='([^']+)\'`, 'g');
```
Two separate limitations fall out of that: unquoted (numeric/bit) values never match, and the `+`
on the OR group requires **at least two** values, so a single-value `IN` list is never captured for
any type.
## Expected vs actual
**Expected** — `CHECK (Level IN (1,2,3))` populates `EntityFieldValue` with `1`, `2`, `3` and sets
`ValueListType='List'`, exactly as the string case does, so the field renders as a dropdown and is
validated before the round trip.
**Actual** — no rows, no `ValueListType`. With `ParseCheckConstraints` enabled the constraint falls
through to an AI-generated `Validate()` method (so it *is* enforced, by a different mechanism, and
still with no dropdown); with AI codegen disabled it is dropped silently.
## Suggested fix
Extend `parseCheckConstraintValues` to accept SQL Server's unquoted numeric form —
`[Field]=(3)` alongside `[Field]='Value'` — and to accept a single-value list. Both are contained
changes to the two regexes plus the value extraction. Worth deciding at the same time:
1. Whether a `bit` list is worth capturing at all. `CHECK (B IN (0,1))` is vacuous (it permits
exactly what `bit` already permits), so capturing it would add a two-item dropdown of `0`/`1`
where a checkbox belongs. `CHECK (B = 1)` is the meaningful bit constraint, and that is a
validator, not a list.
2. That the runtime side is ready for it: `@memberjunction/core`'s value-list validation
(#3969 / PR #3972) already compares numbers by value rather than by JS type, precisely so a
numeric list works the day CodeGen starts producing one.
## Related
- #3969 / PR #3972 — the runtime validation this metadata feeds.
- A `List` field with **no** `EntityFieldValue` rows is a related CodeGen-side check @AN-BC raised in
review of that PR; the runtime now logs it, but CodeGen could catch it at generation time.
Contributor guide
Research direction
Start in @memberjunction/codegen-lib at ManageMetadataBase.parseCheckConstraintValues and inspect the structureRegex, valueRegex, and value extraction. Reproduce the listed SQL Server constraint definitions, then verify numeric and single-value IN lists populate EntityFieldValue and ValueListType='List'. Review @memberjunction/core's value-list validation and decide how the issue's bit-list question should be handled.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- sql, typescript
- Domain
- databases, devtools
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 68/100