Ben Thul
Ben Thul

Reputation: 32737

Can a SqlUserDefinedAggregate be a derived class?

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

Answers (0)

Related Questions