SQLServer

Library that type-ifies some Microsoft SQL Server concepts.
git clone https://git.philomathiclife.com/repos/SQLServer
Log | Files | Refs | README

Shared.cs (63793B)


      1 using FNVHash;
      2 using Microsoft.Data.SqlClient;
      3 using Std;
      4 using Std.Clone;
      5 using Std.Cmp;
      6 using Std.Collections.HashMap;
      7 using Std.Convert;
      8 using Std.Error;
      9 using Std.Hashing;
     10 using Std.Iter;
     11 using Std.Maybe;
     12 using Std.Num;
     13 using Std.Ops;
     14 using Std.Result;
     15 using Std.Vec;
     16 using System;
     17 using System.Data;
     18 using System.Data.SqlTypes;
     19 using System.Diagnostics;
     20 using System.Runtime.CompilerServices;
     21 using System.Runtime.InteropServices;
     22 using System.Text;
     23 #region Namespaces
     24 namespace SQLServer {
     25     #region Types
     26     [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode, Pack = 0)]
     27     readonly struct ActualType: IClone<ActualType>, IHashable, IInto<ActualType>, IInto<string>, IEq<ActualType> {
     28 
     29         #region Type-level Constructors
     30         #endregion
     31 
     32         #region Instance Constructors
     33         public ActualType() => throw new InvalidOperationException("Parameterless constructor is not allowed to be called!");
     34         internal ActualType(Type type) => Value = type;
     35         #endregion
     36 
     37         #region Type-level Fields
     38         #endregion
     39 
     40         #region Instance Fields
     41         [FieldOffset(0)] internal readonly Type Value;
     42         #endregion
     43 
     44         #region Type-level Properties
     45         #endregion
     46 
     47         #region Instance Properties
     48         #endregion
     49 
     50         #region Type-level Functions
     51         #endregion
     52 
     53         #region Instance Functions
     54         public readonly ActualType Clone() => this;
     55         public override readonly bool Equals(object? _) => false;
     56         public override readonly int GetHashCode() => 0;
     57         public readonly Unit Hash<THasher>(ref THasher hasher) where THasher: notnull, IHasher => hasher.WriteInt(Value.GetHashCode());
     58         public readonly ActualType Into() => this;
     59         public readonly string IntoString() => ToString();
     60         readonly string IInto<string>.Into() => IntoString();
     61         public override readonly string ToString() => Value.Name;
     62         readonly Result<ActualType, Bottom> ITryInto<ActualType, Bottom>.TryInto() => new(this);
     63         readonly Result<string, Bottom> ITryInto<string, Bottom>.TryInto() => new(ToString());
     64         #endregion
     65 
     66         #region Operators
     67         public static bool operator !=(ActualType val0, ActualType val1) => !(val0 == val1);
     68         public static bool operator ==(ActualType val0, ActualType val1) => val0.Value == val1.Value;
     69         public static implicit operator ActualType(Type val) => new(val);
     70         public static implicit operator Type(ActualType val) => val.Value;
     71         #endregion
     72 
     73         #region Types
     74         #endregion
     75     }
     76     // This is a fairly large type and it will likely be used frequently as the error in a Result.
     77     // If this were a struct, the Result would be large. If it were the case the Result
     78     // would be local only, then it would be better if this were a struct; however it's likely that the Result will simply
     79     // be passed down the call stack making it important the copies are small so we make it a sealed class instead.
     80     [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode, Pack = 8, Size = 40)]
     81     public sealed class BulkRowError: ISum<Prod<IError, string>, Prod<IError, byte[]>, Prod<IError, string, byte[]>, Prod<StackTrace, string>, Prod<StackTrace, byte[]>, Prod<StackTrace, string, byte[]>, Prod<string, string, string, int>, Prod<byte[], string, string, int>, Prod<string, byte[], string, string, int>>, IBulkRowError, IInto<BulkRowError> {
     82 
     83         #region Type-level Constructors
     84         #endregion
     85 
     86         #region Instance Constructors
     87         BulkRowError() => (_err, _trace, _memberName, _message, _data, _sourceFilePath, _sourceLineNumber, Var) = (default!, default!, default!, default!, default!, default!, default, default);
     88         BulkRowError(IError err, string message) : this() => (Var, _err, _message) = (Tag.ErrorMessage, err, message);
     89         BulkRowError(IError err, byte[] data) : this() => (Var, _err, _data) = (Tag.ErrorData, err, data);
     90         BulkRowError(IError err, string message, byte[] data) : this() => (Var, _err, _message, _data) = (Tag.ErrorMessageAndData, err, message, data);
     91         BulkRowError(StackTrace trace, string message) : this() => (Var, _trace, _message) = (Tag.TraceMessage, trace, message);
     92         BulkRowError(StackTrace trace, byte[] data) : this() => (Var, _trace, _data) = (Tag.TraceData, trace, data);
     93         BulkRowError(StackTrace trace, string message, byte[] data) : this() => (Var, _trace, _message, _data) = (Tag.TraceMessageAndData, trace, message, data);
     94         BulkRowError(string message, string memberName, string sourceFilePath, int sourceLineNumber) : this() => (Var, _message, _memberName, _sourceFilePath, _sourceLineNumber) = (Tag.MessageAndCallerInfo, message, memberName, sourceFilePath, sourceLineNumber);
     95         BulkRowError(byte[] data, string memberName, string sourceFilePath, int sourceLineNumber) : this() => (Var, _data, _memberName, _sourceFilePath, _sourceLineNumber) = (Tag.DataAndCallerInfo, data, memberName, sourceFilePath, sourceLineNumber);
     96         BulkRowError(string message, byte[] data, string memberName, string sourceFilePath, int sourceLineNumber) : this() => (Var, _message, _data, _memberName, _sourceFilePath, _sourceLineNumber) = (Tag.MessageAndDataAndCallerInfo, message, data, memberName, sourceFilePath, sourceLineNumber);
     97         #endregion
     98 
     99         #region Type-level Fields
    100         #endregion
    101 
    102         #region Instance Fields
    103         [FieldOffset(0)] readonly IError _err;
    104         [FieldOffset(0)] readonly StackTrace _trace;
    105         [FieldOffset(0)] readonly string _memberName;
    106         [FieldOffset(8)] readonly string _message;
    107         [FieldOffset(16)] readonly byte[] _data;
    108         [FieldOffset(24)] readonly string _sourceFilePath;
    109         [FieldOffset(32)] readonly int _sourceLineNumber;
    110         [FieldOffset(36)] public readonly Tag Var;
    111         #endregion
    112 
    113         #region Type-level Properties
    114         #endregion
    115 
    116         #region Instance Properties
    117         public Var9 Variant => (Var9)Var;
    118         public Prod<IError, string> Variant0 => IntoErrorMessage();
    119         public Prod<IError, byte[]> Variant1 => IntoErrorData();
    120         public Prod<IError, string, byte[]> Variant2 => IntoErrorMessageAndData();
    121         public Prod<StackTrace, string> Variant3 => IntoTraceMessage();
    122         public Prod<StackTrace, byte[]> Variant4 => IntoTraceData();
    123         public Prod<StackTrace, string, byte[]> Variant5 => IntoTraceMessageAndData();
    124         public Prod<string, string, string, int> Variant6 => IntoMessageAndCallerInfo();
    125         public Prod<byte[], string, string, int> Variant7 => IntoDataAndCallerInfo();
    126         public Prod<string, byte[], string, string, int> Variant8 => IntoMessageAndDataAndCallerInfo();
    127         public nvarchar Trace => Var switch {
    128             Tag.TraceMessage or Tag.TraceData or Tag.TraceMessageAndData => nvarchar.New(_trace.ToString()),
    129             Tag.DataAndCallerInfo or Tag.MessageAndCallerInfo or Tag.MessageAndDataAndCallerInfo => nvarchar.New($@"CallerMemberName: {_memberName}
    130 CallerFilePath: {_sourceFilePath}
    131 CallerLineNumber: {_sourceLineNumber.ToString()}"),
    132             _ => nvarchar.NULL,
    133         };
    134         public nvarchar Message => Var switch {
    135             Tag.ErrorMessage or Tag.ErrorMessageAndData or Tag.TraceMessage or Tag.TraceMessageAndData or Tag.MessageAndCallerInfo or Tag.MessageAndDataAndCallerInfo => nvarchar.New(_message),
    136             _ => nvarchar.NULL,
    137         };
    138         public varbinary Data => Var switch {
    139             Tag.ErrorData or Tag.ErrorMessageAndData or Tag.TraceData or Tag.TraceMessageAndData or Tag.DataAndCallerInfo or Tag.MessageAndDataAndCallerInfo => varbinary.New(_data),
    140             _ => varbinary.NULL,
    141         };
    142         #endregion
    143 
    144         #region Type-level Functions
    145         public static BulkRowError ErrorMessage(IError err, string message) => new(err, message);
    146         public static BulkRowError ErrorData(IError err, byte[] data) => new(err, data);
    147         public static BulkRowError ErrorMessageAndData(IError err, string message, byte[] data) => new(err, message, data);
    148         public static BulkRowError TraceMessage(StackTrace trace, string message) => new(trace, message);
    149         public static BulkRowError TraceData(StackTrace trace, byte[] data) => new(trace, data);
    150         public static BulkRowError TraceMessageAndData(StackTrace trace, string message, byte[] data) => new(trace, message, data);
    151         public static BulkRowError MessageAndCallerInfo(string message, [CallerMemberName] string memberName = "", [CallerFilePath] string sourceFilePath = "", [CallerLineNumber] int sourceLineNumber = 0) => new(message, memberName, sourceFilePath, sourceLineNumber);
    152         public static BulkRowError DataAndCallerInfo(byte[] data, [CallerMemberName] string memberName = "", [CallerFilePath] string sourceFilePath = "", [CallerLineNumber] int sourceLineNumber = 0) => new(data, memberName, sourceFilePath, sourceLineNumber);
    153         public static BulkRowError MessageAndDataAndCallerInfo(string message, byte[] data, [CallerMemberName] string memberName = "", [CallerFilePath] string sourceFilePath = "", [CallerLineNumber] int sourceLineNumber = 0) => new(message, data, memberName, sourceFilePath, sourceLineNumber);
    154         #endregion
    155 
    156         #region Instance Functions
    157         public sealed override bool Equals(object? _) => false;
    158         public sealed override int GetHashCode() => 0;
    159         public BulkRowError Into() => this;
    160         public string IntoString() => ToString();
    161         string IInto<string>.Into() => IntoString();
    162         public Prod<IError, string> IntoErrorMessage() => Var == Tag.ErrorMessage ? new(_err, _message) : throw new InvalidOperationException($"The BulkRowError variant {Var.ToString()} is not ErrorMessage!");
    163         public Prod<IError, byte[]> IntoErrorData() => Var == Tag.ErrorData ? new(_err, _data) : throw new InvalidOperationException($"The BulkRowError variant {Var.ToString()} is not ErrorData!");
    164         public Prod<IError, string, byte[]> IntoErrorMessageAndData() => Var == Tag.ErrorMessageAndData ? new(_err, _message, _data) : throw new InvalidOperationException($"The BulkRowError variant {Var.ToString()} is not ErrorMessageAndData!");
    165         public Prod<StackTrace, string> IntoTraceMessage() => Var == Tag.TraceMessage ? new(_trace, _message) : throw new InvalidOperationException($"The BulkRowError variant {Var.ToString()} is not TraceMessage!");
    166         public Prod<StackTrace, byte[]> IntoTraceData() => Var == Tag.TraceData ? new(_trace, _data) : throw new InvalidOperationException($"The BulkRowError variant {Var.ToString()} is not TraceData!");
    167         public Prod<StackTrace, string, byte[]> IntoTraceMessageAndData() => Var == Tag.TraceMessageAndData ? new(_trace, _message, _data) : throw new InvalidOperationException($"The BulkRowError variant {Var.ToString()} is not TraceMessageAndData!");
    168         public Prod<string, string, string, int> IntoMessageAndCallerInfo() => Var == Tag.MessageAndCallerInfo ? new(_message, _memberName, _sourceFilePath, _sourceLineNumber) : throw new InvalidOperationException($"The BulkRowError variant {Var.ToString()} is not MessageAndCallerInfo!");
    169         public Prod<byte[], string, string, int> IntoDataAndCallerInfo() => Var == Tag.DataAndCallerInfo ? new(_data, _memberName, _sourceFilePath, _sourceLineNumber) : throw new InvalidOperationException($"The BulkRowError variant {Var.ToString()} is not DataAndCallerInfo!");
    170         public Prod<string, byte[], string, string, int> IntoMessageAndDataAndCallerInfo() => Var == Tag.MessageAndDataAndCallerInfo ? new(_message, _data, _memberName, _sourceFilePath, _sourceLineNumber) : throw new InvalidOperationException($"The BulkRowError variant {Var.ToString()} is not MessageAndDataAndCallerInfo!");
    171         public Maybe<IError> Source() => Var switch {
    172             Tag.ErrorMessage or Tag.ErrorData or Tag.ErrorMessageAndData => new(_err),
    173             _ => Maybe<IError>.None(),
    174         };
    175         public Maybe<StackTrace> StackTrace() => Var switch {
    176             Tag.TraceMessage or Tag.TraceData or Tag.TraceMessageAndData => new(_trace),
    177             _ => Maybe<StackTrace>.None(),
    178         };
    179         [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1065:Do not raise exceptions in unexpected locations", Justification = "Need to make sure code handles all enum values.")]
    180         public sealed override string ToString() => $@"{Var.ToString()}({Var switch {
    181             Tag.ErrorMessage => $@"IError: {_err.Into()},
    182 Message: {_message}",
    183             Tag.ErrorData => $@"IError: {_err.Into()},
    184 Data: {_data.AsSpan().UpperHex()}",
    185             Tag.ErrorMessageAndData => $@"IError: {_err.Into()},
    186 Message: {_message},
    187 Data: {_data.AsSpan().UpperHex()}",
    188             Tag.TraceMessage => $@"StackTrace: {_trace.ToString()},
    189 Message: {_message}",
    190             Tag.TraceData => $@"StackTrace: {_trace.ToString()},
    191 Data: {_data.AsSpan().UpperHex()}",
    192             Tag.TraceMessageAndData => $@"StackTrace: {_trace.ToString()},
    193 Message: {_message},
    194 Data: {_data.AsSpan().UpperHex()}",
    195             Tag.MessageAndCallerInfo => $@"Message: {_message},
    196 CallerMemberName: {_memberName}
    197 CallerFilePath: {_sourceFilePath}
    198 CallerLineNumber: {_sourceLineNumber.ToString()}",
    199             Tag.DataAndCallerInfo => $@"Data: {_data.AsSpan().UpperHex()},
    200 CallerMemberName: {_memberName}
    201 CallerFilePath: {_sourceFilePath}
    202 CallerLineNumber: {_sourceLineNumber.ToString()}",
    203             Tag.MessageAndDataAndCallerInfo => $@"Message: {_message},
    204 Data: {_data.AsSpan().UpperHex()},
    205 CallerMemberName: {_memberName}
    206 CallerFilePath: {_sourceFilePath}
    207 CallerLineNumber: {_sourceLineNumber.ToString()}",
    208             _ => throw new InvalidOperationException($"{Var.ToString()} is an invalid BulkRowError variant!"),
    209         }})";
    210         Result<BulkRowError, Bottom> ITryInto<BulkRowError, Bottom>.TryInto() => new(this);
    211         Result<string, Bottom> ITryInto<string, Bottom>.TryInto() => new(ToString());
    212         #endregion
    213 
    214         #region Operators
    215         #endregion
    216 
    217         #region Types
    218         public enum Tag: uint {
    219             ErrorMessage = uint.MinValue,
    220             ErrorData = 1u,
    221             ErrorMessageAndData = 2u,
    222             TraceMessage = 3u,
    223             TraceData = 4u,
    224             TraceMessageAndData = 5u,
    225             MessageAndCallerInfo = 6u,
    226             DataAndCallerInfo = 7u,
    227             MessageAndDataAndCallerInfo = 8u,
    228         }
    229         #endregion
    230     }
    231     [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode, Pack = 8, Size = 16)]
    232     public readonly struct BulkWriterCreateError: ISum<StackTrace, StackTrace, StackTrace, StackTrace, StackTrace>, IError, IInto<BulkWriterCreateError> {
    233 
    234         #region Type-level Constructors
    235         #endregion
    236 
    237         #region Instance Constructors
    238         public BulkWriterCreateError() => throw new InvalidOperationException("Parameterless constructor is not allowed to be called!");
    239         internal BulkWriterCreateError(Tag flag, StackTrace trace) => (Var, Backtrace) = (flag, trace);
    240         #endregion
    241 
    242         #region Type-level Fields
    243         #endregion
    244 
    245         #region Instance Fields
    246         [FieldOffset(0)] public readonly Tag Var;
    247         [FieldOffset(8)] public readonly StackTrace Backtrace;
    248         #endregion
    249 
    250         #region Type-level Properties
    251         #endregion
    252 
    253         #region Instance Properties
    254         public readonly Var5 Variant => (Var5)Var;
    255         public readonly StackTrace Variant0 => Var == Tag.DatabaseIsReadOnly ? Backtrace : throw new InvalidOperationException($"The BulkWriterCreateError variant, {Var.ToString()}, is not DatabaseIsReadOnly!");
    256         public readonly StackTrace Variant1 => Var == Tag.TypeMismatch ? Backtrace : throw new InvalidOperationException($"The BulkWriterCreateError variant, {Var.ToString()}, is not TypeMismatch!");
    257         public readonly StackTrace Variant2 => Var == Tag.InvalidErrorRatio ? Backtrace : throw new InvalidOperationException($"The BulkWriterCreateError variant, {Var.ToString()}, is not InvalidErrorRatio!");
    258         public readonly StackTrace Variant3 => Var == Tag.ProcessNameLengthExceeds128 ? Backtrace : throw new InvalidOperationException($"The BulkWriterCreateError variant, {Var.ToString()}, is not ProcessNameLengthExceeds128!");
    259         public readonly StackTrace Variant4 => Var == Tag.UserNameLengthExceeds128 ? Backtrace : throw new InvalidOperationException($"The BulkWriterCreateError variant, {Var.ToString()}, is not UserNameLengthExceeds128!");
    260         #endregion
    261 
    262         #region Type-level Functions
    263         #endregion
    264 
    265         #region Instance Functions
    266         public override readonly bool Equals(object? _) => false;
    267         public override readonly int GetHashCode() => 0;
    268         public readonly BulkWriterCreateError Into() => this;
    269         public readonly string IntoString() => ToString();
    270         readonly string IInto<string>.Into() => IntoString();
    271         public readonly Maybe<IError> Source() => Maybe<IError>.None();
    272         public readonly Maybe<StackTrace> StackTrace() => new(Backtrace);
    273         public override readonly string ToString() => $"{Var.ToString()}({Backtrace.ToString()})";
    274         readonly Result<BulkWriterCreateError, Bottom> ITryInto<BulkWriterCreateError, Bottom>.TryInto() => new(this);
    275         readonly Result<string, Bottom> ITryInto<string, Bottom>.TryInto() => new(ToString());
    276         #endregion
    277 
    278         #region Operators
    279         #endregion
    280 
    281         #region Types
    282         public enum Tag: ulong {
    283             DatabaseIsReadOnly = ulong.MinValue,
    284             TypeMismatch = 1ul,
    285             InvalidErrorRatio = 2ul,
    286             ProcessNameLengthExceeds128 = 3ul,
    287             UserNameLengthExceeds128 = 4ul,
    288         }
    289         #endregion
    290     }
    291     sealed class ErrorReader: IDataReader {
    292 
    293         #region Type-level Constructors
    294         #endregion
    295 
    296         #region Instance Constructors
    297         ErrorReader() => (Table, _vec, _index, _current, ProcessName, User, _id, IsClosed) = (default, default, default, default, default!, default!, default, default);
    298         // MUST ensure that processName and userName are no more than 128 chars in length before calling!
    299         internal ErrorReader(ErrorTable table, Vec<Prod<nvarchar, nvarchar, varbinary>> vec, string processName, string userName) => (Table, _vec, _index, _current, ProcessName, User, _id, IsClosed) = (table, vec, uint.MinValue, default, processName, userName, short.MinValue, false);
    300         #endregion
    301 
    302         #region Type-level Fields
    303         static readonly Type[] _dataTypes = new Type[7] { typeof(string), typeof(DateTimeOffset), typeof(short), typeof(string), typeof(string), typeof(string), typeof(byte[]) };
    304         const string _error = "The error table must conform to the following schema: <name0> nvarchar(128) NOT NULL, <name1> datetimeoffset(7) NOT NULL, <name2> smallint NOT NULL, <name3> nvarchar(max) NOT NULL, <name4> nvarchar(max) NULL, <name5> nvarchar(max) NULL, <name6> varbinary(max) NULL.";
    305         #endregion
    306 
    307         #region Instance Fields
    308         internal readonly string ProcessName;
    309         internal readonly string User;
    310         short _id;
    311         internal readonly ErrorTable Table;
    312         Prod<nvarchar, nvarchar, varbinary> _current;
    313         Vec<Prod<nvarchar, nvarchar, varbinary>> _vec;
    314         uint _index;
    315         #endregion
    316 
    317         #region Type-level Properties
    318         #endregion
    319 
    320         #region Instance Properties
    321         public bool IsClosed { get; private set; }
    322         public int Depth => 0;
    323         public int FieldCount => 7;
    324         public object this[int ordinal] => GetValue(ordinal);
    325         public object this[string columnName] => GetValue(GetOrdinal(columnName));
    326         public int RecordsAffected => -1;
    327         #endregion
    328 
    329         #region Type-level Functions
    330         #endregion
    331 
    332         #region Instance Functions
    333         public void Close() => Dispose();
    334         public void Dispose() {
    335 
    336             if (IsClosed) { return; }
    337             _index = _vec.Len;
    338             _current = default;
    339             IsClosed = true;
    340         }
    341         public sealed override bool Equals(object? _) => false;
    342         public bool GetBoolean(int _) => throw new InvalidOperationException(_error);
    343         public byte GetByte(int ordinal) => (byte)GetValue(ordinal);
    344         public long GetBytes(int ordinal, long dataOffset, byte[]? buffer, int bufferOffset, int length) {
    345 
    346             var val = GetValue(ordinal);
    347             var bytes = (byte[])val;
    348             var offset = (int)dataOffset;
    349             var len = bytes.Length - offset;
    350             if (len <= 0) { return 0L; }
    351             var count = Math.Min(len, length);
    352             var i = 0;
    353             while (i < count) { buffer![bufferOffset + i] = bytes[offset + i++]; }
    354             return count;
    355         }
    356         public char GetChar(int ordinal) {
    357 
    358             var val = (string)GetValue(ordinal);
    359             return val.Length == 1 ? val[0] : throw new InvalidCastException();
    360         }
    361         public long GetChars(int ordinal, long dataOffset, char[]? buffer, int bufferOffset, int length) {
    362 
    363             var val = GetValue(ordinal);
    364             var chars = (string)val;
    365             var offset = (int)dataOffset;
    366             var len = chars.Length - offset;
    367             if (len <= 0) { return 0L; }
    368             var count = Math.Min(len, length);
    369             var i = 0;
    370             while (i < count) { buffer![bufferOffset + i] = chars[offset + i++]; }
    371             return count;
    372         }
    373         public IDataReader GetData(int _) => throw new NotSupportedException();
    374         public string GetDataTypeName(int ordinal) => GetFieldType(ordinal).Name;
    375         public DateTime GetDateTime(int ordinal) => ((DateTimeOffset)GetValue(ordinal)).DateTime;
    376         public decimal GetDecimal(int _) => throw new InvalidOperationException(_error);
    377         public double GetDouble(int _) => throw new InvalidOperationException(_error);
    378         public Type GetFieldType(int ordinal) => _dataTypes[ordinal];
    379         public float GetFloat(int _) => throw new InvalidOperationException(_error);
    380         public Guid GetGuid(int _) => throw new InvalidOperationException(_error);
    381         public sealed override int GetHashCode() => 0;
    382         public short GetInt16(int ordinal) => (short)GetValue(ordinal);
    383         public int GetInt32(int _) => throw new InvalidOperationException(_error);
    384         public long GetInt64(int _) => throw new InvalidOperationException(_error);
    385         public string GetName(int ordinal) => Table.Value[(ushort)ordinal].Name;
    386         public int GetOrdinal(string name) {
    387 
    388             for (ushort i = 0; i < Table.Value.ColumnCount; i++) { if (Table.Value.Schema.Name.Culture.CompareInfo.Compare(name, Table.Value[i].Name, Table.Value.Schema.Name.Options) == 0) { return i; } }
    389             throw new ArgumentException($"The column name, {name}, does not exist in {Table.IntoString()}.");
    390         }
    391         public DataTable GetSchemaTable() {
    392 
    393             DataTable schema = new() { MinimumCapacity = 7, TableName = $"{Table.Value.Schema.Name.Value}.{Table.Value.Name}", Locale = Table.Value.Schema.Name.Culture };
    394             _ = schema.Columns.Add("Ordinal", typeof(ushort));
    395             _ = schema.Columns.Add("ColumnName", typeof(string));
    396             _ = schema.Columns.Add("DataType", typeof(Type));
    397             for (ushort i = 0; i < Table.Value.ColumnCount; i++) { _ = schema.Rows.Add(i, Table.Value[i].Name, GetFieldType(i)); }
    398             return schema;
    399         }
    400         public string GetString(int ordinal) => (string)GetValue(ordinal);
    401         public object GetValue(int ordinal) {
    402 
    403             short id = 0;
    404 
    405             if (ordinal == 2) {
    406                 id = _id;
    407                 // We don't want to cause exceptions, so we use wrapping addition.
    408                 // Note that it is EXTREMELY unlikely wrapping behavior will ever occur especially since the
    409                 // bulk writers are the only types that can create this type, and they create a new ErrorReader
    410                 // every 4096 errors.
    411                 _id = _id.WrappingAdd(1);
    412             }
    413             return ordinal switch {
    414                 0 => ProcessName!,
    415                 1 => DateTimeOffset.Now,
    416                 2 => id,
    417                 3 => User,
    418                 4 => _current.Item0.Value!,
    419                 5 => _current.Item1.Value!,
    420                 6 => _current.Item2.Value!,
    421                 _ => throw new ArgumentException($"The ordinal position, {ordinal.ToString()}, is not inclusively between 0 and 6."),
    422             };
    423         }
    424         public int GetValues(object[] values) {
    425 
    426             values[0] = GetValue(0);
    427             values[1] = GetValue(1);
    428             values[2] = GetValue(2);
    429             values[3] = GetValue(3);
    430             values[4] = GetValue(4);
    431             values[5] = GetValue(5);
    432             values[6] = GetValue(6);
    433             return 7;
    434         }
    435         public bool IsDBNull(int ordinal) => ordinal switch { 4 => _current.Item0.IsNULL, 5 => _current.Item1.IsNULL, 6 => _current.Item2.IsNULL, _ => false, };
    436         public bool NextResult() {
    437 
    438             _index = _vec.Len;
    439             _current = default;
    440             return false;
    441         }
    442         public bool Read() {
    443             if (_index < _vec.Len) {
    444                 _current = _vec[_index++];
    445                 return true;
    446             } else {
    447                 return false;
    448             }
    449         }
    450         public sealed override string ToString() => string.Empty;
    451         #endregion
    452 
    453         #region Operators
    454         #endregion
    455 
    456         #region Types
    457         #endregion
    458     }
    459     [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 0)]
    460     public readonly struct ErrorTable: IClone<ErrorTable>, IEq<ErrorTable>, IHashable, IInto<ErrorTable>, IInto<string> {
    461 
    462         #region Type-level Constructors
    463         #endregion
    464 
    465         #region Instance Constructors
    466         public ErrorTable() => throw new InvalidOperationException("Parameterless constructor is not allowed to be called!");
    467         ErrorTable(UserTable name) => (Value, ContainsEncrypted) = (name, name.ContainsEncryptedColumn());
    468         #endregion
    469 
    470         #region Type-level Fields
    471         static readonly Fn<ushort, bool> _lenIs128 = (x) => x == 128u;
    472         static readonly Fn<Result<ErrorTable, InvalidErrorTable>> _invalidErrTable = () => new(new InvalidErrorTable(InvalidErrorTable.Tag.Does_not_contain_non_unique_clustered_index_whose_index_keys_are_the_first_three_columns, new StackTrace(1, true)));
    473         #endregion
    474 
    475         #region Instance Fields
    476         public readonly bool ContainsEncrypted;
    477         public readonly UserTable Value;
    478         #endregion
    479 
    480         #region Type-level Properties
    481         #endregion
    482 
    483         #region Instance Properties
    484         #endregion
    485 
    486         #region Type-level Functions
    487         // An error table must conform to the following schema:
    488         // <name0> nvarchar(128) NOT NULL,
    489         // <name1> datetimeoffset(7) NOT NULL,
    490         // <name2> smallint NOT NULL,
    491         // <name3> nvarchar(128) NOT NULL,
    492         // <name4> nvarchar(max) NULL,
    493         // <name5> nvarchar(max) NULL,
    494         // <name6> varbinary(max) NULL
    495         // Additionally must not contain any non-clustered indexes but must contain a non-unique clustered index
    496         // whose index columns are <name0> and <name1> in order. Must not contain any child objects (e.g., constraints) either.
    497         // Obviously must not be in a ReadOnly database.
    498         // The intent of <name0> is to be a quick description or source of the error (e.g., process name).
    499         // The intent of <name1> is to be the date and time the error occurred.
    500         // The intent of <name2> is to be an ID for rows that have the same values for <name0> and <name1>.
    501         // The intent of <name3> is to be the name of the user the process that generated the error ran under.
    502         // The intent of <name4> is to be detailed information of where the error occurred (e.g., a stack trace).
    503         // The intent of <name5> is to be a message containing the error that occurred and/or the raw data that caused the error.
    504         // The intent of <name6> is to be a the raw data that caused the error (assuming <name4> cannot be used).
    505         [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1502:Rewrite or refactor the code to decrease its complexity below '26'.", Justification = "No real alternative.")]
    506         public static Result<ErrorTable, InvalidErrorTable> New(in UserTable errorTable) {
    507 
    508             if (errorTable.Schema.Database.IsReadOnly) {
    509                 return new(new InvalidErrorTable(InvalidErrorTable.Tag.Database_is_readonly, new StackTrace(1, true)));
    510             } else if(errorTable.ColumnCount != 7) {
    511                 return new(new InvalidErrorTable(InvalidErrorTable.Tag.Does_not_contain_7_columns, new StackTrace(1, true)));
    512             } else {
    513                 ref readonly var col = ref errorTable[ushort.MinValue];
    514                 if (!(!col.IsNullable && col.ComputedInfo.IsNone && col.EncryptionInfo.IsNone && col.MaxLength.MapOr(false, _lenIs128) && col.DataType == typeof(nvarchar))) { return new(new InvalidErrorTable(InvalidErrorTable.Tag.First_column_not_a_non_nullable_non_encrypted_non_computed_nvarchar_128, new StackTrace(1, true))); }
    515                 col = ref errorTable[1];
    516                 if (!(!col.IsNullable && col.ComputedInfo.IsNone && col.EncryptionInfo.IsNone && col.DataType == typeof(datetimeoffset) && col.Scale.Unwrap() == 7)) { return new(new InvalidErrorTable(InvalidErrorTable.Tag.Second_column_not_a_non_nullable_non_encrypted_non_computed_datetimeoffset_7, new StackTrace(1, true))); }
    517                 col = ref errorTable[2];
    518                 if (!(!col.IsNullable && col.ComputedInfo.IsNone && col.EncryptionInfo.IsNone && col.DataType == typeof(smallint))) { return new(new InvalidErrorTable(InvalidErrorTable.Tag.Third_column_not_a_non_nullable_non_encrypted_non_computed_smallint, new StackTrace(1, true))); }
    519                 col = ref errorTable[3];
    520                 if (!(!col.IsNullable && col.ComputedInfo.IsNone && col.EncryptionInfo.IsNone && col.MaxLength.MapOr(false, _lenIs128) && col.DataType == typeof(nvarchar))) { return new(new InvalidErrorTable(InvalidErrorTable.Tag.Fourth_column_not_a_non_nullable_non_encrypted_non_computed_nvarchar_128, new StackTrace(1, true))); }
    521                 col = ref errorTable[4];
    522                 if (!(col.IsNullable && col.ComputedInfo.IsNone && col.EncryptionInfo.IsNone && col.MaxLength.IsNone && col.DataType == typeof(nvarchar))) { return new(new InvalidErrorTable(InvalidErrorTable.Tag.Fifth_column_not_a_nullable_non_encrypted_non_computed_nvarchar_max, new StackTrace(1, true))); }
    523                 col = ref errorTable[5];
    524                 if (!(col.IsNullable && col.ComputedInfo.IsNone && col.MaxLength.IsNone && col.DataType == typeof(nvarchar))) { return new(new InvalidErrorTable(InvalidErrorTable.Tag.Sixth_column_not_a_nullable_non_computed_nvarchar_max, new StackTrace(1, true))); }
    525                 col = ref errorTable[6];
    526                 if (!(col.IsNullable && col.ComputedInfo.IsNone && col.MaxLength.IsNone && col.DataType == typeof(varbinary))) { return new(new InvalidErrorTable(InvalidErrorTable.Tag.Seventh_column_not_a_nullable_non_computed_varbinary_max, new StackTrace(1, true))); }
    527 
    528                 if (errorTable.ChildObjectCount != ushort.MinValue) {
    529                     return new(new InvalidErrorTable(InvalidErrorTable.Tag.Contains_child_objects, new StackTrace(1, true)));
    530                 } else {
    531                     var errorCopy = errorTable;
    532                     return errorTable.GetClusteredIndex().AndThen(
    533                         (ix) => !(ix.ColumnCount == 3u && !ix.IsUnique && ix[ushort.MinValue].Item0 == errorCopy[ushort.MinValue] && ix[1].Item0 == errorCopy[1] && ix[2].Item0 == errorCopy[2]) ? Maybe<Index>.None() : new(ix)
    534                     ).MapOrElse(
    535                         _invalidErrTable,
    536                         (_) => errorCopy.IndexCount != 1 ? new(new InvalidErrorTable(InvalidErrorTable.Tag.Contains_non_clustered_indexes, new StackTrace(1, true))) : new(new ErrorTable(errorCopy))
    537                     );
    538                 }
    539             }
    540         }
    541         #endregion
    542 
    543         #region Instance Functions
    544         public readonly ErrorTable Clone() => this;
    545         public override readonly bool Equals(object? _) => false;
    546         public override readonly int GetHashCode() => 0;
    547         public readonly Unit Hash<THasher>(ref THasher hasher) where THasher: notnull, IHasher => Value.Hash(ref hasher);
    548         public readonly ErrorTable Into() => this;
    549         public readonly string IntoString() => ToString();
    550         readonly string IInto<string>.Into() => IntoString();
    551         public override readonly string ToString() => Value.IntoString();
    552         readonly Result<ErrorTable, Bottom> ITryInto<ErrorTable, Bottom>.TryInto() => new(this);
    553         readonly Result<string, Bottom> ITryInto<string, Bottom>.TryInto() => new(ToString());
    554         #endregion
    555 
    556         #region Operators
    557         public static bool operator !=(ErrorTable val0, ErrorTable val1) => !(val0 == val1);
    558         public static bool operator ==(ErrorTable val0, ErrorTable val1) => val0.Value == val1.Value;
    559         #endregion
    560 
    561         #region Types
    562         #endregion
    563     }
    564     [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode, Pack = 8, Size = 16)]
    565     public readonly struct InvalidErrorTable: ISum<StackTrace, StackTrace, StackTrace, StackTrace, StackTrace, StackTrace, StackTrace, StackTrace, StackTrace, StackTrace, StackTrace, StackTrace>, IError, IInto<InvalidErrorTable> {
    566 
    567         #region Type-level Constructors
    568         #endregion
    569 
    570         #region Instance Constructors
    571         public InvalidErrorTable() => throw new InvalidOperationException("Parameterless constructor is not allowed to be called!");
    572         internal InvalidErrorTable(Tag flag, StackTrace trace) => (Var, Backtrace) = (flag, trace);
    573         #endregion
    574 
    575         #region Type-level Fields
    576         #endregion
    577 
    578         #region Instance Fields
    579         [FieldOffset(0)] public readonly Tag Var;
    580         [FieldOffset(8)] public readonly StackTrace Backtrace;
    581         #endregion
    582 
    583         #region Type-level Properties
    584         #endregion
    585 
    586         #region Instance Properties
    587         public readonly Var12 Variant => (Var12)Var;
    588         public readonly StackTrace Variant0 => Var == Tag.Database_is_readonly ? Backtrace : throw new InvalidOperationException($"The InvalidErrorTable variant, {Var.ToString()}, is not Database_is_readonly!");
    589         public readonly StackTrace Variant1 => Var == Tag.Does_not_contain_7_columns ? Backtrace : throw new InvalidOperationException($"The InvalidErrorTable variant, {Var.ToString()}, is not Does_not_contain_7_columns!");
    590         public readonly StackTrace Variant2 => Var == Tag.First_column_not_a_non_nullable_non_encrypted_non_computed_nvarchar_128 ? Backtrace : throw new InvalidOperationException($"The InvalidErrorTable variant, {Var.ToString()}, is not First_column_not_a_non_nullable_non_encrypted_non_computed_nvarchar_128!");
    591         public readonly StackTrace Variant3 => Var == Tag.Second_column_not_a_non_nullable_non_encrypted_non_computed_datetimeoffset_7 ? Backtrace : throw new InvalidOperationException($"The InvalidErrorTable variant, {Var.ToString()}, is not Second_column_not_a_non_nullable_non_encrypted_non_computed_datetimeoffset_7!");
    592         public readonly StackTrace Variant4 => Var == Tag.Third_column_not_a_non_nullable_non_encrypted_non_computed_smallint ? Backtrace : throw new InvalidOperationException($"The InvalidErrorTable variant, {Var.ToString()}, is not Third_column_not_a_non_nullable_non_encrypted_non_computed_smallint!");
    593         public readonly StackTrace Variant5 => Var == Tag.Fourth_column_not_a_non_nullable_non_encrypted_non_computed_nvarchar_128 ? Backtrace : throw new InvalidOperationException($"The InvalidErrorTable variant, {Var.ToString()}, is not Third_column_not_a_non_nullable_non_encrypted_non_computed_nvarchar_128!");
    594         public readonly StackTrace Variant6 => Var == Tag.Fifth_column_not_a_nullable_non_encrypted_non_computed_nvarchar_max ? Backtrace : throw new InvalidOperationException($"The InvalidErrorTable variant, {Var.ToString()}, is not Fourth_column_not_a_nullable_non_encrypted_non_computed_nvarchar_max!");
    595         public readonly StackTrace Variant7 => Var == Tag.Sixth_column_not_a_nullable_non_computed_nvarchar_max ? Backtrace : throw new InvalidOperationException($"The InvalidErrorTable variant, {Var.ToString()}, is not Fifth_column_not_a_nullable_non_computed_nvarchar_max!");
    596         public readonly StackTrace Variant8 => Var == Tag.Seventh_column_not_a_nullable_non_computed_varbinary_max ? Backtrace : throw new InvalidOperationException($"The InvalidErrorTable variant, {Var.ToString()}, is not Sixth_column_not_a_nullable_non_computed_varbinary_max!");
    597         public readonly StackTrace Variant9 => Var == Tag.Contains_child_objects ? Backtrace : throw new InvalidOperationException($"The InvalidErrorTable variant, {Var.ToString()}, is not Contains_child_objects!");
    598         public readonly StackTrace Variant10 => Var == Tag.Does_not_contain_non_unique_clustered_index_whose_index_keys_are_the_first_three_columns ? Backtrace : throw new InvalidOperationException($"The InvalidErrorTable variant, {Var.ToString()}, is not Does_not_contain_non_unique_clustered_index_whose_index_keys_are_the_first_three_columns!");
    599         public readonly StackTrace Variant11 => Var == Tag.Contains_non_clustered_indexes ? Backtrace : throw new InvalidOperationException($"The InvalidErrorTable variant, {Var.ToString()}, is not Contains_non_clustered_indexes!");
    600         #endregion
    601 
    602         #region Type-level Functions
    603         #endregion
    604 
    605         #region Instance Functions
    606         public override readonly bool Equals(object? _) => false;
    607         public override readonly int GetHashCode() => 0;
    608         public readonly InvalidErrorTable Into() => this;
    609         public readonly string IntoString() => ToString();
    610         readonly string IInto<string>.Into() => IntoString();
    611         public readonly Maybe<IError> Source() => Maybe<IError>.None();
    612         public readonly Maybe<StackTrace> StackTrace() => new(Backtrace);
    613         public override readonly string ToString() => $"{Var.ToString()}({Backtrace.ToString()})";
    614         readonly Result<InvalidErrorTable, Bottom> ITryInto<InvalidErrorTable, Bottom>.TryInto() => new(this);
    615         readonly Result<string, Bottom> ITryInto<string, Bottom>.TryInto() => new(ToString());
    616         #endregion
    617 
    618         #region Operators
    619         #endregion
    620 
    621         #region Types
    622         public enum Tag: ulong {
    623             Database_is_readonly = ulong.MinValue,
    624             Does_not_contain_7_columns = 1ul,
    625             First_column_not_a_non_nullable_non_encrypted_non_computed_nvarchar_128 = 2ul,
    626             Second_column_not_a_non_nullable_non_encrypted_non_computed_datetimeoffset_7 = 3ul,
    627             Third_column_not_a_non_nullable_non_encrypted_non_computed_smallint = 4ul,
    628             Fourth_column_not_a_non_nullable_non_encrypted_non_computed_nvarchar_128 = 5ul,
    629             Fifth_column_not_a_nullable_non_encrypted_non_computed_nvarchar_max = 6ul,
    630             Sixth_column_not_a_nullable_non_computed_nvarchar_max = 7ul,
    631             Seventh_column_not_a_nullable_non_computed_varbinary_max = 8ul,
    632             Contains_child_objects = 9ul,
    633             Does_not_contain_non_unique_clustered_index_whose_index_keys_are_the_first_three_columns = 10ul,
    634             Contains_non_clustered_indexes = 11ul,
    635         }
    636         #endregion
    637     }
    638     [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode, Pack = 8, Size = 16)]
    639     public readonly struct WriteError: ISum<StackTrace, StackTrace, StackTrace>, IError, IInto<WriteError> {
    640 
    641         #region Type-level Constructors
    642         #endregion
    643 
    644         #region Instance Constructors
    645         public WriteError() => throw new InvalidOperationException("Parameterless constructor is not allowed to be called!");
    646         internal WriteError(Tag flag, StackTrace trace) => (Var, Backtrace) = (flag, trace);
    647         #endregion
    648 
    649         #region Type-level Fields
    650         #endregion
    651 
    652         #region Instance Fields
    653         [FieldOffset(0)] public readonly Tag Var;
    654         [FieldOffset(8)] public readonly StackTrace Backtrace;
    655         #endregion
    656 
    657         #region Type-level Properties
    658         #endregion
    659 
    660         #region Instance Properties
    661         public readonly Var3 Variant => (Var3)Var;
    662         public readonly StackTrace Variant0 => Var == Tag.MaxErrorsExceeded ? Backtrace : throw new InvalidOperationException($"The WriteError variant, {Var.ToString()}, is not MaxErrorsExceeded!");
    663         public readonly StackTrace Variant1 => Var == Tag.TableExpectedToContainEncryptedDataButDoesNot ? Backtrace : throw new InvalidOperationException($"The WriteError variant, {Var.ToString()}, is not TableExpectedToContainEncryptedDataButDoesNot!");
    664         public readonly StackTrace Variant2 => Var == Tag.TableColumnMismatchOrWriteOptionsMustContainAllowEncryptedValueModifications ? Backtrace : throw new InvalidOperationException($"The WriteError variant, {Var.ToString()}, is not TableColumnMismatchOrWriteOptionsMustContainAllowEncryptedValueModifications!");
    665         #endregion
    666 
    667         #region Type-level Functions
    668         #endregion
    669 
    670         #region Instance Functions
    671         public override readonly bool Equals(object? _) => false;
    672         public override readonly int GetHashCode() => 0;
    673         public readonly WriteError Into() => this;
    674         public readonly string IntoString() => ToString();
    675         readonly string IInto<string>.Into() => IntoString();
    676         public readonly Maybe<IError> Source() => Maybe<IError>.None();
    677         public readonly Maybe<StackTrace> StackTrace() => new(Backtrace);
    678         public override readonly string ToString() => $"{Var.ToString()}({Backtrace.ToString()})";
    679         readonly Result<WriteError, Bottom> ITryInto<WriteError, Bottom>.TryInto() => new(this);
    680         readonly Result<string, Bottom> ITryInto<string, Bottom>.TryInto() => new(ToString());
    681         #endregion
    682 
    683         #region Operators
    684         #endregion
    685 
    686         #region Types
    687         public enum Tag: ulong {
    688             MaxErrorsExceeded = ulong.MinValue,
    689             TableExpectedToContainEncryptedDataButDoesNot = 1ul,
    690             TableColumnMismatchOrWriteOptionsMustContainAllowEncryptedValueModifications = 2ul,
    691         }
    692         #endregion
    693     }
    694     [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode, Pack = 8, Size = 16)]
    695     public readonly struct WriteErrorOrTransactionError: ISum<StackTrace, StackTrace, StackTrace, StackTrace, StackTrace, StackTrace, StackTrace>, IError, IInto<WriteErrorOrTransactionError> {
    696 
    697         #region Type-level Constructors
    698         #endregion
    699 
    700         #region Instance Constructors
    701         public WriteErrorOrTransactionError() => throw new InvalidOperationException("Parameterless constructor is not allowed to be called!");
    702         internal WriteErrorOrTransactionError(Tag flag, StackTrace trace) => (Var, Backtrace) = (flag, trace);
    703         #endregion
    704 
    705         #region Type-level Fields
    706         #endregion
    707 
    708         #region Instance Fields
    709         [FieldOffset(0)] public readonly Tag Var;
    710         [FieldOffset(8)] public readonly StackTrace Backtrace;
    711         #endregion
    712 
    713         #region Type-level Properties
    714         #endregion
    715 
    716         #region Instance Properties
    717         public readonly Var7 Variant => (Var7)Var;
    718         public readonly StackTrace Variant0 => Var == Tag.MaxErrorsExceeded ? Backtrace : throw new InvalidOperationException($"The WriteErrorOrTransactionError variant, {Var.ToString()}, is not MaxErrorsExceeded!");
    719         public readonly StackTrace Variant1 => Var == Tag.SqlConnectionIsNotOpen ? Backtrace : throw new InvalidOperationException($"The WriteErrorOrTransactionError variant, {Var.ToString()}, is not SqlConnectionIsNotOpen!");
    720         public readonly StackTrace Variant2 => Var == Tag.SqlConnectionServerMismatch ? Backtrace : throw new InvalidOperationException($"The WriteErrorOrTransactionError variant, {Var.ToString()}, is not SqlConnectionServerMismatch!");
    721         public readonly StackTrace Variant3 => Var == Tag.SqlConnectionViolatesAllowEncryptedValueModifications ? Backtrace : throw new InvalidOperationException($"The WriteErrorOrTransactionError variant, {Var.ToString()}, is not SqlConnectionViolatesAllowEncryptedValueModifications!");
    722         public readonly StackTrace Variant4 => Var == Tag.TableExpectedToContainEncryptedDataButDoesNot ? Backtrace : throw new InvalidOperationException($"The WriteErrorOrTransactionError variant, {Var.ToString()}, is not TableExpectedToContainEncryptedDataButDoesNot!");
    723         public readonly StackTrace Variant5 => Var == Tag.TableColumnMismatchOrWriteOptionsMustContainAllowEncryptedValueModifications ? Backtrace : throw new InvalidOperationException($"The WriteErrorOrTransactionError variant, {Var.ToString()}, is not TableColumnMismatchOrWriteOptionsMustContainAllowEncryptedValueModifications!");
    724         public readonly StackTrace Variant6 => Var == Tag.ContainsEncryptedColumnsButConnectionDoesNotHandleEncryptedColumns ? Backtrace : throw new InvalidOperationException($"The WriteErrorOrTransactionError variant, {Var.ToString()}, is not ContainsEncryptedColumnsButConnectionDoesNotHandleEncryptedColumns!");
    725         #endregion
    726 
    727         #region Type-level Functions
    728         #endregion
    729 
    730         #region Instance Functions
    731         public override readonly bool Equals(object? _) => false;
    732         public override readonly int GetHashCode() => 0;
    733         public readonly WriteErrorOrTransactionError Into() => this;
    734         public readonly string IntoString() => ToString();
    735         readonly string IInto<string>.Into() => IntoString();
    736         public readonly Maybe<IError> Source() => Maybe<IError>.None();
    737         public readonly Maybe<StackTrace> StackTrace() => new(Backtrace);
    738         public override readonly string ToString() => $"{Var.ToString()}({Backtrace.ToString()})";
    739         readonly Result<WriteErrorOrTransactionError, Bottom> ITryInto<WriteErrorOrTransactionError, Bottom>.TryInto() => new(this);
    740         readonly Result<string, Bottom> ITryInto<string, Bottom>.TryInto() => new(ToString());
    741         #endregion
    742 
    743         #region Operators
    744         #endregion
    745 
    746         #region Types
    747         public enum Tag: ulong {
    748             MaxErrorsExceeded = ulong.MinValue,
    749             SqlConnectionIsNotOpen = 1ul,
    750             SqlConnectionServerMismatch = 2ul,
    751             SqlConnectionViolatesAllowEncryptedValueModifications = 3ul,
    752             TableExpectedToContainEncryptedDataButDoesNot = 4ul,
    753             TableColumnMismatchOrWriteOptionsMustContainAllowEncryptedValueModifications = 5ul,
    754             ContainsEncryptedColumnsButConnectionDoesNotHandleEncryptedColumns = 6ul,
    755         }
    756         #endregion
    757     }
    758     public interface IBulkRowError: IError {
    759 
    760         #region Type-level Constructors
    761         #endregion
    762 
    763         #region Instance Constructors
    764         #endregion
    765 
    766         #region Type-level Fields
    767         #endregion
    768 
    769         #region Instance Fields
    770         #endregion
    771 
    772         #region Type-level Properties
    773         #endregion
    774 
    775         #region Instance Properties
    776         public virtual nvarchar Trace => nvarchar.NULL;
    777         public virtual nvarchar Message => nvarchar.NULL;
    778         public virtual varbinary Data => varbinary.NULL;
    779         #endregion
    780 
    781         #region Type-level Functions
    782         #endregion
    783 
    784         #region Instance Functions
    785         #endregion
    786 
    787         #region Operators
    788         #endregion
    789 
    790         #region Types
    791         #endregion
    792     }
    793     [Flags()]
    794     public enum WriteOptions: byte {
    795         Default = byte.MinValue,
    796         KeepIdentity = 1,
    797         CheckConstraints = 2,
    798         TableLock = 4,
    799         KeepNulls = 8,
    800         FireTriggers = 16,
    801         AllowEncryptedValueModifications = 32,
    802     }
    803     public static class Helpers {
    804 
    805         #region Type-level Constructors
    806         static Helpers() {
    807 
    808             _typeMap = HashMap<ActualType, Type, FNVHasher, RandomFNVHashBuilder>.WithCapacityAndHasher(28u, RandomFNVHashBuilder.New());
    809             _ = _typeMap.Insert(typeof(bigint), typeof(long));
    810             _ = _typeMap.Insert(typeof(binary), typeof(byte[]));
    811             _ = _typeMap.Insert(typeof(bit), typeof(bool));
    812             _ = _typeMap.Insert(typeof(@char), typeof(string));
    813             _ = _typeMap.Insert(typeof(date), typeof(DateTime));
    814             _ = _typeMap.Insert(typeof(datetime), typeof(SqlDateTime));
    815             _ = _typeMap.Insert(typeof(datetime2), typeof(DateTime));
    816             _ = _typeMap.Insert(typeof(datetimeoffset), typeof(DateTimeOffset));
    817             _ = _typeMap.Insert(typeof(@decimal), typeof(SqlDecimal));
    818             _ = _typeMap.Insert(typeof(@float), typeof(double));
    819             _ = _typeMap.Insert(typeof(image), typeof(byte[]));
    820             _ = _typeMap.Insert(typeof(@int), typeof(int));
    821             _ = _typeMap.Insert(typeof(money), typeof(SqlMoney));
    822             _ = _typeMap.Insert(typeof(nchar), typeof(string));
    823             _ = _typeMap.Insert(typeof(ntext), typeof(string));
    824             _ = _typeMap.Insert(typeof(nvarchar), typeof(string));
    825             _ = _typeMap.Insert(typeof(real), typeof(float));
    826             _ = _typeMap.Insert(typeof(smalldatetime), typeof(SqlDateTime));
    827             _ = _typeMap.Insert(typeof(smallint), typeof(short));
    828             _ = _typeMap.Insert(typeof(smallmoney), typeof(SqlMoney));
    829             _ = _typeMap.Insert(typeof(sql_variant), typeof(object));
    830             _ = _typeMap.Insert(typeof(text), typeof(string));
    831             _ = _typeMap.Insert(typeof(time), typeof(TimeSpan));
    832             _ = _typeMap.Insert(typeof(tinyint), typeof(byte));
    833             _ = _typeMap.Insert(typeof(uniqueidentifier), typeof(Guid));
    834             _ = _typeMap.Insert(typeof(varbinary), typeof(byte[]));
    835             _ = _typeMap.Insert(typeof(varchar), typeof(string));
    836             _ = _typeMap.Insert(typeof(xml), typeof(string));
    837         }
    838         #endregion
    839 
    840         #region Instance Constructors
    841         #endregion
    842 
    843         #region Type-level Fields
    844         internal static readonly HashMap<ActualType, Type, FNVHasher, RandomFNVHashBuilder> _typeMap;
    845         internal static readonly Fn<NonZeroUshort, int> _nzUshortToInt = (x) => x.Value;
    846         static readonly Fn<Maybe<FilterMap<Prod<Column, Maybe<ColumnSort>>, Std.Vec.IntoIterator<Prod<Column, Maybe<ColumnSort>>>, SqlBulkCopyColumnOrderHint>>> _noFilterMap = () => Maybe<FilterMap<Prod<Column, Maybe<ColumnSort>>, Std.Vec.IntoIterator<Prod<Column, Maybe<ColumnSort>>>, SqlBulkCopyColumnOrderHint>>.None();
    847         internal static readonly Fn<WriteErrorOrTransactionError, WriteError> _writeTxnErrToWriteErr = (err) => new WriteError(WriteError.Tag.MaxErrorsExceeded, err.Backtrace);
    848         #endregion
    849 
    850         #region Instance Fields
    851         #endregion
    852 
    853         #region Type-level Properties
    854         #endregion
    855 
    856         #region Instance Properties
    857         #endregion
    858 
    859         #region Type-level Functions
    860         public static Result<Unit, StringBuilder> BulkWriteFinish<TBulkWriter>(TBulkWriter bulkWriter, UserTable destination, ulong initialBulkTableCount, Maybe<NonZeroUshort> timeout, SessionOptions options, bool identityINSERT, bool commitIfErr) where TBulkWriter: struct, IBulkWriter {
    861 
    862             using var con = Functions.CreateOpenedConnection(in destination.Schema.Database, options, false, Maybe<Uri>.None());
    863             using var txn = con.BeginTransaction(IsolationLevel.Serializable);
    864             return BulkWriteFinish(bulkWriter, destination, initialBulkTableCount, txn, timeout, identityINSERT).MapErr((e) => { if (commitIfErr) { txn.Commit(); } else { txn.Rollback(); } return e; }).Map((_) => { txn.Commit(); return new Unit(); });
    865         }
    866         // Attempts to INSERT rows from bulkWriter.Destination to destination avoiding any PRIMARY KEY CONSTRAINT, UNIQUE CONSTRAINT, or UNIQUE INDEX violation.
    867         // If this succeeds, then bulkWriter.Destination is TRUNCATEd iff initialBulkTableCount is 0 and bulkWriter.CurrentSuccessfullyProcessedCount = the count returned from the INSERT into destination.
    868         // If that succeeds, then an error message is generated iff initialBulkTableCount is not 0, bulkWriter.CurrentErrorCount > 0, or bulkWriter.CurrentSuccessfullyProcessedCount ≠ the INSERT count.
    869         public static Result<Unit, StringBuilder> BulkWriteFinish<TBulkWriter>(TBulkWriter bulkWriter, UserTable destination, ulong initialBulkTableCount, SqlTransaction txn, Maybe<NonZeroUshort> timeout, bool identityINSERT) where TBulkWriter: struct, IBulkWriter => bulkWriter.Destination.INSERT_INTO_AVOID_UNIQUE_VIOLATIONS(in destination, timeout, txn, identityINSERT).MapErr((e) => new StringBuilder(e.IntoString(), 512)).AndThen((count) => (count == bulkWriter.CurrentSuccessfullyProcessedCount && initialBulkTableCount == ulong.MinValue ? bulkWriter.Destination.TRUNCATE(timeout, txn).MapErr((e) => new StringBuilder(e.IntoString(), 512)) : new(new Unit())).AndThen((_) => {
    870             var err = (bulkWriter.CurrentErrorCount > ulong.MinValue ? new StringBuilder($@"There were {bulkWriter.CurrentErrorCount.ToString()} errors when bulk-loading into {bulkWriter.Destination.IntoString()}.
    871 This represents ≈ {bulkWriter.CurrentErrorRatio.ToString("N2")} of the total {bulkWriter.CurrentProcessedCount.ToString()} rows that were processed.
    872 {bulkWriter.ErrTable.MapOr(string.Empty, (e) => $@"Query {e.IntoString()} for more information.
    873 ")}", 512) : new StringBuilder(128)).Append(initialBulkTableCount != ulong.MinValue ? $@"{initialBulkTableCount.ToString()} rows already existed in {bulkWriter.Destination.IntoString()} before processing new rows. This table should be TRUNCATEd after every time it is populated and any issues that may have occurred are resolved.
    874 " : string.Empty).Append(count != bulkWriter.CurrentSuccessfullyProcessedCount ? $@"{bulkWriter.CurrentSuccessfullyProcessedCount.ToString()} rows were successfully processed, but {count.ToString()} rows were subsequently INSERTed into {destination.IntoString()}. A PRIMARY KEY CONSTRAINT, UNIQUE CONSTRAINT, or UNIQUE INDEX violation was avoided or there were existing rows in {bulkWriter.Destination.IntoString()} that were also INSERTed.
    875 " : string.Empty);
    876             return err.Length == 0 ? new Result<Unit, StringBuilder>(new Unit()) : new(err);
    877         }));
    878         public static Result<Unit, StringBuilder> BulkWriteFinishAlwaysTRUNCATE<TBulkWriter>(TBulkWriter bulkWriter, UserTable destination, SqlTransaction txn, Maybe<NonZeroUshort> timeout, bool identityINSERT) where TBulkWriter: struct, IBulkWriter => bulkWriter.Destination.INSERT_INTO_AVOID_UNIQUE_VIOLATIONS(in destination, timeout, txn, identityINSERT).MapErr((e) => new StringBuilder(e.IntoString(), 512)).AndThen((count) => bulkWriter.Destination.TRUNCATE(timeout, txn).MapErr((e) => new StringBuilder(e.IntoString(), 512)).AndThen((_) => {
    879             var err = (bulkWriter.CurrentErrorCount > ulong.MinValue ? new StringBuilder($@"There were {bulkWriter.CurrentErrorCount.ToString()} errors when bulk-loading into {bulkWriter.Destination.IntoString()}.
    880 This represents ≈ {bulkWriter.CurrentErrorRatio.ToString("N2")} of the total {bulkWriter.CurrentProcessedCount.ToString()} rows that were processed.
    881 {bulkWriter.ErrTable.MapOr(string.Empty, (e) => $@"Query {e.IntoString()} for more information.
    882 ")}", 512) : new StringBuilder(128)).Append(count != bulkWriter.CurrentSuccessfullyProcessedCount ? $@"{bulkWriter.CurrentSuccessfullyProcessedCount.ToString()} rows were successfully processed, but {count.ToString()} rows were subsequently INSERTed into {destination.IntoString()}. A PRIMARY KEY CONSTRAINT, UNIQUE CONSTRAINT, or UNIQUE INDEX violation was avoided or there were existing rows in {bulkWriter.Destination.IntoString()} that were also INSERTed.
    883 " : string.Empty);
    884             return err.Length == 0 ? new Result<Unit, StringBuilder>(new Unit()) : new(err);
    885         }));
    886         // MUST ensure that txn is null or using con as its SqlConnection!
    887         internal static SqlBulkCopy CreateBulkCopy(in UserTable table, WriteOptions writeOptions, Maybe<NonZeroUshort> batchSize, Maybe<NonZeroUshort> timeout, bool enableStreaming, bool isSortedAccordingToClusteredIndex, SqlConnection con, SqlTransaction? txn) {
    888 
    889             SqlBulkCopy blk = new(con, (SqlBulkCopyOptions)writeOptions, txn) { BatchSize = batchSize.MapOr(0, _nzUshortToInt), BulkCopyTimeout = timeout.MapOr(0, _nzUshortToInt), DestinationTableName = $"[{table.Schema.Database.Name.Value}].[{table.Schema.Name.Value}].[{table.Name}]", EnableStreaming = enableStreaming };
    890             var cols = table.IntoIter();
    891             Maybe<Column> maybeCol;
    892             Column col;
    893 
    894             while ((maybeCol = cols.Next()).IsSome) {
    895                 col = maybeCol.Unwrap();
    896                 if (col.ComputedInfo.IsNone) { _ = blk.ColumnMappings.Add(col.OrdinalPosition, col.OrdinalPosition); }
    897             }
    898             // If we are told the data set is sorted and a clustered index exists, inform blk; otherwise do nothing.
    899             // Also note that IDENTITY keys MUST be ignored if IDENTITY values are not kept.
    900             var dontKeepIdentity = (writeOptions & WriteOptions.KeepIdentity) != WriteOptions.KeepIdentity;
    901             _ = (isSortedAccordingToClusteredIndex
    902                     ? table.GetClusteredIndex().MapOrElse(
    903                         _noFilterMap,
    904                         (ix) => new Maybe<FilterMap<Prod<Column, Maybe<ColumnSort>>, Std.Vec.IntoIterator<Prod<Column, Maybe<ColumnSort>>>, SqlBulkCopyColumnOrderHint>>(ix.IntoIter().FilterMap<Std.Vec.IntoIterator<Prod<Column, Maybe<ColumnSort>>>, Prod<Column, Maybe<ColumnSort>>, SqlBulkCopyColumnOrderHint>(
    905                                 (tup) => tup.Item0.IdentityInfo.IsSome && dontKeepIdentity ? Maybe<SqlBulkCopyColumnOrderHint>.None() : new(new(tup.Item0.Name, tup.Item1.Unwrap().Var == ColumnSort.Tag.Descending ? SortOrder.Descending : SortOrder.Ascending)))
    906                               )
    907                       )
    908                     : Maybe<FilterMap<Prod<Column, Maybe<ColumnSort>>, Std.Vec.IntoIterator<Prod<Column, Maybe<ColumnSort>>>, SqlBulkCopyColumnOrderHint>>.None()
    909                 ).MapOr(
    910                     new Unit(),
    911                     (hintIter) => {
    912                         Maybe<SqlBulkCopyColumnOrderHint> val;
    913                         while ((val = hintIter.Next()).IsSome) { _ = blk.ColumnOrderHints.Add(val.Unwrap()); }
    914                         return new Unit();
    915                     }
    916                   );
    917             return blk;
    918         }
    919         // Returns None iff all non-encrypted types match and all encrypted types are modeled directly as a byte[] and where one of which does not have a varbinary plaintext type.
    920         // Otherwise returns Some(true) iff all types match or Some(false) if there is at least one type mismatch.
    921         internal static Maybe<bool> TypeMatch(FromFn<Type> types, in UserTable table) {
    922 
    923             var columns = table.IntoIter();
    924             var encryptedAsBytes = false;
    925             var encrypted = false;
    926             return (columns.All((Column c) => types.Next().MapOr(false, (col) => {
    927                 // The only time a type mismatch is allowed is in the event a byte[] is being used for an encrypted non-varbinary column.
    928                 // This is due to the fact that the bulk writers allow one to directly write encrypted data into a table bypassing the need
    929                 // to encrypt the data. Note that directly writing encrypted data via a bulk writer must apply to ALL encrypted columns
    930                 // (i.e., either all the encrypted columns are written directly in their already encrypted form or none of them are).
    931                 // Also note that the bulk writers still have to ensure that SqlBulkCopyOptions.AllowEncryptedValueModifications is being used to write to the table.
    932                 if (c.EncryptionInfo.IsSome) {
    933 
    934                     if (encryptedAsBytes) {
    935                         return col == typeof(varbinary);
    936                     } else if (encrypted) {
    937                         return col == c.DataType;
    938                     } else if (col == typeof(varbinary)) {
    939                         // Since the plaintext data type may also be a varbinary column, we can only say
    940                         // the column is meant to represent encrypted data when the actual data type is not a varbinary.
    941                         if (c.DataType != col) { encryptedAsBytes = true; }
    942                         return true;
    943                     } else {
    944                         // Since varbinary is not the intended data type of the column,
    945                         // we know all encrypted columns require the data to be encrypted.
    946                         encrypted = true;
    947                         return col == c.DataType;
    948                     }
    949                 } else {
    950                     return col == c.DataType;
    951                 }
    952             })) && types.Next().IsNone) ? encryptedAsBytes ? Maybe<bool>.None() : new(true) : new(false);
    953         }
    954         // MUST ensure that processName and userName are no more than 128 chars in length before calling!
    955         internal static Unit WriteErrors(in ErrorTable table, ref Vec<Prod<nvarchar, nvarchar, varbinary>> errs, string processName, string userName, ushort exclusiveMinCount) {
    956 
    957             if (errs.Len > exclusiveMinCount) {
    958                 using var con = Functions.CreateOpenedConnection(in table.Value.Schema.Database, SessionOptions.DEFAULT, table.ContainsEncrypted, Maybe<Uri>.None());
    959                 using SqlBulkCopy blk = new(con, SqlBulkCopyOptions.Default, null) { BatchSize = (int)errs.Len, BulkCopyTimeout = 600, DestinationTableName = $"[{table.Value.Schema.Name.Value}].[{table.Value.Name}]", EnableStreaming = true };
    960                 _ = blk.ColumnMappings.Add(0, 0);
    961                 _ = blk.ColumnMappings.Add(1, 1);
    962                 _ = blk.ColumnMappings.Add(2, 2);
    963                 _ = blk.ColumnMappings.Add(3, 3);
    964                 _ = blk.ColumnMappings.Add(4, 4);
    965                 _ = blk.ColumnMappings.Add(5, 5);
    966                 _ = blk.ColumnMappings.Add(6, 6);
    967                 using var rdr = new ErrorReader(table, errs, processName, userName);
    968                 blk.WriteToServer(rdr);
    969                 return errs.Clear();
    970             }
    971             return new Unit();
    972         }
    973         #endregion
    974 
    975         #region Instance Functions
    976         #endregion
    977 
    978         #region Operators
    979         #endregion
    980 
    981         #region Types
    982         #endregion
    983     }
    984     public interface IBulkWriter {
    985 
    986         #region Type-level Constructors
    987         #endregion
    988 
    989         #region Instance Constructors
    990         #endregion
    991 
    992         #region Type-level Fields
    993         #endregion
    994 
    995         #region Instance Fields
    996         #endregion
    997 
    998         #region Type-level Properties
    999         #endregion
   1000 
   1001         #region Instance Properties
   1002         public abstract Maybe<ErrorTable> ErrTable { get; }
   1003         public abstract UserTable Destination { get; }
   1004         public abstract Prod<ulong, double> MaxErrorsAllowed { get; }
   1005         public abstract ulong CurrentErrorCount { get; }
   1006         public abstract ulong CurrentProcessedCount { get; }
   1007         public abstract ulong CurrentSuccessfullyProcessedCount { get; }
   1008         public abstract double CurrentErrorRatio { get; }
   1009         public abstract bool IsInError { get; }
   1010         #endregion
   1011 
   1012         #region Type-level Functions
   1013         #endregion
   1014 
   1015         #region Instance Functions
   1016         internal abstract void Sealed();
   1017         #endregion
   1018 
   1019         #region Operators
   1020         #endregion
   1021 
   1022         #region Types
   1023         #endregion
   1024     }
   1025     #endregion
   1026 
   1027     #region Namespaces
   1028     #endregion
   1029 }
   1030 #endregion