Concatenate binary data using || by virzak · Pull Request #2200 · npgsql/efcore.pg

Hi @roji,

Ultimately, the goal is to have an easy way to implement the last non null puzzle. lastval is the one that needs to be generated.

id     col1     lastval
----   ------   -----------
2      NULL     NULL
3      10       10
5      -1       -1
7      NULL     -1
11     NULL     -1
13     -12      -12
17     NULL     -12
19     NULL     -12
23     1759     1759

This SQL is the most efficient for SQL server.

SELECT id, col1,
  CAST(
    SUBSTRING(
  MAX( CAST(id AS BINARY(4)) + CAST(col1 AS BINARY(4)) )
    OVER( ORDER BY id
      ROWS UNBOUNDED PRECEDING ),
  5, 4)
    AS INT) AS lastval
FROM dbo.T1;

The walkthrough is here (unfortunately behind a registration gate) and also mentioned here

So I'm writing an extension that handles:

  • Window functions
  • Binary manipulations

I just got it to work with SQL Server, but not with SQLite or PostgreSQL (never used it before).

The LINQ looks as follows:

var query = dbContext.LastNonNulls
.Select(r => new
{
    LastNonNull =
    EF.Functions.ToInt32(
    EF.Functions.Substring(
        EF.Functions.MaxOver(
            EF.Functions.Concatenate(
                EF.Functions.GetBytes(r.Id), EF.Functions.GetBytes(r.Col1)
            ), EF.Functions.CreateOrdering(r.Id))
        , 5, 4
    )
    )
});

The innermost sql for posgres shoud (I think) look as follows:

SELECT l."Id"::bit(32) || l."Col1"::bit(32)
FROM "LastNonNulls" AS l

One way to generate that would be to override FindBaseMapping and invoke this code:

if (clrType == typeof(BitArray))
{
mapping = mappingInfo.IsFixedLength ?? false ? _bit : _varbit;
return mapping.Clone($"{mapping.StoreType}({mappingInfo.Size})", mappingInfo.Size);
}

That's how the concatenation operands end up with BitArray type.

The current workaround is this:

public class BinaryNpgsqlQuerySqlGenerator : NpgsqlQuerySqlGenerator
{
    public BinaryNpgsqlQuerySqlGenerator(QuerySqlGeneratorDependencies dependencies, bool reverseNullOrderingEnabled, Version postgresVersion) : base(dependencies, reverseNullOrderingEnabled, postgresVersion)
    {
    }

    protected override string GetOperator(SqlBinaryExpression e)
        => e.OperatorType switch
        {
            ExpressionType.Add when
                e.Type == typeof(BitArray) || e.Left.TypeMapping?.ClrType == typeof(BitArray) || e.Right.TypeMapping?.ClrType == typeof(BitArray)
                => " || ",
            _ => base.GetOperator(e)
        };
}

I'll be publishing the entire source code within the next few days.