fix(storage): mirror bulk upsert validation#60
Conversation
Greptile SummaryThis PR hardens
Confidence Score: 5/5Safe to merge. All changes are additive validation guards with no behavioral change for valid inputs. The change adds early-throw guards for previously unvalidated empty-array inputs across three consistent locations. Valid callers are unaffected. The new test exercises both rejection cases through the public ORM surface, matching the style and harness used throughout the suite. No existing behavior is altered. No files require special attention. The memory adapter's pre-existing absence of a values.length === 0 early-return (unlike the Drizzle adapter) is harmless since iterating an empty array is a no-op, and is unrelated to this PR. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A["caller: upsertMany(name, {target, update, values})"] --> B{values.length === 0?}
B -- yes --> C[return early]
B -- no --> D{target.length === 0?}
D -- yes --> E["throw: at least one target column required"]
D -- no --> F{update.length === 0?}
F -- yes --> G["throw: at least one update column required"]
F -- no --> H["resolve columns, apply policies"]
H --> I{adapter.upsertMany available?}
I -- yes --> J["adapter.upsertMany()"]
I -- no --> K["fallback: loop upsert()"]
J --> L["Drizzle guard: target.length === 0? throw"]
J --> M["Drizzle guard: update.length === 0? throw"]
L --> N["execute SQL upsert"]
M --> N
style E fill:#f66,color:#fff
style G fill:#f66,color:#fff
style L fill:#ffa,color:#000
style M fill:#ffa,color:#000
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
A["caller: upsertMany(name, {target, update, values})"] --> B{values.length === 0?}
B -- yes --> C[return early]
B -- no --> D{target.length === 0?}
D -- yes --> E["throw: at least one target column required"]
D -- no --> F{update.length === 0?}
F -- yes --> G["throw: at least one update column required"]
F -- no --> H["resolve columns, apply policies"]
H --> I{adapter.upsertMany available?}
I -- yes --> J["adapter.upsertMany()"]
I -- no --> K["fallback: loop upsert()"]
J --> L["Drizzle guard: target.length === 0? throw"]
J --> M["Drizzle guard: update.length === 0? throw"]
L --> N["execute SQL upsert"]
M --> N
style E fill:#f66,color:#fff
style G fill:#f66,color:#fff
style L fill:#ffa,color:#000
style M fill:#ffa,color:#000
Reviews (1): Last reviewed commit: "fix(storage): mirror bulk upsert validat..." | Re-trigger Greptile |
Summary
upsertManyconflict targets at the public FumaDB query boundary.Validation
bunx vitest run src/query/table-policy.test.tsfrompackages/core/fumadbbunx oxlint --no-ignore --deny-warnings src/query/orm/index.ts src/adapters/drizzle/query.ts src/adapters/memory/index.ts src/query/table-policy.test.tsfrompackages/core/fumadbgit diff --checkNotes
bun run --cwd packages/core/fumadb typecheckis currently blocked in this checkout by the existing missing@libsql/clientimport insrc/adapters/drizzle/runtime-ensure.test.ts. Root oxlint intentionally ignorespackages/core/fumadb/, so the file-level lint was run from the package with--no-ignore.