I'm trying to build an IN
clause for array of Guid
s for MySql query. Guid
columns are represented as binary(16)
in the DB. According to docs and answers here I should be able to do something like
var arrayOfGuidsFromDb = ...;
await dbconn.ExecuteAsync<T>("UPDATE ...
SET ...
WHERE SomeGuidField IN @Ids",
new { Ids = arrayOfGuidsFromDb }
I'm also using this Guid converter
class MySqlGuidTypeHandler : SqlMapper.TypeHandler<Guid>
{
public override void SetValue(IDbDataParameter parameter, Guid guid) => parameter.Value = guid.ToByteArray();
public override Guid Parse(object value) => new Guid((byte[])value);
}
The issue with MySql though is that it tries (by default) to optimize GUID layout in the DB by rearranging the timestamp part of the GUID value. I decided not to change this behavior, it is working fine for reads/writes and conditions like WHERE SomeGuidField = @SomeGuid
but for the IN
statement in the question it matches 0 results. I was able to write this hack instead
guids.Select(guid => $"uuid_to_bin('{RotateToMatchInDbGuid(guid).ToString()}')")
where I convert each guid to a string and then string.Join(','...
them for the IN
clause, the helper method:
static Guid RotateToMatchInDbGuid(Guid source)
{
Span<byte> result = stackalloc byte[16];
source.TryWriteBytes(result);
Swap(result, 0, 3);
Swap(result, 1, 2);
Swap(result, 4, 5);
Swap(result, 6, 7);
return new Guid(result);
}
This obviously doesn't look and feel right. Am I doing something wrong or is there some setting missing that I should enable to make Dapper behavior consistent for both =
and IN
GUID conditions?
Full code:
Guid[] guids = await dbConn.QueryAsync("SELECT Id FROM SomeTable"); //returns 1 row
// query using IN clause and array param:
var usingIn = await dbConn.QueryAsync("SELECT * From SomeTable WHERE Id IN @Ids", new { Ids = guids}); // returns 0 rows, should be 1
// now query using the `=` operator and same param but as a single value
var usingEquals = await dbConn.QueryAsync("SELECT * From SomeTable WHERE Id = @Id", new { Id = guids.First() }); // returns 1 row as expected
// query using array as CSV and no params
var usingCSV = await dbConn.QueryAsync($"SELECT * From SomeTable WHERE Id IN ({BuildCsv(guids)})"); // also returns 1 row as expected