- Added a new SQL script to address the "String or binary data would be truncated" error in mfr sync by widening the Note fields in service objects and companies to NVARCHAR(MAX). - Updated the table types and associated stored procedures to accommodate the changes, ensuring compatibility with existing data. - Queued a re-sync for previously rejected rows due to truncation issues. - Created application logins for fuchs_fds and fuchs_web with placeholder passwords for initial setup. - Introduced functions for invoice backdating and invoice date retrieval to streamline invoice processing. - Developed a password generation function with customizable parameters for length and character types. - Added a random number generation function to support password creation.
5.0 KiB
status, date, applyTo, supersededBy
| status | date | applyTo | supersededBy | |
|---|---|---|---|---|
| Accepted | 2026-09-30 |
|
0036 — The database project build is the T-SQL gate
Context
A rewrite of fds__getDatevExports added a JOIN @inv as i next to fds__invoices as iv
and left an unqualified [file_guid] in the select list. SQL Server rejects that statement
with Msg 209 (ambiguous column name) — but only when it runs, and the working-tree SQL runs
nowhere: the schema lives in the Fuchs_Database SSDT project, the dev database only gets
what is explicitly published to it, and the xUnit suite cannot bind T-SQL. The defect reached
code review as the only check.
The SSDT build would have caught it: it reported the line as SQL71502 ("unresolved reference
… or ambiguous"). Nobody saw that because
- the project did not build at all — the two
CREATE USER … FOR LOGINfiles referenced logins that were not in the model, which SSDT treats as errorSQL71501; - the genuine warning sat among ~160 identical
SQL71502warnings caused by references tosys.*,INFORMATION_SCHEMA,sp_executesql,sp_getapplock(nomasterreference) and toocms_fn_generatePassword, an OCMS helper the project never contained; - a warning does not fail anything.
The same sweep found that ocms_fn_generatePassword (and its dependency ocms_fn_randbetween)
is missing on the dev database as well, so every id-generating function (fds__fn_invoice_id,
fds__fn_reminder_id, fis_fn_id, …) fails there with Msg 4121.
Decision
FuchsDatabase.sqlprojreferences themastersystem dacpac ($(DacPacRootPath)\…\SqlServer\170\SQLSchemas\master.dacpac), so system objects bind.- Everything the SQL references is in the project. The OCMS helpers
ocms_fn_generatePasswordandocms_fn_randbetweenare vendored copies of the OCMSsharpocms_mt_databasefiles.Security/Logins.sqlscripts the two application logins with a placeholder,MUST_CHANGEpassword so the users bind; real passwords stay in Key Vault and SSDT never compares passwords, so existing logins are untouched on publish. TreatTSqlWarningsAsErrorsis on. Suppressed:71501(dynamic SQL / cross-database references) and71558(identifier differs only by case — harmless under theCImodel collation). Every other T-SQL warning, in particularSQL71502, fails the build.- Building the database project is part of the workflow whenever
Fuchs_Database/**changes (msbuild Fuchs_Database/FuchsDatabase.sqlproj -p:Configuration=Debug, from the Visual Studio MSBuild —dotnet buildcannot build the legacy SSDT project format). A red database build is a failure like a red C# build. - The C# side of the contract is a test:
Fuchs.Tests/SqlContractTestsscans every[dbo].[…]named in C# strings (Fuchs,Fuchs_DataService) and checks it against the SSDT files — the object exists, everyEXECUTEbinds (no unknown names, no surplus positional arguments, every parameter without a default supplied), every function call passes the declared argument count, and everymfr__updt__*proc declares the@tblname, @referencetable, @tgtidsignatureFdsMfrClientcalls dynamically.
Consequences
- An unqualified column that becomes ambiguous, a typo in a column name, or a call to a function that is not in the project is a build error before review, not a Msg 209/4121 at a customer.
SELECT *insideEXISTS (…)over joined tables tripsSQL71502in SSDT even though SQL Server accepts it; writeSELECT 1there (done forfds__fn_IntermediateIsAllocatedToOtherandfis_admin_testGroupMembership).- New procedures that use
sys.*/sp_*need no suppression any more. A new reference that is genuinely outside the model (another database, dynamic SQL) is suppressed per statement or by adding the object to the project — never by wideningSuppressTSqlWarningsfor71502. - A proc signature change that breaks a C# caller fails
dotnet test, not the first request. Only literal SQL is covered: dynamically built names (FuchsVisualization's report procs, resolved fromsys.parametersat runtime) and result-set column names are not. - The build proves binding, not behaviour: publishing to the dev database (
SqlPackage /Action:PublishwithExcludeObjectTypes=Logins;Users;Permissions;RoleMembership, or the schema compare) and exercising the code path remains the runtime check. - The dev database needs the two
ocms_*helpers published before any id-generating path works there.
Alternatives considered
- Run the changed procedures against the dev database before review. Needs a publish of every working-tree change first, and does not cover code paths the reviewer does not think to exercise; the compile-time check is free and total.
- Suppress
SQL71502and keep the build warning-free that way. Removes exactly the signal that catches this class of bug. - A C#-side test that parses the SQL for unqualified columns. A parser-level heuristic without a schema cannot decide ambiguity; the SSDT model can.