You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* Pull in functional_cpp for its C++11-compatible optional (#2)
fcpp::optional_t<T> is the nullable-field representation: it aliases
std::optional<T> under C++17 and later and falls back to an equivalent
C++11 implementation otherwise. Consumed header-only via FetchContent,
pinned to tag 1.1.1; SOURCE_SUBDIR points at the header directory (which
has no CMakeLists.txt) so only the sources are downloaded, without
building functional_cpp's own library and test targets.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NUt3c1wdseRtRSSXfg3MCK
* Add MEMBER_*_NULLABLE macros and a nullability flag in member metadata (#2)
Each field macro gains a _NULLABLE variant declaring the member as
fcpp::optional_t of the underlying type (std::optional under C++17+).
MemberMetadata carries an orthogonal nullable flag - storage_class keeps
describing the underlying contained type - set via the new
DEFINE_MEMBER_NULLABLE during registration, so query generation, binding,
and hydration can branch on it. No behavior change yet for existing
records: the flag defaults to false everywhere.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NUt3c1wdseRtRSSXfg3MCK
* Round-trip SQL NULL for nullable members across schema, read, write, and predicates (#2, #20)
Write side: SqlValue gains an is_null state bound via sqlite3_bind_null;
GetValues reads a nullable member through its optional and produces a null
SqlValue when it is empty.
Read side (the #20 fix): HydrateCurrentRow hydrates a nullable member's
SQL NULL into an empty optional instead of skipping it, and a present
value into the contained value - an empty TEXT becomes a contained empty
string, distinct from NULL, ending the NULL/empty conflation. Non-nullable
members keep today's exact skip semantics, including the NULL/BLOB
accessor-safety skip.
Schema: CreateTableQuery emits NOT NULL for non-nullable columns and omits
it for nullable ones (id keeps PRIMARY KEY AUTOINCREMENT, which is
implicitly NOT NULL). Only newly created tables are affected, since tables
are created with CREATE TABLE IF NOT EXISTS. The pre-existing
NULL-skip-semantics test now recreates its table the way a legacy database
file would look, which is the very scenario those skip semantics protect.
Predicates: new IsNull()/IsNotNull() emit "<column> IS NULL"/"IS NOT NULL"
with no bound value and survive Clone() inside And()/Or() compounds. Value
predicates on a nullable member compare against the contained value; given
an empty optional they throw std::invalid_argument, since "= NULL" never
matches in SQL - IsNull()/IsNotNull() is the correct tool there.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NUt3c1wdseRtRSSXfg3MCK
* Test nullable fields end to end (#2, #20)
NullableRecord mixes a non-nullable member with a nullable member of every
storage class. Covered: unset fields round-trip as SQL NULL (with IS NULL
matching at the SQL level as raw proof); set fields round-trip equal; NULL
is distinguishable from a stored empty string and stored zero after fetch
(the #20 core case); non-nullable columns reject NULL while nullable ones
accept it (schema NOT NULL, asserted behaviorally); IsNull/IsNotNull select
the right rows, also inside And() compounds; value predicates operate on
the contained value and fail fast on an empty optional; a record without
nullable members round-trips unchanged. Under C++17+ a static_assert pins
that nullable fields are literally std::optional.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NUt3c1wdseRtRSSXfg3MCK
* Document nullable fields in the README (#2)
Covers the MEMBER_*_NULLABLE macros, the fcpp::optional_t representation
(std::optional under C++17+, C++11 fallback otherwise), NULL round-trip
semantics, IsNull/IsNotNull and value-predicate behavior, and the NOT NULL
migration caveat for pre-existing database files, mirroring the existing
AUTOINCREMENT caveat.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NUt3c1wdseRtRSSXfg3MCK
* Bind IsNull predicates to locals before taking their address
Apple Clang errors on &IsNull(...) with -Waddress-of-temporary (taking the
address of a temporary object), failing both macOS CI jobs; GCC only
tolerated it via the project's -fpermissive. Bind each predicate to a named
const local first, matching how every other predicate call site in the
tests already does it.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NUt3c1wdseRtRSSXfg3MCK
* Guard functional_cpp population for CMake older than 3.18
FetchContent's SOURCE_SUBDIR handling was added in CMake 3.18, but the
project's minimum is 3.14; on 3.14-3.17, MakeAvailable would have
add_subdirectory'd functional_cpp's top-level CMakeLists.txt, pulling its
own src/tests into this build. Branch on the CMake version: 3.18+ keeps the
SOURCE_SUBDIR MakeAvailable path, older versions populate the sources via
FetchContent_Populate, which never calls add_subdirectory at all. The
deprecated-in-3.30 Populate call sits behind the VERSION_LESS 3.18 check,
so modern CMake never executes it.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NUt3c1wdseRtRSSXfg3MCK
* Support nullable members in range predicates
GreaterThan, GreaterThanOrEqual, SmallerThan and SmallerThanOrEqual only
exposed int64_t and double member-pointer overloads, so they could not be
used with nullable numeric members at all, even though value predicates on
nullable members are documented to compare against the contained value.
Add fcpp::optional_t<int64_t> and fcpp::optional_t<double> overloads that
route through the existing QueryPredicate constructor, which already
unwraps the optional and fails fast on an empty one.
Also fix the README nullness-predicate example, which took the address of
a temporary (the pattern Clang and MSVC reject), and mention the range
predicates in the nullable value-predicate documentation.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NUt3c1wdseRtRSSXfg3MCK
* Use SaveAutoIncrement in the README nullable example
The example default-constructed an Employee and saved it via Save, which
binds the appended int64_t id without ever writing one back - so the id
stayed indeterminate and the subsequent fetch by e.id read an
uninitialized value. SaveAutoIncrement assigns the id and writes it back
into the object, making the example correct as written.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NUt3c1wdseRtRSSXfg3MCK
---------
Co-authored-by: Claude <noreply@anthropic.com>
0 commit comments