// when reading, you don't use bind. Sqlite creates a temporary in the memory that it manages. If you want the object to live beyond the next step operation, need to make a copy
// When writing objects, we reinterpret a pointer to a typed object as a blob pointer, when reading them, we need a typed copy, otherwise calling the destructor could be bad.
// We don't want Sqlite3 calling destructors on our objects, hence write them as static, and create them from raw bytes on reading.
virtualstd::span<constuint8_t>Isqlite3_column_blob(int)const=0;// returns the null pointer and zero length if null.
virtualintIsqlite3_column_int(int)const=0;
virtualint64_tIsqlite3_column_int64(int)const=0;
virtualchar*Isqlite3_column_text(int)const=0;// returns pointer to zero length
// string if null. If we need to distinguish betweem zero length strings and nulls, need the
// type function.
// We can store any type in any column, and read any type from any column, but if something
// unexpected is in a column, it gets coerced to the expected type on being read back.
// Thus something stored as a number and read back as blob will come back as the decimal character string.
// It is very rarely valid to store different types in the same column, except that
// null is permissible. The difference between null and zero matters, but the case of
// null is usually dealt with by sql code, not C code.