Faithlife / FaithlifeData

Helpers for querying ADO.NET-compatible databases.
https://faithlife.github.io/FaithlifeData/
MIT License
6 stars 4 forks source link

Add support for prepared commands #17

Closed TyMick closed 3 years ago

TyMick commented 3 years ago

Resolves #12.

When writing tests for this one, I searched Faithlife's System.Data.SQLite long and hard for some way to verify that SQLite had actually prepared the statement, trying to find some way to get at the native C methods, but I couldn't come up with anything. So I have unit tests, and I have tests to make sure commands that use the Prepare method actually work, but nothing that "proves" SQLite prepared the statement when requested. Is that sufficient, or is there another direction I should try?

bgrainger commented 3 years ago

As per http://system.data.sqlite.org/index.html/file?name=System.Data.SQLite/SQLiteCommand.cs&ci=trunk, System.Data.SQLite doesn't implement DbCommand.Prepare.

    /// <summary>
    /// Does nothing.  Commands are prepared as they are executed the first time, and kept in prepared state afterwards.
    /// </summary>
    public override void Prepare()
    {
      CheckDisposed();
      SQLiteConnection.Check(_cnn);
    }

Microsoft.Data.Sqlite does (https://github.com/dotnet/efcore/blob/624781d0d904204b330124db25c5937639a9d583/src/Microsoft.Data.Sqlite.Core/SqliteCommand.cs#L237-L240) but I don't know of any way (in that library) to check that a command is prepared.

TyMick commented 3 years ago

I've moved the IDbCommand.Prepare call to the correct location with a

if (IsPrepared && !IsCached)
    command.Prepare();

right before the command is returned in DoCreate, but should we do anything with IsPrepared if we are interacting with the cache? Note it in a newly-created CachedCommand somehow to make sure the command is explicitly prepared before being executed?

TyMick commented 3 years ago

"Getting it out of the cache" is only when cache.TryGetCommand(commandText, out command) is true, right? Here's where I've set wasCached:

bool wasCached = false;
var cache = IsCached ? Connector.CommandCache : null;
if (cache != null)
{
    if (cache.TryGetCommand(commandText, out command))
    {
        command.Parameters.Clear();
        command.Transaction = transaction;
        wasCached = true;
    }
    else
    {
        command = new CachedCommand(CreateNewCommand());
        cache.AddCommand(commandText, command);
    }
}