sqlite: Exceptions on conflict handling with database.applyChangeset()
#56210
Labels
sqlite
Issues and PRs related to the SQLite subsystem.
database.applyChangeset()
#56210
Refs: #54181
The SQLite session extension provides for conflict resolution when applying changesets. The way this works within
sqlite3changeset_apply()
is:xConflict
callback is called with a conflict codeeConflict
xConflict
callback returns one of the resolution codesSQLITE_CHANGESET_OMIT
,SQLITE_CHANGESET_ABORT
orSQLITE_CHANGESET_REPLACE
sqlite3changeset_apply()
applies the requested resolutionTo create an
xConflict
callback, theonConflict
option passed todatabase.applyChangeset()
is transformed into a simple lambda which ignoreseConflict
entirely and always returns the given resolution code:node/src/node_sqlite.cc
Line 482 in 552a182
This is mostly fine, except where
SQLITE_CHANGESET_REPLACE
is concerned.As per the documentation, this code may only be returned where the
eConflict
passed to the callback isSQLITE_CHANGESET_DATA
orSQLITE_CHANGESET_CONFLICT
. The current implementation does not honour this, and so will result in the function aborting, rolling back and returningSQLITE_MISUSE
if it is asked to deal with a different type of conflict. Minimal repro:In this instance, the conflict code passed to
xConflict
isSQLITE_CHANGESET_NOTFOUND
, and replying withSQLITE_CHANGESET_REPLACE
is illegal. The function aborts and returnsSQLITE_MISUSE
, and this results in an error being thrown byDatabaseSync::ApplyChangeset()
.In addition, there is an issue with using
CHECK_ERROR_OR_THROW
here, because aborted returns fromsqlite3changeset_apply()
that arise from within the changeset functions' internal logic (rather than as a result of an unsuccessful database operation) don't set an error code on the database handle.This is the cause of the nonsensical error message in the example, as
CHECK_ERROR_OR_THROW
ends up setting the properties of the thrown error by interrogating the database handle, not by using the error code passed to the macro.This is slightly small-print, but not all errors returned by the changeset functions are associated with database operations (eg.
SQLITE_NOMEM
). It's not clear to me how important it would be to handle these cases, or indeed whether similar cases arise in other areas of node_sqlite.In terms of things to address:
options.onConflict == SQLITE_CHANGESET_REPLACE
but the conflict code isn't one that accepts this response.SQLITE_CHANGESET_OMIT
instead in these cases?CHECK_ERROR_OR_THROW
needs to account for errors which aren't associated with the database handle.The text was updated successfully, but these errors were encountered: