cs/ScalarFunctionWrapper.cs

#pragma warning disable DuckDBNET001
#pragma warning disable CS1701
 
using System;
using System.Collections.Generic;
using System.Management.Automation;
using System.Reflection;
using DuckDB.NET.Data.DataChunk.Reader;
using DuckDB.NET.Data.DataChunk.Writer;
 
namespace PaperinikDB
{
    /// <summary>
    /// Wraps a PowerShell ScriptBlock to be used as a DuckDB scalar function delegate.
    /// </summary>
    public class ScalarFunctionWrapper
    {
        private readonly ScriptBlock _scriptBlock;
        private readonly Type _resultType;
 
        public ScalarFunctionWrapper(ScriptBlock scriptBlock, Type resultType)
        {
            _scriptBlock = scriptBlock ?? throw new ArgumentNullException(nameof(scriptBlock));
            _resultType = resultType ?? throw new ArgumentNullException(nameof(resultType));
        }
 
        public void Invoke(IReadOnlyList<IDuckDBDataReader> readers, IDuckDBDataWriter writer, ulong rowCount)
        {
            // Get the WriteValue method with proper generic parameter (cached reflection)
            var writeValueMethod = writer.GetType().GetMethod("WriteValue");
            MethodInfo genericWriteValue = null;
             
            if (writeValueMethod != null && writeValueMethod.IsGenericMethodDefinition)
            {
                genericWriteValue = writeValueMethod.MakeGenericMethod(_resultType);
            }
             
            for (ulong index = 0; index < rowCount; index++)
            {
                try
                {
                    // Collect input values
                    var inputValues = new object[readers.Count];
                    for (int p = 0; p < readers.Count; p++)
                    {
                        inputValues[p] = readers[p].GetValue(index);
                    }
 
                    // Invoke the ScriptBlock
                    var results = _scriptBlock.Invoke(inputValues);
                     
                    if (results != null && results.Count > 0 && results[0] != null)
                    {
                        var result = results[0].BaseObject;
                         
                        // Convert result to the expected type and write
                        var convertedResult = Convert.ChangeType(result, _resultType);
                         
                        if (genericWriteValue != null)
                        {
                            genericWriteValue.Invoke(writer, new object[] { convertedResult, index });
                        }
                        else if (writeValueMethod != null)
                        {
                            writeValueMethod.Invoke(writer, new object[] { convertedResult, index });
                        }
                        else
                        {
                            writer.WriteNull(index);
                        }
                    }
                    else
                    {
                        writer.WriteNull(index);
                    }
                }
                catch
                {
                    // On any error, write NULL for this row
                    writer.WriteNull(index);
                }
            }
        }
    }
}