SQL REBUILD without options throws NullReferenceException and leaves the engine closed (regression since 5.0.18)
This is the "SQL" bullet of #2824, split out because its cause and fix are independent of the password handling described there. Close it as a duplicate if you prefer to keep everything in #2824.
The SQL statement REBUILD without an options document throws NullReferenceException, and the database is unusable afterwards because the engine has already been closed.
This is a regression since 5.0.18 and affects 5.0.18 to 5.0.21, master and dev.
Reproduction
using var db = new LiteDatabase("data.db");
db.GetCollection("items").Insert(new BsonDocument { ["_id"] = 1 });
db.Execute("REBUILD"); // NullReferenceException
db.GetCollection("items").Count(); // ObjectDisposedException: 'TransactionMonitor'REBUILD { collation: 'en-US/IgnoreCase' } and db.Rebuild() work. Only the statement without options fails.
Cause
SqlParser.ParseRebuild (Rebuild.cs) sets options = null when the statement has no options document and passes that to the engine:
if (next.Type == TokenType.EOF || next.Type == TokenType.SemiColon)
{
options = null;
_tokenizer.ReadToken();
}
...
var diff = _engine.Rebuild(options);LiteEngine.Rebuild(RebuildOptions options) closes the engine first and then hands the options to RebuildService.Rebuild, which dereferences them (options.Errors, options.Collation, options.Password). The exception leaves the engine closed.
Up to 5.0.17 the engine handled null itself (options?.Collation, if (options != null) around the password change), so REBUILD meant "rebuild and keep the current password and collation". 5.0.18 moved the work into RebuildService without that handling.
LiteDatabase.Rebuild(RebuildOptions options = null) does not hit this exception, because it replaces null with new RebuildOptions(). That is not the same thing as "keep what the file has": empty options mean no password, which is the separate problem tracked in #2824. The parameterless LiteEngine.Rebuild() is the overload that reads the current collation and password.
Suggested fix
Treat null in LiteEngine.Rebuild(RebuildOptions) as the parameterless overload, which reads the current collation and password:
if (options == null) return this.Rebuild();That restores the 5.0.17 meaning of REBUILD without options. It has to happen before this.Close(), because the parameterless overload reads a pragma.
A fix with a regression test (Rebuild_Sql_Without_Options_Keeps_Password_Collation_And_Data) is in #2907 as commit 5d33d498. It is independent of the rest of that PR and can be cherry-picked.
How it was found
By a sweep that runs every SQL statement kind as part of the Native AOT validation in #2907. The bug is not related to AOT; it fails the same way under the JIT.
Source: litedb-org/LiteDB