6#include "DataMapper/DataMapper.hpp"
7#include "ProgressTask.hpp"
9#include "SqlQuery/Migrate.hpp"
10#include "SqlQuery/MigrationPlan.hpp"
11#include "SqlSchema.hpp"
12#include "SqlTransaction.hpp"
93 std::size_t stepIndex,
94 std::string failedSql,
126 return _driverMessage;
133 std::size_t _stepIndex;
134 std::string _failedSql;
135 std::string _driverMessage;
244 "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
267 std::uint64_t
id = 0;
358 SqlException(std::move(info)),
359 _unitNumber { unitNumber },
360 _unitCount { unitCount }
377 std::size_t _unitNumber;
378 std::size_t _unitCount;
381 class MigrationManager;
440 std::runtime_error(std::format(
"Pre-migrate step \"{}\" failed: {}", stepTitle, cause)),
441 _stepTitle { std::move(stepTitle) }
446 [[nodiscard]] std::string
const&
StepTitle() const noexcept
452 std::string _stepTitle;
515 std::function<
void(
MigrationBase const& ,
size_t ,
size_t )>;
585 [[nodiscard]] LIGHTWEIGHT_API std::string_view
DefaultSchema() const noexcept;
627 using
LogSink = std::function<
void(std::string_view)>;
639 LIGHTWEIGHT_API
void Log(std::string_view message) const;
765 std::vector<MigrationTimestamp>
800 std::map<SqlSchema::FullyQualifiedTableName, TableState>
tables;
848 SqlQueryFormatter const& formatter, std::optional<MigrationTimestamp> upToInclusive = std::nullopt)
const;
894 SqlSchema::FullyQualifiedTableName
table;
928 std::vector<std::vector<SqlSchema::FullyQualifiedTableName>>
units;
1029 [[nodiscard]] LIGHTWEIGHT_API std::vector<ChecksumVerificationResult>
VerifyChecksums()
const;
1090 [[nodiscard]] LIGHTWEIGHT_API std::vector<MigrationRelease>
const&
GetAllReleases() const noexcept;
1151 void PersistVirtualAppliedMigrations();
1155 void ReportSkippedRows(std::vector<std::
string> const& skippedRows);
1158 struct RegisteredPreMigrateStep
1166 std::vector<MigrationRelease> _releases;
1167 std::unique_ptr<DataMapper> _dataMapper;
1169 std::uint32_t _dataMapperGeneration {};
1171 std::string _defaultSchema;
1179 std::vector<MigrationTimestamp> _virtualAppliedIds;
1183 std::size_t _skippedExistingRowCount {};
1186 std::vector<RegisteredPreMigrateStep> _preMigrateSteps;
1192#define LIGHTWEIGHT_MIGRATION_PLUGIN() \
1194 extern "C" LIGHTWEIGHT_EXPORT Lightweight::SqlMigration::MigrationManager* AcquireMigrationManager() \
1196 return &Lightweight::SqlMigration::MigrationManager::GetInstance(); \
1203#define LIGHTWEIGHT_DETAIL_MIGRATION_PLUGIN_HOOK(entryPoint, body, connArg, mgrArg) \
1204 static void body(Lightweight::SqlConnection&, Lightweight::SqlMigration::MigrationManager&); \
1205 extern "C" LIGHTWEIGHT_EXPORT bool entryPoint(Lightweight::SqlConnection& connection, \
1206 Lightweight::SqlMigration::MigrationManager& manager, \
1207 Lightweight::SqlMigration::PluginHookErrorSink reportError, \
1208 void* errorContext) noexcept \
1212 body(connection, manager); \
1215 catch (std::exception const& exception) \
1217 if (reportError != nullptr) \
1218 reportError(errorContext, exception.what()); \
1222 if (reportError != nullptr) \
1223 reportError(errorContext, "unknown exception"); \
1227 static void body(Lightweight::SqlConnection&(connArg), Lightweight::SqlMigration::MigrationManager&(mgrArg))
1251#define LIGHTWEIGHT_MIGRATION_PLUGIN_POSTINIT(connArg, mgrArg) \
1252 LIGHTWEIGHT_DETAIL_MIGRATION_PLUGIN_HOOK( \
1253 LightweightMigrationPluginPostInitReported, LightweightMigrationPluginPostInitBody, connArg, mgrArg)
1276#define LIGHTWEIGHT_MIGRATION_PLUGIN_POSTMIGRATE(connArg, mgrArg) \
1277 LIGHTWEIGHT_DETAIL_MIGRATION_PLUGIN_HOOK( \
1278 LightweightMigrationPluginPostMigrate, LightweightMigrationPluginPostMigrateBody, connArg, mgrArg)
1285 using PluginHookErrorSink = void (*)(
void* context,
char const* message)
noexcept;
1288 using PostMigrateErrorSink = PluginHookErrorSink;
1304 _timestamp { timestamp },
1362 [[nodiscard]]
virtual std::string_view
GetAuthor() const noexcept
1398 [[nodiscard]] std::string_view
GetTitle() const noexcept
1415 std::string_view _title;
1441 template <u
int64_t TsValue>
1456 Migration(std::string_view title,
1459 MigrationBase(TimeStamp, title),
1471 Migration(std::string_view title,
1472 MigrationMetadata metadata,
1473 std::function<
void(SqlMigrationQueryBuilder&)>
const& up,
1474 std::function<
void(SqlMigrationQueryBuilder&)>
const& down = {}):
1478 _metadata { std::move(metadata) }
1485 void Up(SqlMigrationQueryBuilder& builder)
const override
1493 void Down(SqlMigrationQueryBuilder& builder)
const override
1502 [[nodiscard]]
bool HasDownImplementation() const noexcept
override
1504 return static_cast<bool>(_down);
1508 [[nodiscard]] std::vector<MigrationTimestamp> GetDependencies()
const override
1510 return _metadata.dependencies;
1514 [[nodiscard]] std::string_view GetAuthor() const noexcept
override
1516 return _metadata.author;
1520 [[nodiscard]] std::string_view GetDescription() const noexcept
override
1522 return _metadata.description;
1526 [[nodiscard]]
bool RequiresBackup() const noexcept
override
1528 return _metadata.requiresBackup;
1532 std::function<void(SqlMigrationQueryBuilder&)> _up;
1533 std::function<void(SqlMigrationQueryBuilder&)> _down;
1534 MigrationMetadata _metadata {};
1549 template <
typename T>
1550 inline constexpr MigrationTimestamp TimestampOf = T::TimeStamp;
1558 struct ReleaseRegistrar
1561 ReleaseRegistrar(std::string version, MigrationTimestamp highestTimestamp, MigrationReleaseOptions options = {})
1567 ReleaseRegistrar(std::string version, std::uint64_t highestTimestamp, MigrationReleaseOptions options = {})
1570 std::move(version), MigrationTimestamp { highestTimestamp }, options);
1579#define _LIGHTWEIGHT_CONCATENATE(s1, s2) s1##s2
1580#define _LIGHTWEIGHT_CONCATENATE_INNER(s1, s2) _LIGHTWEIGHT_CONCATENATE(s1, s2)
1586#define LIGHTWEIGHT_MIGRATION_INSTANCE(timestamp) migration_##timestamp
1606#define LIGHTWEIGHT_SQL_MIGRATION(timestamp, description) \
1607 struct Migration_##timestamp: public Lightweight::SqlMigration::MigrationBase \
1610 static constexpr Lightweight::SqlMigration::MigrationTimestamp TimeStamp { static_cast<uint64_t>(timestamp) }; \
1611 explicit Migration_##timestamp(): \
1612 Lightweight::SqlMigration::MigrationBase(TimeStamp, description) \
1616 void Up(Lightweight::SqlMigrationQueryBuilder& plan) const override; \
1617 void Down(Lightweight::SqlMigrationQueryBuilder& ) const override {} \
1620 static Migration_##timestamp _LIGHTWEIGHT_CONCATENATE(migration_, timestamp); \
1622 void Migration_##timestamp::Up(Lightweight::SqlMigrationQueryBuilder& plan) const
1647#define LIGHTWEIGHT_SQL_MIGRATION_REVERSIBLE(timestamp, description) \
1648 struct Migration_##timestamp: public Lightweight::SqlMigration::MigrationBase \
1650 explicit Migration_##timestamp(): \
1651 Lightweight::SqlMigration::MigrationBase(Lightweight::SqlMigration::MigrationTimestamp { timestamp }, \
1656 void Up(Lightweight::SqlMigrationQueryBuilder& plan) const override; \
1657 void Down(Lightweight::SqlMigrationQueryBuilder& plan) const override; \
1659 [[nodiscard]] bool HasDownImplementation() const noexcept override \
1665 static Migration_##timestamp _LIGHTWEIGHT_CONCATENATE(migration_, timestamp); \
1667 void Migration_##timestamp::Up(Lightweight::SqlMigrationQueryBuilder& plan) const
1677#define LIGHTWEIGHT_SQL_MIGRATION_DOWN(timestamp) \
1678 void Migration_##timestamp::Down(Lightweight::SqlMigrationQueryBuilder& plan) const
1697#define LIGHTWEIGHT_SQL_RELEASE(version, ...) \
1698 static ::Lightweight::SqlMigration::detail::ReleaseRegistrar _LIGHTWEIGHT_CONCATENATE_INNER(_lw_release_, __COUNTER__) \
1700 (version), __VA_ARGS__ \
Main API for mapping records to and from the database using high level C++ syntax.
Progress of one task, which the library reports and a host (a CLI, a GUI) renders.
Represents a connection to a SQL database.
Query builder for building SQL migration queries.
virtual void Up(SqlMigrationQueryBuilder &plan) const =0
MigrationTimestamp GetTimestamp() const noexcept
std::string_view GetTitle() const noexcept
virtual std::vector< MigrationTimestamp > GetDependencies() const
virtual std::string_view GetAuthor() const noexcept
virtual std::string_view GetDescription() const noexcept
virtual bool RequiresBackup() const noexcept
MigrationBase(MigrationBase const &)=default
Default copy constructor.
LIGHTWEIGHT_API std::string ComputeChecksum(SqlQueryFormatter const &formatter) const
MigrationBase(MigrationTimestamp timestamp, std::string_view title)
Constructs a migration with the given timestamp and title.
virtual void Down(SqlMigrationQueryBuilder &plan) const
virtual bool HasDownImplementation() const noexcept
MigrationBase & operator=(MigrationBase const &)=default
Default copy assignment operator.
std::string const & GetMigrationTitle() const noexcept
Human-readable title of the failing migration.
Operation GetOperation() const noexcept
Whether the failure occurred while applying or reverting.
std::string const & GetFailedSql() const noexcept
The exact SQL statement that the driver rejected.
std::string const & GetDriverMessage() const noexcept
Operation
Whether the failure happened while applying (Up) or reverting (Down).
MigrationTimestamp GetMigrationTimestamp() const noexcept
Timestamp of the failing migration.
LIGHTWEIGHT_API MigrationException(Operation operation, MigrationTimestamp timestamp, std::string title, std::size_t stepIndex, std::string failedSql, SqlErrorInfo driverError)
std::size_t GetStepIndex() const noexcept
Zero-based step index inside the plan of the failing migration.
LIGHTWEIGHT_API void MarkMigrationAsApplied(MigrationBase const &migration)
LIGHTWEIGHT_API void ComposeCompatPolicy(CompatPolicy policy)
Composes an additional policy with the currently installed one. If no policy is installed,...
std::function< void(MigrationBase const &, size_t, size_t)> ExecuteCallback
Callback type invoked during migration execution to report progress.
LIGHTWEIGHT_API DataMapper & GetDataMapper()
LIGHTWEIGHT_API RevertResult RevertToMigration(MigrationTimestamp target, ExecuteCallback const &feedbackCallback={})
LIGHTWEIGHT_API void AddMigration(MigrationBase const *migration)
std::list< MigrationBase const * > MigrationList
Type alias for a list of migration pointers.
LIGHTWEIGHT_API MigrationList const & GetAllMigrations() const noexcept
LIGHTWEIGHT_API void SetDefaultSchema(std::string schema)
Sets the default schema applied to migration connections.
LIGHTWEIGHT_API void RemoveAllMigrations()
LIGHTWEIGHT_API std::vector< std::string > PreviewPendingMigrationsUpTo(MigrationTimestamp targetInclusive, ExecuteCallback const &feedbackCallback={}) const
LIGHTWEIGHT_API size_t ApplyPendingMigrationsUpTo(MigrationTimestamp targetInclusive, ExecuteCallback const &feedbackCallback={})
LIGHTWEIGHT_API std::vector< std::string > PreviewPendingMigrations(ExecuteCallback const &feedbackCallback={}) const
LIGHTWEIGHT_API void RevertSingleMigration(MigrationBase const &migration)
std::function< void(std::string_view)> LogSink
LIGHTWEIGHT_API void CloseDataMapper()
LIGHTWEIGHT_API std::string_view DefaultSchema() const noexcept
Returns the default schema currently installed on the manager (empty when unset).
LIGHTWEIGHT_API void RemoveAllReleases()
Remove all registered releases. Useful for resetting state in tests.
LIGHTWEIGHT_API PlanFoldingResult FoldRegisteredMigrations(SqlQueryFormatter const &formatter, std::optional< MigrationTimestamp > upToInclusive=std::nullopt) const
Pure plan-walk that folds the effect of every registered migration.
LIGHTWEIGHT_API SqlTransaction Transaction()
LIGHTWEIGHT_API MigrationBase const * GetMigration(MigrationTimestamp timestamp) const noexcept
LIGHTWEIGHT_API std::set< std::string > CompatFlagsFor(MigrationBase const &migration) const
Returns the compat flags the current policy assigns to migration, or an empty set if no policy is ins...
LIGHTWEIGHT_API std::vector< ChecksumVerificationResult > VerifyChecksums() const
LIGHTWEIGHT_API MigrationRelease const * FindReleaseByVersion(std::string_view version) const noexcept
LIGHTWEIGHT_API RewriteChecksumsResult RewriteChecksums(bool dryRun=false)
Re-stamps schema_migrations.checksum rows that have drifted.
LIGHTWEIGHT_API CompatPolicy const & GetCompatPolicy() const noexcept
Returns a view of the installed policy so it can be propagated across managers (plugin → central)....
LIGHTWEIGHT_API void RemoveMigration(MigrationBase const *migration) noexcept
LIGHTWEIGHT_API HardResetResult HardReset(ExecutionMode mode=ExecutionMode::Execute)
Drops every table the registered migrations would create (incl. schema_migrations),...
LIGHTWEIGHT_API void RegisterRelease(std::string version, MigrationTimestamp highestTimestamp, MigrationReleaseOptions options={})
LIGHTWEIGHT_API MigrationRelease const * FindReleaseForTimestamp(MigrationTimestamp timestamp) const noexcept
LIGHTWEIGHT_API MigrationList GetMigrationsForRelease(std::string_view version) const
LIGHTWEIGHT_API std::vector< std::string > PreviewMigration(MigrationBase const &migration) const
LIGHTWEIGHT_API size_t ApplyPendingMigrations(ExecuteCallback const &feedbackCallback={})
LIGHTWEIGHT_API UnicodeUpgradeResult UnicodeUpgradeTables(ExecutionMode mode=ExecutionMode::Execute, ProgressTask *progress=nullptr)
Rewrites legacy VARCHAR/CHAR columns to NVARCHAR/NCHAR where the registered migrations now declare wi...
LIGHTWEIGHT_API void CreateMigrationHistory()
Create the migration history table if it does not exist.
LIGHTWEIGHT_API MigrationStatus GetMigrationStatus() const
LIGHTWEIGHT_API std::list< MigrationBase const * > GetPending() const
LIGHTWEIGHT_API void AddVirtualAppliedMigrations(std::span< MigrationTimestamp const > timestamps)
LIGHTWEIGHT_API void SetCompatPolicy(CompatPolicy policy)
Installs a per-migration compat policy. Pass {} to clear.
LIGHTWEIGHT_API void SetLogSink(LogSink sink)
LIGHTWEIGHT_API void ValidateDependencies() const
LIGHTWEIGHT_API void AddPreMigrateStep(std::string title, PreMigrateStepOptions options, PreMigrateStep step)
LIGHTWEIGHT_API PreMigrateStepsOutcome RunPreMigrateSteps(ProgressTask &parent, ExecutionMode mode)
std::function< std::set< std::string >(MigrationBase const &)> CompatPolicy
Per-migration compat policy.
LIGHTWEIGHT_API void RemoveAllPreMigrateSteps() noexcept
Removes every pre-migrate step.
LIGHTWEIGHT_API void Log(std::string_view message) const
LIGHTWEIGHT_API std::vector< MigrationRelease > const & GetAllReleases() const noexcept
Get all registered releases, sorted ascending by highestTimestamp.
LIGHTWEIGHT_API void ApplySingleMigration(MigrationBase const &migration)
LIGHTWEIGHT_API std::size_t SkippedExistingRowCount() const noexcept
Number of rows Insert(...).IfNotExists(...) steps skipped because a row with the same key values alre...
LIGHTWEIGHT_API std::vector< std::string > PreviewMigrationWithContext(MigrationBase const &migration, MigrationRenderContext &context) const
Variant of PreviewMigration that threads a caller-owned render context so the column-width cache accu...
static LIGHTWEIGHT_API MigrationManager & GetInstance()
LIGHTWEIGHT_API std::vector< MigrationTimestamp > GetAppliedMigrationIds() const
LIGHTWEIGHT_API void ClearVirtualAppliedMigrations()
LIGHTWEIGHT_API std::vector< PreMigrateStepInfo > GetPreMigrateSteps() const
LIGHTWEIGHT_API BackupRequirement BackupRequirementFor(std::span< MigrationTimestamp const > toRun)
PreMigrateStepError(std::string stepTitle, std::string_view cause)
std::string const & StepTitle() const noexcept
std::size_t UnitNumber() const noexcept
The failed unit, counted from 1; the units before it are committed.
std::size_t UnitCount() const noexcept
The number of units of the upgrade.
UnicodeUpgradeUnitError(SqlErrorInfo info, std::size_t unitNumber, std::size_t unitCount)
std::variant< SqlCreateTablePlan, SqlAlterTablePlan, SqlDropTablePlan, SqlCreateIndexPlan, SqlRawSqlPlan, SqlInsertDataPlan, SqlUpdateDataPlan, SqlDeleteDataPlan > SqlMigrationPlanElement
Represents a single SQL migration plan element.
LIGHTWEIGHT_API bool BackupRecordCovers(BackupRecord const &record, MigrationHistoryState const &state) noexcept
LIGHTWEIGHT_API std::optional< BackupRecord > ReadLatestBackupRecord(DataMapper &dataMapper)
constexpr std::string_view BackupRecordTableName
constexpr std::string_view EmptyMigrationHistoryDigest
std::function< void(SqlConnection &, MigrationManager &, ProgressTask &, ExecutionMode)> PreMigrateStep
LIGHTWEIGHT_API std::expected< BackupRecord, SqlErrorInfo > WriteBackupRecord(DataMapper &dataMapper, BackupRecord record)
LIGHTWEIGHT_API bool IsValidSchemaName(std::string_view schema) noexcept
LIGHTWEIGHT_API MigrationHistoryState ReadMigrationHistoryState(DataMapper &dataMapper)
@ Execute
Carry the operation out.
@ DryRun
Report the plan and change nothing.
@ Completed
Every step ran; the migrations may follow.
@ Cancelled
The task asked to cancel; the caller applies no migration.
@ Backup
A verified backup archive was written; stored as backup.
Opt-in behavioural knobs that the ToSql migration-plan renderer honours.
Represents an ODBC SQL error.
MigrationHistoryState observedState
BackupRecordKind kind
What the record documents.
std::string archiveFileName
UTF-8, without its directory.
std::string archiveSha256
SHA-256 of the archive file, lowercase hex.
std::string toolVersion
The tool that wrote the record, e.g. dbtool 1.2.3.
std::vector< std::string > reasons
What demands it: release <version>, migration <timestamp> "<title>" and step "<title>",...
bool required
Whether anything demands a backup.
bool satisfied
Whether nothing is required or the newest backup or restore record covers the database's current stat...
std::optional< BackupRecord > lastBackup
The newest backup or restore record; waived records are skipped.
MigrationTimestamp timestamp
The timestamp of the verified migration.
std::string computedChecksum
The checksum computed from the current migration definition.
std::string storedChecksum
The checksum stored in the database.
bool matches
Whether the stored and computed checksums match.
std::string_view title
The title of the verified migration.
std::optional< SqlDateTime > lastAppliedAt
The latest applied_at over all recorded migrations; empty when none carries a time....
std::optional< MigrationTimestamp > lastAppliedMigration
Highest applied version; empty when none is recorded. For messages.
One row in the RewriteChecksumsResult.entries list.
std::string_view title
Title of the registered migration (or "(Unknown Migration)").
MigrationTimestamp timestamp
The migration whose stored checksum was rewritten.
std::string oldChecksum
Stored checksum before the rewrite. Empty if there was none.
std::string newChecksum
Stored checksum after the rewrite.
One column upgrade entry in UnicodeUpgradeResult.
SqlColumnTypeDefinition liveType
Live byte-counted type the column currently has.
SqlSchema::FullyQualifiedTableName table
Fully-qualified name of the table that owns the column.
std::string joinedByForeignKeyTo
SqlColumnTypeDefinition intendedType
Char-counted type the migrations now declare for this column.
bool nullable
Whether the column is nullable (preserved across the type rewrite).
std::string column
Name of the column being upgraded.
Result of a HardReset call.
bool schemaBackupsDropped
std::vector< SqlSchema::FullyQualifiedTableName > absentTables
std::vector< SqlSchema::FullyQualifiedTableName > preservedTables
std::vector< SqlSchema::FullyQualifiedTableName > droppedTables
bool schemaMigrationsDropped
One data step (INSERT/UPDATE/DELETE/RawSql) tagged with its source migration.
std::string sourceTitle
Title of the migration that contributed this data step.
SqlMigrationPlanElement element
The plan element itself (INSERT, UPDATE, DELETE, or RawSql).
MigrationTimestamp sourceTimestamp
Timestamp of the migration that contributed this data step.
Per-table state: ordered column declarations + per-table FK list.
std::vector< SqlColumnDeclaration > columns
Ordered column declarations as they would be emitted in CREATE TABLE.
std::vector< SqlCompositeForeignKeyConstraint > compositeForeignKeys
Snapshot of the schema the registered migrations intend to produce.
std::vector< SqlCreateIndexPlan > indexes
Indexes that survive folding (created on tables still present at end).
std::map< SqlSchema::FullyQualifiedTableName, TableState > tables
std::vector< MigrationRelease > releases
Releases declared via LIGHTWEIGHT_SQL_RELEASE that fall within the fold range.
std::vector< DataStep > dataSteps
std::vector< SqlSchema::FullyQualifiedTableName > creationOrder
std::vector< std::pair< MigrationTimestamp, std::string > > foldedMigrations
Result of a RewriteChecksums call.
bool wasDryRun
True if the call ran in dry-run mode.
std::vector< MigrationTimestamp > unregisteredTimestamps
Applied rows whose migration is no longer registered.
std::vector< ChecksumRewriteEntry > entries
One entry per rewritten or would-be-rewritten row.
Result of an UnicodeUpgradeTables call.
std::size_t committedUnitCount
std::vector< ColumnUpgradeEntry > columns
std::vector< std::string > rebuiltObjects
std::vector< SqlCompositeForeignKeyConstraint > rebuiltForeignKeys
std::vector< std::vector< SqlSchema::FullyQualifiedTableName > > units
bool requiresBackup
Shorthand for "every migration of the release requires a backup".
MigrationTimestamp highestTimestamp
Highest migration timestamp contained in this release (inclusive).
std::string version
Human-readable version string, e.g. "6.7.0".
size_t mismatchCount
Number of applied migrations with checksum mismatches.
size_t appliedCount
Number of migrations that have been applied.
size_t totalRegistered
Total number of registered migrations.
size_t pendingCount
Number of migrations waiting to be applied.
size_t unknownAppliedCount
Number of applied migrations not found in registered list.
constexpr std::weak_ordering operator<=>(MigrationTimestamp const &other) const noexcept=default
Three-way comparison operator.
uint64_t value
The numeric timestamp value identifying the migration.
std::string title
The step's title, as its task shows it.
bool declaresBackupRequirement
Whether the step has a requiresBackup predicate.
std::function< bool(SqlConnection &, MigrationManager &)> requiresBackup
std::vector< MigrationTimestamp > revertedTimestamps
Successfully reverted migrations.
std::optional< MigrationTimestamp > failedAt
Migration that failed, if any.
std::string sqlState
SQLSTATE diagnostic code from the driver, if available.
std::string errorMessage
Short error message if failed (driver message only)
std::size_t failedStepIndex
SQLINTEGER nativeErrorCode
Native driver error code, if available.