Surfaced by the column-type coverage added in #579, where it is asserted as-is and labelled KNOWN DEFECT so it does not go unnoticed.
Symptom
On MS SQL Server, a column declared through the migration query builder as
migration.CreateTable("Example")
.PrimaryKeyWithAutoIncrement("id")
.RequiredColumn("timestampColumn", Timestamp {});
comes back out of the catalog as an 8-byte binary column, and ddl2cpp generates Light::SqlDynamicBinary<8> for it instead of Light::SqlDateTime. SQLite and PostgreSQL produce Light::SqlDateTime as expected.
Cause
SqlServerFormatter::ColumnType emits the type name verbatim:
// src/Lightweight/QueryFormatter/SqlServerFormatter.hpp:356
[](Timestamp const&) -> std::string { return "TIMESTAMP"; },
On SQL Server, TIMESTAMP is not a point in time. It is a deprecated synonym for rowversion: an 8-byte, server-generated, non-writable binary counter that changes on every update of the row. So the user asks for a timestamp and gets a read-only binary column.
Impact
- A
Timestamp {} column on SQL Server cannot be written to at all — inserts and updates targeting it are rejected by the server.
- Round-tripping such a schema through
ddl2cpp produces a binary record member, so the C++ side inherits the wrong type too.
- The type is deprecated by Microsoft, so the spelling is on borrowed time regardless.
Suggested fix
Emit DATETIME2 for Timestamp {} in SqlServerFormatter::ColumnType. That is the natural counterpart to what the other two backends store, and it is what DateTime {} already maps to.
Worth deciding as part of the fix: whether existing SQL Server schemas created with the current spelling need a migration path, and whether the schema reader should keep recognising rowversion/timestamp columns as binary when reading third-party schemas (it should — that is a correct reading of what the column actually is).
Verification
src/tests/Ddl2CppColumnTypeTests.cpp (from #579) has a dedicated ColumnTypeTimestamp case that currently branches on SqlServerType::MICROSOFT_SQL to expect Light::SqlDynamicBinary<8>. Once fixed, that branch should collapse to the single unconditional Light::SqlDateTime expectation.
Related: #191, #579
Surfaced by the column-type coverage added in #579, where it is asserted as-is and labelled
KNOWN DEFECTso it does not go unnoticed.Symptom
On MS SQL Server, a column declared through the migration query builder as
comes back out of the catalog as an 8-byte binary column, and
ddl2cppgeneratesLight::SqlDynamicBinary<8>for it instead ofLight::SqlDateTime. SQLite and PostgreSQL produceLight::SqlDateTimeas expected.Cause
SqlServerFormatter::ColumnTypeemits the type name verbatim:On SQL Server,
TIMESTAMPis not a point in time. It is a deprecated synonym forrowversion: an 8-byte, server-generated, non-writable binary counter that changes on every update of the row. So the user asks for a timestamp and gets a read-only binary column.Impact
Timestamp {}column on SQL Server cannot be written to at all — inserts and updates targeting it are rejected by the server.ddl2cppproduces a binary record member, so the C++ side inherits the wrong type too.Suggested fix
Emit
DATETIME2forTimestamp {}inSqlServerFormatter::ColumnType. That is the natural counterpart to what the other two backends store, and it is whatDateTime {}already maps to.Worth deciding as part of the fix: whether existing SQL Server schemas created with the current spelling need a migration path, and whether the schema reader should keep recognising
rowversion/timestampcolumns as binary when reading third-party schemas (it should — that is a correct reading of what the column actually is).Verification
src/tests/Ddl2CppColumnTypeTests.cpp(from #579) has a dedicatedColumnTypeTimestampcase that currently branches onSqlServerType::MICROSOFT_SQLto expectLight::SqlDynamicBinary<8>. Once fixed, that branch should collapse to the single unconditionalLight::SqlDateTimeexpectation.Related: #191, #579