Reputation: 32737
I'm attempting to write some bitwise aggregate functions for a project I'm working on. What I have below is a working implementation for an aggregate where the operator is bitwise-or.
using Microsoft.SqlServer.Server;
using System;
using System.Collections;
using System.Data.SqlTypes;
using System.IO;
namespace BitwiseAggregates
{
[Serializable]
[SqlUserDefinedAggregate(
Format.UserDefined,
MaxByteSize = 8000,
IsInvariantToNulls = false,
IsInvariantToDuplicates = true,
IsInvariantToOrder = true
)]
public class BitwiseOrAggregate : IBinarySerialize
{
private BitArray Accumulator { get; set; }
private bool IsFirstValue { get; set; }
private bool IsNull { get; set; }
public void Init()
{
IsFirstValue = true;
IsNull = false;
}
public void Accumulate(SqlBinary Values)
{
if (Values.IsNull == false && this.IsNull == false)
{
if (this.IsFirstValue == true)
{
this.Accumulator = FromSqlBinary(Values);
this.IsFirstValue = false;
}
else
{
BitArray tempBA = FromSqlBinary(Values);
this.Accumulator = this.Accumulator.Or(tempBA);
}
}
else
{
IsNull = true;
}
}
public void Merge(BitwiseOrAggregate that)
{
if (this.Accumulator.Length != that.Accumulator.Length)
{
throw new InvalidOperationException("All values must be the same length");
}
if (that.IsNull)
{
this.IsNull = true;
}
else
{
this.Accumulator = this.Accumulator.Or(that.Accumulator);
}
}
public SqlBinary Terminate()
{
if (IsNull == true)
{
return SqlBinary.Null;
}
else
{
return new SqlBinary(FromBitArray(Accumulator));
}
}
// Methods for IBinarySerialize
public void Read(BinaryReader r)
{
IsFirstValue = r.ReadBoolean();
IsNull = r.ReadBoolean();
if (IsNull == false)
{
int bytesToRead = r.ReadInt32();
byte[] ba = r.ReadBytes(bytesToRead);
Accumulator = new BitArray(ba);
}
}
public void Write(BinaryWriter w)
{
w.Write(IsFirstValue);
w.Write(IsNull);
if (IsNull == false)
{
byte[] ba = FromBitArray(Accumulator);
int bytesToWrite = ba.Length;
w.Write(bytesToWrite);
w.Write(ba);
}
}
// Helper methods
protected BitArray FromSqlBinary(SqlBinary bytes)
{
return new BitArray((byte[])bytes);
}
protected byte[] FromBitArray(BitArray ba)
{
int returnLength = ba.Length / 8;
byte[] retVal = new byte[returnLength];
ba.CopyTo(retVal, 0);
return retVal;
}
}
}
In looking to implement the next aggregate (using bitwise-and), I evaluated all of the methods and all but Accumulate
and Merge
will be the same. In an attempt to not repeat myself, I changed my code to this:
using Microsoft.SqlServer.Server;
using System;
using System.Collections;
using System.Data.SqlTypes;
using System.IO;
namespace BitwiseAggregates
{
[Serializable]
[SqlUserDefinedAggregate(
Format.UserDefined,
MaxByteSize = 8000,
IsInvariantToNulls = false,
IsInvariantToDuplicates = true,
IsInvariantToOrder = true
)]
public class BitwiseOrAggregate : BitwiseAggregate
{
public void Accumulate(SqlBinary Values)
{
if (Values.IsNull == false && this.IsNull == false)
{
if (this.IsFirstValue == true)
{
this.Accumulator = FromSqlBinary(Values);
this.IsFirstValue = false;
}
else
{
BitArray tempBA = FromSqlBinary(Values);
this.Accumulator = this.Accumulator.Or(tempBA);
}
}
else
{
IsNull = true;
}
}
public void Merge(BitwiseOrAggregate that)
{
if (this.Accumulator.Length != that.Accumulator.Length)
{
throw new InvalidOperationException("All values must be the same length");
}
if (that.IsNull)
{
this.IsNull = true;
}
else
{
this.Accumulator = this.Accumulator.Or(that.Accumulator);
}
}
}
public class BitwiseAggregate : IBinarySerialize
{
protected BitArray Accumulator { get; set; }
protected bool IsFirstValue { get; set; }
protected bool IsNull { get; set; }
public void Init()
{
IsFirstValue = true;
IsNull = false;
}
public SqlBinary Terminate()
{
if (IsNull == true)
{
return SqlBinary.Null;
}
else
{
return new SqlBinary(FromBitArray(Accumulator));
}
}
// Methods for IBinarySerialize
public void Read(BinaryReader r)
{
IsFirstValue = r.ReadBoolean();
IsNull = r.ReadBoolean();
if (IsNull == false)
{
int bytesToRead = r.ReadInt32();
byte[] ba = r.ReadBytes(bytesToRead);
Accumulator = new BitArray(ba);
}
}
public void Write(BinaryWriter w)
{
w.Write(IsFirstValue);
w.Write(IsNull);
if (IsNull == false)
{
byte[] ba = FromBitArray(Accumulator);
int bytesToWrite = ba.Length;
w.Write(bytesToWrite);
w.Write(ba);
}
}
// Helper methods
protected BitArray FromSqlBinary(SqlBinary bytes)
{
return new BitArray((byte[])bytes);
}
protected byte[] FromBitArray(BitArray ba)
{
int returnLength = ba.Length / 8;
byte[] retVal = new byte[returnLength];
ba.CopyTo(retVal, 0);
return retVal;
}
}
}
But it doesn't work. Through a little experimentation, it seems that neither Init()
nor Terminate()
are making it into the SqlUserDefinedAggregate. What am I missing?
Upvotes: 0
Views: 52