MemberJunction / MemberJunction/MJ

Numeric and bit IN(...) CHECK constraints produce no value list, so the field loses both its dropdown and its validation

Open
#3,978 0 comments 0 reactions 0 assignees View on GitHub
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.