fix: detect no-op document stores via OUTPUT rowset; sanitise regenerated filename
Follow-up to 99bf533 from code review:
- The AffectedRows == 0 guard in Store{Invoice,Reminder}DocumentFileAsync
was dead: fds__setInvoiceFile / fds__setReminderFile run SET NOCOUNT ON,
so ExecuteNonQuery returns -1 whether or not the UPDATE ... WHERE [file]
IS NULL matched. Both procs already return the updated Id through their
OUTPUT clause, so read that result set (getSQLDatatable_async) instead:
empty rowset = no-op store, logged, and the blob upload / FileCreated
event are skipped rather than reporting unpersisted bytes as success.
- inv/rdoc's regenerate path built the download name from the raw
DocumentName; a user-typed title with '/' truncated FileInfo.Name.
SafeDocumentFileName strips editor markup and every invalid filename
char, and the path no longer goes through FileInfo at all.
- Reminder counterpart tests for the store-file failure branches.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -42,12 +42,13 @@ A read is therefore allowed to write `[file]`, upload the blob and emit
|
||||
Every failure inside `StoreInvoiceDocumentFileAsync`/`StoreReminderDocumentFileAsync`
|
||||
(render threw, render produced no bytes, DB write threw, DB write matched 0
|
||||
rows) is logged at Error level individually, because the method signals all
|
||||
four to callers with the same empty array. The 0-rows case needed switching
|
||||
from `setSQLValue_async` (reports success once `ExecuteNonQueryAsync`
|
||||
completes without throwing, regardless of rows affected) to
|
||||
`setSQLValue_async_result`, whose `AffectedRows` makes a no-op `UPDATE ...
|
||||
WHERE [file] IS NULL` distinguishable from an actual write — this is what
|
||||
made the original incident's failure invisible to every log.
|
||||
four to callers with the same empty array. The 0-rows case is detected via
|
||||
the procs' existing `OUTPUT inserted.[Id]` result set (read with
|
||||
`getSQLDatatable_async`; empty ⇒ the `UPDATE ... WHERE [file] IS NULL`
|
||||
matched nothing). Rows-affected is **not** usable for this: both procs run
|
||||
`SET NOCOUNT ON`, so `ExecuteNonQuery` returns -1 whether or not the
|
||||
`UPDATE` matched, which is why `setSQLValue_async` could never distinguish
|
||||
a no-op from a write and the original incident was invisible to every log.
|
||||
|
||||
## Consequences
|
||||
|
||||
|
||||
Reference in New Issue
Block a user