Null
Constant functions null
The keyword Null is used in the GeoDMS to indicate missing data.
The constant: null_suffix can be used in expressions, it is important to use the version with the correct Value type, see the following table:
| Value type | Name |
|---|---|
| Uint8 | null_b |
| Uint16 | null_w |
| Uint32 | null_u |
| Uint64 | null_u64 |
| Int8 | null_c |
| Int16 | null_s |
| Int32 | null_i |
| Int64 | null_i64 |
| Float32 | null_f |
| Float64 | null_d |
| Spoint | null_sp |
| Wpoint | null_wp |
| Ipoint | null_ip |
| Upoint | null_up |
| Fpoint | null_fp |
| Dpoint | null_dp |
syntax
- null_suffix
definition
null_suffix results in the null data value for the Value type defined by the suffix.
applies to
Data item with the value type defined by the suffix.
since
version 14.3.0
example
parameter<float32> param_null_f := null_f;
result: param_null_f = null
Internal sentinel encoding (and a worked debugging example)
Each value type encodes “null” with a fixed bit pattern. Knowing the exact pattern matters when diagnosing “I expected NULL but got a defined-looking value” bugs, because a sentinel from one type reinterpreted as another can produce surprising defined values.
| Value type | NULL sentinel (bits) | If reinterpreted as same-width signed Int |
|---|---|---|
| UInt8 | 0xFF | −1 |
| UInt16 | 0xFFFF | −1 |
| UInt32 | 0xFFFFFFFF | −1 |
| UInt64 | 0xFFFF…FFFF | −1 |
| Int8 | 0x80 = INT8_MIN | −128 |
| Int16 | 0x8000 = INT16_MIN | −32 768 |
| Int32 | 0x80000000 = INT32_MIN | −2 147 483 648 |
| Float32 / Float64 | NaN | n/a |
| 2D points (spoint, ipoint, fpoint, dpoint, …) | both components = the type’s NULL sentinel | see signed/unsigned components above |
Note that the NULL pattern for unsigned integer types is −1 (all-ones), while for signed integer types it is INT_MIN (sign bit only). If a NULL written as one type is byte-reinterpreted as the other, the resulting value is defined (and meaningless): a UInt32 NULL becomes Int32 −1, an Int32 NULL becomes UInt32 2 147 483 648, etc.
Worked example: diagnosing a NULL-loss bug with @statistics
In May 2026, the t060 BAG snapshot regression was producing 407 301 of 1 138 872 adres rows at coordinate (−0.001, −0.001) instead of the proper “no geometry” of the 19.x reference (which had POINT EMPTY / NaN). The cfg uses an rdc_mm ipoint grid (cell size 0.001 m, origin at 0), so the dpoint (−0.001, −0.001) corresponds to the ipoint (−1, −1).
−1 is not the Int32 NULL sentinel (INT32_MIN = −2 147 483 648). It is what you get when you reinterpret 0xFFFFFFFF (the UInt32 NULL sentinel) through an Int32 lens. So the working hypothesis was:
Somewhere in the rjoin chain, a UInt32 NULL row-relation is being byte-cast through to the result point’s Int32 components instead of being translated to per-component INT32_MIN.
To confirm without re-writing the gpkg every iteration, diagnostic intermediates were added to adres with DisableStorage = "True":
attribute<bool> dbg_vbo_isNull := IsNull(impl/nummeraanduiding_met_geom/vbo_point), DisableStorage = "True";
attribute<bool> dbg_lp_src_isNull := IsNull(impl/nummeraanduiding_met_geom/lp/point_src), DisableStorage = "True";
attribute<bool> dbg_geom_mm_isNull := IsNull(geometry_mm), DisableStorage = "True";
attribute<int32> dbg_geom_X_mm := PointCol(geometry_mm), DisableStorage = "True";
attribute<int32> dbg_geom_Y_mm := PointRow(geometry_mm), DisableStorage = "True";
Then queried via GeoDmsRun … @statistics …:
GeoDmsRun.exe /S1 /S2 /S3 cfg.dms @file diag.txt @statistics ^
.../adres/dbg_vbo_isNull ^
.../adres/dbg_lp_src_isNull ^
.../adres/dbg_geom_mm_isNull ^
.../adres/dbg_geom_X_mm ^
.../adres/dbg_geom_Y_mm
Output (abridged):
dbg_vbo_isNull True: 0 False: 1,138,872
dbg_lp_src_isNull True: 0 False: 1,138,872
dbg_geom_mm_isNull True: 0 False: 1,138,872
dbg_geom_X_mm Minimum: -1 Maximum: 171,423,377 #Nulls: 0
dbg_geom_Y_mm Minimum: -1 Maximum: 479,558,859 #Nulls: 0
Reading these:
vbo_pointwas expected NULL for orphan addresses (no matching verblijfsobject); it’s never NULL.lp/point_srcwas expected NULL when the rjoin can’t find a matching ligplaats; it’s never NULL either.dbg_geom_X_mm’s minimum is−1, notINT32_MIN— and#Nulls = 0. The hypothesis is confirmed: the rjoin’s no-match path is producing defined ipoint components of−1, which is0xFFFFFFFF(UInt32 NULL) reinterpreted as Int32.
The bug is therefore in rjoin’s handling of the no-match case for point-typed source attributes: the internal UInt32 row-relation NULL bytes are flowing through to the Int32 point components instead of being translated to per-component INT32_MIN. Downstream MakeDefined(vbo_point, lp/point, sp/point) then picks the (−1, −1) “defined” value over the genuinely NULL fallbacks, and the gpkg writer emits POINT(−0.001 −0.001) instead of POINT EMPTY.
Lesson: When debugging NULL propagation, @statistics on IsNull(...)-typed and component-extracted (PointCol / PointRow) intermediates is a very fast way to locate where a NULL gets converted to a defined-but-garbage value. Compare the observed minimum against the table above to identify which type’s NULL sentinel was reinterpreted.