Garry There's probably not a clean / perfect solution. Here are a few thoughts that I hope might help you decide what to do.
As you know, in .NET System.Data, there's a (singleton?) DBNull.Value that's what comes back from data queries when a column value is NULL. In DataReaders (equivalent of RecordSet) you have to check for e.g. DataReader[columnName].IsDbNull or else check for DBNull.Value. Unfortunately there has to be something like IsDBNull in RecordSet because there's a distinct difference between, say, an integer value of zero and NULL because NULL is saying "uninitialized" or "unknown" or "not set" which is not the same as a potentially deliberately set value of zero; you can't know if it's a default or not. You HAVE to be able to distinguish between the two in many situations.
In my personal opinion I don't see a need for a DBNull.Value equivalent so long as a RecordSet can tell me, along with the column type info it already provides, whether the column is nullable in the DB schema in the first place (IsNullable) and whether the value in a particular row is NULL (IsDBNull). The first would tell me if I need to check for NULL and the second would tell me if a cell is NULL or not. IsNullable could either be a separate property of the column or it could be wrapped in what comes back from ColumNativeType(n) either by appending " NULL" to the type name or returning a TypeInfo-like class rather than a String.
IMO this decouples whether an incoming DB value is NULL from whether the receiving type is nullable in the first place. I don't think the Objo type HAS to be nullable or lose its value semantics just because it might receive NULLs from a database. That is, after all, only one use case for e.g. DateTime. But one just has to detect it when reading the query result, and retain it in some way if it's important to the app. You cannot ask apps to "fly blind" in this regard.
For DateTime specifically I think most devs will find DateTime.Empty / MinValue an acceptable way to retain this info. An actual need to store January first AD 1 is vanishingly rare.
But it's more of a problem with other value types. I suppose e.g. Integer.MinValue could be sacrificed in most cases to stand-in for NULL / Nothing but it will maybe seem awkward to those accustomed to null support.
The biggest problem is Boolean. There, you'd have to have a parallel Boolean to indicate nullility.
I guess this just moves the problem around, it doesn't make it go away. So it boils down to how much of a burden this becomes on the Objo developer. IMO tracking nullility in your own way isn't substantially worse than using nullables or other forms of null support, but what null support brings to the table is a standardized and disciplined way to go about it across any ObjoBasic codebase. It lubricates the exchange of ideas and makes random ObjoBasic code easier to understand and explain because there's a universal way of doing a basic thing.
OTOH C# (and VB.NET) got along without nullables for a long time. They were introduced sometime after .NET 2.0 IIRC. All we had up to then was DBNull.Value and the ability to see the underlying NULL in the incoming data. So I am not advocating a Manhattan Project to implement nullables and don't know that I ever will. I am just arguing for basic support in RecordSet for now. Sort of like IDataReader.IsDbNull without DBNull.Value. I think that will be adequate at least for a time.