nullable-integers-and-never-blank-text¶
Date: 2026-09-21. Status: implemented.
Decision. The writers now carry two kinds of column the tables never had. A nullable integer is written to Parquet as a null, rendered in the CSV as an empty cell and in the digest as the null marker, and restored on import with NULLIF, never as zero. A nullable text column that is never blank is named under empty_is_null in the export contract, and the import restores its empty cells as nulls; every other text column keeps its existing rule, blank or structural null by record type.
Reason. The rurality codes are the first nullable integers and their statuses the first text that is either a value or null. Without the rules an empty code would load as 0, a class that does not exist, and an empty status would load as a blank, which the digest would then report as corruption of a faithful load.
Evidence. Verified on SQL Server 2022 (16.0, RTM-CU24-GDR) in the local container with the shipped import and check scripts: 230,103 and 247,773 rows, all ten checks pass, history digest -3244194952865985434297 over 185 exported columns equal to the manifest's. The main table's digest, 600732040592218046898, is unchanged. tests/test_exports.py requires the NULLIF guard on every nullable integer and the rule on every empty_is_null column.
Columns (27): urbanrural2003_2004_6fold, urbanrural2003_2004_8fold, urbanrural2003_2004_status, urbanrural2005_2006_6fold, urbanrural2005_2006_8fold, urbanrural2005_2006_status, urbanrural2007_2008_6fold, urbanrural2007_2008_8fold, urbanrural2007_2008_status, urbanrural2009_2010_6fold, urbanrural2009_2010_8fold, urbanrural2009_2010_status, urbanrural2011_2012_6fold, urbanrural2011_2012_8fold, urbanrural2011_2012_status, urbanrural2013_2014_6fold, urbanrural2013_2014_8fold, urbanrural2013_2014_status, urbanrural2016_6fold, urbanrural2016_8fold, urbanrural2016_status, urbanrural2020_6fold, urbanrural2020_8fold, urbanrural2020_status, urbanrural2022_6fold, urbanrural2022_8fold, urbanrural2022_status