Skip to content

SqliteVec: re-use a single command for upserts - #55

Open
rossdonald wants to merge 2 commits into
CommunityToolkit:mainfrom
rossdonald:reuse-command-for-insert
Open

rossdonald wants to merge 2 commits into
CommunityToolkit:mainfrom
rossdonald:reuse-command-for-insert

Conversation

@rossdonald

Copy link
Copy Markdown
Contributor

Changes upserts to use a single command for inserts so SQL text for an insert is stable across records and the provider's per-command prepared statement cache and parameter bindings can be reused, and only values are re-bound per record.
Fixes #54

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Remove the unused test declarations causing CS0219 and bump the provider package version.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 1 High severity

Open (1)
What changed in this PR

Refactors SqliteVec upserts to reuse stable single-row commands and rebind values per record.

Changes:

  • Reuses prepared insert commands.
  • Separates returning and non-returning upsert paths.
  • Expands command-builder tests for parameter rebinding and vector handling.
File Summary
MEVD/​test/​SqliteVec.UnitTests/​SqliteCommandBuilderTests.cs Tests updated command and parameter-binding behavior; unused declarations cause CS0219.
MEVD/​src/​SqliteVec/​SqliteCommandBuilder.cs Builds stable insert SQL and binds values dynamically; package version should be bumped.
MEVD/​src/​SqliteVec/​SqliteCollection.cs Reuses insert commands during upserts; package version should be bumped.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread MEVD/test/SqliteVec.UnitTests/SqliteCommandBuilderTests.cs

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Bump the provider package version for the implementation changes.

Review effort: Lite
Findings: None

Resolved since last review (1)
Previously missed (1)

In code that hasn't changed since last review

Low severity Update provider version for changed C# code

MEVD/​src/​SqliteVec/​SqliteCommandBuilder.cs:120

This PR changes provider implementation files under MEVD/src/SqliteVec, but MEVD/src/SqliteVec/SqliteVec.csproj still declares version 1.0.2-preview. The repository release guidance requires a SemVer update for every such C# change; please bump the provider version so this fix can be published under a distinct package/tag version.

@adamsitnik adamsitnik left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rossdonald thanks for another great contribution!

The code looks good, I found only few minor things that could be addressed before we merge, PTAL at my comments.

Comment on lines +579 to +580
(keyProperty.Type == typeof(int) && keyProperty.GetValue<int>(record) is var i && i == 0)
|| (keyProperty.Type == typeof(long) && keyProperty.GetValue<long>(record) is var l && l == 0L));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: could this be simplified? I know it's pretty subjective but I am not a big fan of the is var syntax

Suggested change
(keyProperty.Type == typeof(int) && keyProperty.GetValue<int>(record) is var i && i == 0)
|| (keyProperty.Type == typeof(long) && keyProperty.GetValue<long>(record) is var l && l == 0L));
(keyProperty.Type == typeof(int) && keyProperty.GetValue<int>(record) == default(int))
|| (keyProperty.Type == typeof(long) && keyProperty.GetValue<long>(record) == default(long)));

dataCommand.Transaction = transaction;
DbCommand? insertCommandReturning = null;
DbCommand? insertCommandNonReturning = null;
var insertProperties = SqliteCommandBuilder.GetInsertProperties(_model, data: true);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The model is immutable for the collection’s lifetime, so we could compute both lists once in  SqliteCollection ctor:

_dataInsertProperties = [.. _model.KeyProperties, .. _model.DataProperties];
_vectorInsertProperties = [.. _model.KeyProperties, .. _model.VectorProperties];

And just pass them as arguments to SqliteCommandBuilder. This would reduce some allocations.

Comment on lines +502 to +510
private static byte[] FloatToBytes(params float[] values)
{
var bytes = new byte[values.Length * sizeof(float)];
for (var i = 0; i < values.Length; i++)
{
BitConverter.GetBytes(values[i]).CopyTo(bytes, i * sizeof(float));
}

return bytes;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test project is targeting only .NET 10, so you can use the BCL provided method here:

Suggested change
private static byte[] FloatToBytes(params float[] values)
{
var bytes = new byte[values.Length * sizeof(float)];
for (var i = 0; i < values.Length; i++)
{
BitConverter.GetBytes(values[i]).CopyTo(bytes, i * sizeof(float));
}
return bytes;
private static byte[] FloatToBytes(params float[] values)
=> MemoryMarshal.AsBytes(values.AsSpan()).ToArray();

This is going to require using System.Runtime.InteropServices;

@rossdonald

Copy link
Copy Markdown
Contributor Author

Thanks for the feedback, looks good. I will update in a few days as I am travelling

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SqliteVec BuildInsertCommand is slow as it does not reuse a single command for inserts

3 participants