Server.cs (233252B)
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.Collections.HashSet; 8 using Std.Convert; 9 using Std.Error; 10 using Std.Hashing; 11 using Std.Iter; 12 using Std.Maybe; 13 using Std.Num; 14 using Std.Ops; 15 using Std.Result; 16 using Std.Vec; 17 using Std.Wrappers; 18 using System; 19 using System.Data; 20 using System.Data.SqlTypes; 21 using System.Diagnostics; 22 using System.Globalization; 23 using System.Runtime.InteropServices; 24 using System.Text; 25 using XXHash; 26 #region Namespaces 27 namespace SQLServer { 28 #region Types 29 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 0)] 30 public readonly struct CHECK_CONSTRAINT: IClone<CHECK_CONSTRAINT>, IEq<CHECK_CONSTRAINT>, IHashable, IInto<CHECK_CONSTRAINT>, IInto<string>, IObject { 31 32 #region Type-level Constructors 33 #endregion 34 35 #region Instance Constructors 36 public CHECK_CONSTRAINT() => throw new InvalidOperationException("Parameterless constructor is not allowed to be called!"); 37 internal CHECK_CONSTRAINT(Schema schema, string name, int objectID, int parentObjectID, bool isDisabled, bool isNotTrusted, int parentColumnID, string? definition, bool usesDatabaseCollation) => (Schema, Name, ObjectID, ParentObjectID, IsDisabled, IsNotTrusted, ParentColumnID, _definition, UsesDatabaseCollation) = (schema, name, objectID, parentObjectID, isDisabled, isNotTrusted, parentColumnID, definition, usesDatabaseCollation); 38 #endregion 39 40 #region Type-level Fields 41 #endregion 42 43 #region Instance Fields 44 public readonly Schema Schema; 45 public readonly string Name; 46 public readonly int ObjectID; 47 public readonly int ParentObjectID; 48 public readonly bool IsDisabled; 49 public readonly bool IsNotTrusted; 50 public readonly int ParentColumnID; 51 internal readonly string? _definition; 52 public readonly bool UsesDatabaseCollation; 53 #endregion 54 55 #region Type-level Properties 56 #endregion 57 58 #region Instance Properties 59 public readonly Maybe<string> Definition => _definition is null ? Maybe<string>.None() : new(_definition); 60 readonly Schema IObject.Schema => Schema; 61 readonly string IObject.Name => Name; 62 readonly int IObject.ObjectID => ObjectID; 63 #endregion 64 65 #region Type-level Functions 66 #endregion 67 68 #region Instance Functions 69 public readonly CHECK_CONSTRAINT Clone() => this; 70 public override readonly bool Equals(object? _) => false; 71 public override readonly int GetHashCode() => 0; 72 public readonly Unit Hash<THasher>(ref THasher hasher) where THasher: notnull, IHasher { 73 74 _ = Schema.Hash(ref hasher); 75 return hasher.WriteInt(ObjectID); 76 } 77 public readonly CHECK_CONSTRAINT Into() => this; 78 public readonly string IntoString() => ToString(); 79 readonly string IInto<string>.Into() => IntoString(); 80 readonly void IObject.Sealed() { } 81 public override readonly string ToString() => Name; 82 readonly Result<CHECK_CONSTRAINT, Bottom> ITryInto<CHECK_CONSTRAINT, Bottom>.TryInto() => new(this); 83 readonly Result<string, Bottom> ITryInto<string, Bottom>.TryInto() => new(ToString()); 84 #endregion 85 86 #region Operators 87 public static bool operator !=(CHECK_CONSTRAINT val0, CHECK_CONSTRAINT val1) => !(val0 == val1); 88 public static bool operator ==(CHECK_CONSTRAINT val0, CHECK_CONSTRAINT val1) => val0.Schema == val1.Schema && val0.ObjectID == val1.ObjectID; 89 #endregion 90 91 #region Types 92 #endregion 93 } 94 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 0)] 95 public readonly struct Column: IClone<Column>, IEq<Column>, IHashable, IInto<Column>, IInto<string> { 96 97 #region Type-level Constructors 98 #endregion 99 100 #region Instance Constructors 101 public Column() => throw new InvalidOperationException("Parameterless constructor is not allowed to be called!"); 102 internal Column(string name, bool isNullable, Maybe<ushort> maxLength, Maybe<byte> precision, Maybe<byte> scale, bool isFilestream, Type dataType, Maybe<ColumnCollationInfo> collationInfo, Maybe<ColumnIdentityInfo> identityInfo, Maybe<ColumnComputedInfo> computedInfo, Maybe<ColumnEncryptionInfo> encryptionInfo, ushort ordinalPosition, int columnID) => (Name, IsNullable, MaxLength, Precision, Scale, IsFilestream, DataType, CollationInfo, IdentityInfo, ComputedInfo, EncryptionInfo, OrdinalPosition, ColumnID) = (name, isNullable, maxLength, precision, scale, isFilestream, dataType, collationInfo, identityInfo, computedInfo, encryptionInfo, ordinalPosition, columnID); 103 #endregion 104 105 #region Type-level Fields 106 #endregion 107 108 #region Instance Fields 109 public readonly Maybe<ColumnIdentityInfo> IdentityInfo; 110 public readonly Maybe<ushort> MaxLength; 111 public readonly Maybe<byte> Precision; 112 public readonly Maybe<byte> Scale; 113 public readonly string Name; 114 public readonly Type DataType; 115 public readonly Maybe<ColumnCollationInfo> CollationInfo; 116 public readonly Maybe<ColumnComputedInfo> ComputedInfo; 117 public readonly Maybe<ColumnEncryptionInfo> EncryptionInfo; 118 public readonly ushort OrdinalPosition; 119 public readonly bool IsNullable; 120 public readonly bool IsFilestream; 121 public readonly int ColumnID; 122 #endregion 123 124 #region Type-level Properties 125 #endregion 126 127 #region Instance Properties 128 #endregion 129 130 #region Type-level Functions 131 #endregion 132 133 #region Instance Functions 134 public readonly Column Clone() => this; 135 public readonly bool EqIgnoreComputedColumnDefinitionIfNone(in Column other) => string.Equals(Name, other.Name, StringComparison.Ordinal) && OrdinalPosition == other.OrdinalPosition && IsNullable == other.IsNullable && MaxLength.EqualsEquatable(in other.MaxLength) && Precision.EqualsEquatable(in other.Precision) && Scale.EqualsEquatable(in other.Scale) && IsFilestream == other.IsFilestream && DataType == other.DataType && CollationInfo.Eq(in other.CollationInfo) && IdentityInfo.Eq(in other.IdentityInfo) && ((ComputedInfo.IsNone && other.ComputedInfo.IsNone) || (ComputedInfo.IsSome && other.ComputedInfo.IsSome && ComputedInfo.Unwrap().EqIgnoreDefinitionIfNone(other.ComputedInfo.Unwrap()))) && EncryptionInfo.Eq(in other.EncryptionInfo); 136 public readonly bool EqIgnoreEncryptionInfo(in Column other) => string.Equals(Name, other.Name, StringComparison.Ordinal) && OrdinalPosition == other.OrdinalPosition && IsNullable == other.IsNullable && MaxLength.EqualsEquatable(in other.MaxLength) && Precision.EqualsEquatable(in other.Precision) && Scale.EqualsEquatable(in other.Scale) && IsFilestream == other.IsFilestream && DataType == other.DataType && CollationInfo.Eq(in other.CollationInfo) && IdentityInfo.Eq(in other.IdentityInfo) && ComputedInfo.Eq(in other.ComputedInfo); 137 public readonly bool EqIgnoreIDENTITYProperty(in Column other) => string.Equals(Name, other.Name, StringComparison.Ordinal) && OrdinalPosition == other.OrdinalPosition && IsNullable == other.IsNullable && MaxLength.EqualsEquatable(in other.MaxLength) && Precision.EqualsEquatable(in other.Precision) && Scale.EqualsEquatable(in other.Scale) && IsFilestream == other.IsFilestream && DataType == other.DataType && CollationInfo.Eq(in other.CollationInfo) && ComputedInfo.Eq(in other.ComputedInfo) && EncryptionInfo.Eq(in other.EncryptionInfo); 138 public override readonly bool Equals(object? _) => false; 139 public override readonly int GetHashCode() => 0; 140 public readonly Unit Hash<THasher>(ref THasher hasher) where THasher: notnull, IHasher { 141 142 _ = hasher.WriteSliceUnsafe(Name.AsSpan()); 143 _ = hasher.WriteByte(IsNullable ? (byte)1 : byte.MinValue); 144 if (MaxLength.IsNone) { 145 _ = hasher.WriteByte(byte.MinValue); 146 } else { 147 _ = hasher.WriteByte(byte.MaxValue); 148 _ = hasher.WriteUshort(MaxLength.Unwrap()); 149 } 150 // OK since Maybe<byte> is exactly two bytes. 151 _ = hasher.WriteUnsafe(Precision); 152 _ = hasher.WriteUnsafe(Scale); 153 _ = hasher.WriteByte(IsFilestream ? (byte)1 : byte.MinValue); 154 _ = hasher.WriteInt(DataType.GetHashCode()); 155 _ = CollationInfo.Hash(ref hasher); 156 _ = IdentityInfo.Hash(ref hasher); 157 _ = ComputedInfo.Hash(ref hasher); 158 _ = EncryptionInfo.Hash(ref hasher); 159 return hasher.WriteUshort(OrdinalPosition); 160 } 161 public readonly Column Into() => this; 162 public readonly string IntoString() => ToString(); 163 readonly string IInto<string>.Into() => IntoString(); 164 public override readonly string ToString() => Name; 165 readonly Result<Column, Bottom> ITryInto<Column, Bottom>.TryInto() => new(this); 166 readonly Result<string, Bottom> ITryInto<string, Bottom>.TryInto() => new(ToString()); 167 #endregion 168 169 #region Operators 170 public static bool operator !=(Column val0, Column val1) => !(val0 == val1); 171 public static bool operator ==(Column val0, Column val1) => string.Equals(val0.Name, val1.Name, StringComparison.Ordinal) && val0.OrdinalPosition == val1.OrdinalPosition && val0.IsNullable == val1.IsNullable && val0.MaxLength.EqualsEquatable(in val1.MaxLength) && val0.Precision.EqualsEquatable(in val1.Precision) && val0.Scale.EqualsEquatable(in val1.Scale) && val0.IsFilestream == val1.IsFilestream && val0.DataType == val1.DataType && val0.CollationInfo.Eq(in val1.CollationInfo) && val0.IdentityInfo.Eq(in val1.IdentityInfo) && val0.ComputedInfo.Eq(in val1.ComputedInfo) && val0.EncryptionInfo.Eq(in val1.EncryptionInfo); 172 #endregion 173 174 #region Types 175 #endregion 176 } 177 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 0)] 178 public readonly struct ColumnCollationInfo: IClone<ColumnCollationInfo>, IHashable, IInto<ColumnCollationInfo>, IInto<string>, IEq<ColumnCollationInfo> { 179 180 #region Type-level Constructors 181 #endregion 182 183 #region Instance Constructors 184 public ColumnCollationInfo() => throw new InvalidOperationException("Parameterless constructor is not allowed to be called!"); 185 internal ColumnCollationInfo(string collation, CultureInfo cultureInfo, CompareOptions compareOptions) => (Collation, CultureInfo, CompareOptions) = (collation, cultureInfo, compareOptions); 186 #endregion 187 188 #region Type-level Fields 189 #endregion 190 191 #region Instance Fields 192 public readonly string Collation; 193 public readonly CultureInfo CultureInfo; 194 public readonly CompareOptions CompareOptions; 195 #endregion 196 197 #region Type-level Properties 198 #endregion 199 200 #region Instance Properties 201 #endregion 202 203 #region Type-level Functions 204 #endregion 205 206 #region Instance Functions 207 public readonly ColumnCollationInfo Clone() => this; 208 public override readonly bool Equals(object? _) => false; 209 public override readonly int GetHashCode() => 0; 210 public readonly Unit Hash<THasher>(ref THasher hasher) where THasher: notnull, IHasher => hasher.WriteSliceUnsafe(Collation.AsSpan()); 211 public readonly ColumnCollationInfo Into() => this; 212 public readonly string IntoString() => ToString(); 213 readonly string IInto<string>.Into() => IntoString(); 214 public override readonly string ToString() => Collation; 215 readonly Result<ColumnCollationInfo, Bottom> ITryInto<ColumnCollationInfo, Bottom>.TryInto() => new(this); 216 readonly Result<string, Bottom> ITryInto<string, Bottom>.TryInto() => new(ToString()); 217 #endregion 218 219 #region Operators 220 public static bool operator !=(ColumnCollationInfo val0, ColumnCollationInfo val1) => !(val0 == val1); 221 public static bool operator ==(ColumnCollationInfo val0, ColumnCollationInfo val1) => string.Equals(val0.Collation, val1.Collation, StringComparison.Ordinal); 222 #endregion 223 224 #region Types 225 #endregion 226 } 227 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 0)] 228 public readonly struct ColumnComputedInfo: IClone<ColumnComputedInfo>, IHashable, IInto<ColumnComputedInfo>, IInto<string>, IEq<ColumnComputedInfo> { 229 230 #region Type-level Constructors 231 #endregion 232 233 #region Instance Constructors 234 public ColumnComputedInfo() => throw new InvalidOperationException("Parameterless constructor is not allowed to be called!"); 235 internal ColumnComputedInfo(bool isPersisted, bool usesDatabaseCollation, string? definition) => (IsPersisted, UsesDatabaseCollation, _definition) = (isPersisted, usesDatabaseCollation, definition); 236 #endregion 237 238 #region Type-level Fields 239 #endregion 240 241 #region Instance Fields 242 readonly string? _definition; 243 public readonly bool IsPersisted; 244 public readonly bool UsesDatabaseCollation; 245 #endregion 246 247 #region Type-level Properties 248 #endregion 249 250 #region Instance Properties 251 public readonly Maybe<string> Definition => _definition is null ? Maybe<string>.None() : new(_definition); 252 #endregion 253 254 #region Type-level Functions 255 #endregion 256 257 #region Instance Functions 258 public readonly ColumnComputedInfo Clone() => this; 259 public readonly bool EqIgnoreDefinitionIfNone(in ColumnComputedInfo other) => IsPersisted == other.IsPersisted && UsesDatabaseCollation == other.UsesDatabaseCollation && (string.Equals(_definition, other._definition, StringComparison.Ordinal) || _definition is null || other._definition is null); 260 public override readonly bool Equals(object? _) => false; 261 public override readonly int GetHashCode() => 0; 262 public readonly Unit Hash<THasher>(ref THasher hasher) where THasher: notnull, IHasher { 263 264 _ = hasher.WriteByte(IsPersisted ? (byte)1 : byte.MinValue); 265 _ = hasher.WriteByte(UsesDatabaseCollation ? (byte)1 : byte.MinValue); 266 if (_definition is null) { 267 return hasher.WriteByte(byte.MinValue); 268 } else { 269 _ = hasher.WriteByte(byte.MaxValue); 270 return hasher.WriteSliceUnsafe(_definition.AsSpan()); 271 } 272 } 273 public readonly ColumnComputedInfo Into() => this; 274 public readonly string IntoString() => ToString(); 275 readonly string IInto<string>.Into() => IntoString(); 276 public override readonly string ToString() => _definition is null ? string.Empty : _definition; 277 readonly Result<ColumnComputedInfo, Bottom> ITryInto<ColumnComputedInfo, Bottom>.TryInto() => new(this); 278 readonly Result<string, Bottom> ITryInto<string, Bottom>.TryInto() => new(ToString()); 279 #endregion 280 281 #region Operators 282 public static bool operator !=(ColumnComputedInfo val0, ColumnComputedInfo val1) => !(val0 == val1); 283 public static bool operator ==(ColumnComputedInfo val0, ColumnComputedInfo val1) => val0.IsPersisted == val1.IsPersisted && val0.UsesDatabaseCollation == val1.UsesDatabaseCollation && string.Equals(val0._definition, val1._definition, StringComparison.Ordinal); 284 #endregion 285 286 #region Types 287 #endregion 288 } 289 [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode, Pack = 1, Size = 1)] 290 public readonly struct ColumnEncryptionAlgorithm: IClone<ColumnEncryptionAlgorithm>, IHashable, IInto<ColumnEncryptionAlgorithm>, IInto<string>, IEquality<ColumnEncryptionAlgorithm> { 291 292 #region Type-level Constructors 293 #endregion 294 295 #region Instance Constructors 296 public ColumnEncryptionAlgorithm() { } 297 #endregion 298 299 #region Type-level Fields 300 public static readonly ColumnEncryptionAlgorithm AEAD_AES_256_CBC_HMAC_SHA_256 = new(); 301 #endregion 302 303 #region Instance Fields 304 #endregion 305 306 #region Type-level Properties 307 #endregion 308 309 #region Instance Properties 310 #endregion 311 312 #region Type-level Functions 313 #endregion 314 315 #region Instance Functions 316 public readonly ColumnEncryptionAlgorithm Clone() => this; 317 public readonly bool Equals(in ColumnEncryptionAlgorithm _) => true; 318 public override readonly bool Equals(object? _) => false; 319 public override readonly int GetHashCode() => 0; 320 public readonly Unit Hash<THasher>(ref THasher _) where THasher: notnull, IHasher => new Unit(); 321 public readonly ColumnEncryptionAlgorithm Into() => this; 322 public readonly string IntoString() => ToString(); 323 readonly string IInto<string>.Into() => IntoString(); 324 public override readonly string ToString() => "AEAD_AES_256_CBC_HMAC_SHA_256"; 325 readonly Result<ColumnEncryptionAlgorithm, Bottom> ITryInto<ColumnEncryptionAlgorithm, Bottom>.TryInto() => new(this); 326 readonly Result<string, Bottom> ITryInto<string, Bottom>.TryInto() => new(ToString()); 327 #endregion 328 329 #region Operators 330 public static bool operator !=(ColumnEncryptionAlgorithm _, ColumnEncryptionAlgorithm _1) => false; 331 public static bool operator ==(ColumnEncryptionAlgorithm _, ColumnEncryptionAlgorithm _1) => true; 332 #endregion 333 334 #region Types 335 #endregion 336 } 337 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 0)] 338 public readonly struct ColumnEncryptionInfo: IClone<ColumnEncryptionInfo>, IHashable, IInto<ColumnEncryptionInfo>, IInto<string>, IEq<ColumnEncryptionInfo> { 339 340 #region Type-level Constructors 341 #endregion 342 343 #region Instance Constructors 344 public ColumnEncryptionInfo() => throw new InvalidOperationException("Parameterless constructor is not allowed to be called!"); 345 internal ColumnEncryptionInfo(string name, ColumnEncryptionType type, ColumnEncryptionAlgorithm algo, bool isEnclave) => (KeyName, Type, Algorithm, IsEnclaveEnabled) = (name, type, algo, isEnclave); 346 #endregion 347 348 #region Type-level Fields 349 #endregion 350 351 #region Instance Fields 352 public readonly string KeyName; 353 public readonly bool IsEnclaveEnabled; 354 public readonly ColumnEncryptionType Type; 355 public readonly ColumnEncryptionAlgorithm Algorithm; 356 #endregion 357 358 #region Type-level Properties 359 #endregion 360 361 #region Instance Properties 362 #endregion 363 364 #region Type-level Functions 365 #endregion 366 367 #region Instance Functions 368 public readonly ColumnEncryptionInfo Clone() => this; 369 public override readonly bool Equals(object? _) => false; 370 public override readonly int GetHashCode() => 0; 371 public readonly Unit Hash<THasher>(ref THasher hasher) where THasher: notnull, IHasher { 372 373 _ = hasher.WriteSliceUnsafe(KeyName.AsSpan()); 374 _ = hasher.WriteByte(IsEnclaveEnabled ? (byte)1 : byte.MinValue); 375 _ = Type.Hash(ref hasher); 376 return Algorithm.Hash(ref hasher); 377 } 378 public readonly ColumnEncryptionInfo Into() => this; 379 public readonly string IntoString() => ToString(); 380 readonly string IInto<string>.Into() => IntoString(); 381 public override readonly string ToString() => Type.IntoString(); 382 readonly Result<ColumnEncryptionInfo, Bottom> ITryInto<ColumnEncryptionInfo, Bottom>.TryInto() => new(this); 383 readonly Result<string, Bottom> ITryInto<string, Bottom>.TryInto() => new(ToString()); 384 #endregion 385 386 #region Operators 387 public static bool operator !=(ColumnEncryptionInfo val0, ColumnEncryptionInfo val1) => !(val0 == val1); 388 public static bool operator ==(ColumnEncryptionInfo val0, ColumnEncryptionInfo val1) => string.Equals(val0.KeyName, val1.KeyName, StringComparison.Ordinal) && val0.Type == val1.Type && val0.Algorithm == val1.Algorithm && val0.IsEnclaveEnabled == val1.IsEnclaveEnabled; 389 #endregion 390 391 #region Types 392 #endregion 393 } 394 [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode, Pack = 1, Size = 1)] 395 public readonly struct ColumnEncryptionType: ISum<ColumnEncryptionType.Tag, ColumnEncryptionType.Tag>, IClone<ColumnEncryptionType>, IHashable, IInto<ColumnEncryptionType>, IInto<string>, IEquality<ColumnEncryptionType> { 396 397 #region Type-level Constructors 398 #endregion 399 400 #region Instance Constructors 401 public ColumnEncryptionType() => throw new InvalidOperationException("Parameterless constructor is not allowed to be called!"); 402 ColumnEncryptionType(Tag flag) => Var = flag; 403 #endregion 404 405 #region Type-level Fields 406 public static readonly ColumnEncryptionType DETERMINISTIC = new(Tag.DETERMINISTIC); 407 public static readonly ColumnEncryptionType RANDOMIZED = new(Tag.RANDOMIZED); 408 #endregion 409 410 #region Instance Fields 411 [FieldOffset(0)] public readonly Tag Var; 412 #endregion 413 414 #region Type-level Properties 415 #endregion 416 417 #region Instance Properties 418 public readonly Var2 Variant => (Var2)Var; 419 public readonly Tag Variant0 => Var == Tag.DETERMINISTIC ? Var : throw new InvalidOperationException($"The ColumnEncryptionType variant, {Var.ToString()}, is not DETERMINISTIC!"); 420 public readonly Tag Variant1 => Var == Tag.RANDOMIZED ? Var : throw new InvalidOperationException($"The ColumnEncryptionType variant, {Var.ToString()}, is not RANDOMIZED!"); 421 #endregion 422 423 #region Type-level Functions 424 #endregion 425 426 #region Instance Functions 427 public readonly ColumnEncryptionType Clone() => this; 428 public readonly bool Equals(in ColumnEncryptionType other) => this == other; 429 public override readonly bool Equals(object? _) => false; 430 public override readonly int GetHashCode() => 0; 431 public readonly Unit Hash<THasher>(ref THasher hasher) where THasher: notnull, IHasher => hasher.WriteUnsafe(this); 432 public readonly ColumnEncryptionType Into() => this; 433 public readonly string IntoString() => ToString(); 434 readonly string IInto<string>.Into() => IntoString(); 435 public override readonly string ToString() => Var.ToString(); 436 readonly Result<ColumnEncryptionType, Bottom> ITryInto<ColumnEncryptionType, Bottom>.TryInto() => new(this); 437 readonly Result<string, Bottom> ITryInto<string, Bottom>.TryInto() => new(ToString()); 438 #endregion 439 440 #region Operators 441 public static bool operator !=(ColumnEncryptionType val0, ColumnEncryptionType val1) => !(val0 == val1); 442 public static bool operator ==(ColumnEncryptionType val0, ColumnEncryptionType val1) => val0.Var == val1.Var; 443 #endregion 444 445 #region Types 446 public enum Tag: byte { 447 DETERMINISTIC = byte.MinValue, 448 RANDOMIZED = 1, 449 } 450 #endregion 451 } 452 [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode, Pack = 8, Size = 32)] 453 public readonly struct ColumnIdentityInfo: IClone<ColumnIdentityInfo>, IHashable, IInto<ColumnIdentityInfo>, IInto<string>, IEquality<ColumnIdentityInfo> { 454 455 #region Type-level Constructors 456 #endregion 457 458 #region Instance Constructors 459 public ColumnIdentityInfo() => throw new InvalidOperationException("Parameterless constructor is not allowed to be called!"); 460 internal ColumnIdentityInfo(I128 seed, I128 increment) => (Seed, Increment) = (seed, increment); 461 #endregion 462 463 #region Type-level Fields 464 #endregion 465 466 #region Instance Fields 467 [FieldOffset(0)] public readonly I128 Seed; 468 [FieldOffset(16)] public readonly I128 Increment; 469 #endregion 470 471 #region Type-level Properties 472 #endregion 473 474 #region Instance Properties 475 #endregion 476 477 #region Type-level Functions 478 #endregion 479 480 #region Instance Functions 481 public readonly ColumnIdentityInfo Clone() => this; 482 public readonly bool Equals(in ColumnIdentityInfo other) => this == other; 483 public override readonly bool Equals(object? _) => false; 484 public override readonly int GetHashCode() => 0; 485 public readonly Unit Hash<THasher>(ref THasher hasher) where THasher: notnull, IHasher => hasher.WriteUnsafe(this); 486 public readonly ColumnIdentityInfo Into() => this; 487 public readonly string IntoString() => ToString(); 488 readonly string IInto<string>.Into() => IntoString(); 489 public override readonly string ToString() => $"Seed: {Seed.IntoString()}, Increment: {Increment.IntoString()}"; 490 readonly Result<ColumnIdentityInfo, Bottom> ITryInto<ColumnIdentityInfo, Bottom>.TryInto() => new(this); 491 readonly Result<string, Bottom> ITryInto<string, Bottom>.TryInto() => new(ToString()); 492 #endregion 493 494 #region Operators 495 public static bool operator !=(ColumnIdentityInfo val0, ColumnIdentityInfo val1) => !(val0 == val1); 496 public static bool operator ==(ColumnIdentityInfo val0, ColumnIdentityInfo val1) => val0.Seed == val1.Seed && val0.Increment == val1.Increment; 497 #endregion 498 499 #region Types 500 #endregion 501 } 502 [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode, Pack = 1, Size = 1)] 503 public readonly struct ColumnSort: ISum<ColumnSort.Tag, ColumnSort.Tag>, IClone<ColumnSort>, IHashable, IInto<ColumnSort>, IInto<string>, IEquality<ColumnSort> { 504 505 #region Type-level Constructors 506 #endregion 507 508 #region Instance Constructors 509 public ColumnSort() => throw new InvalidOperationException("Parameterless constructor is not allowed to be called!"); 510 internal ColumnSort(Tag kind) => Var = kind; 511 #endregion 512 513 #region Type-level Fields 514 public static readonly ColumnSort Ascending = new(Tag.Ascending); 515 public static readonly ColumnSort Descending = new(Tag.Descending); 516 #endregion 517 518 #region Instance Fields 519 [FieldOffset(0)] public readonly Tag Var; 520 #endregion 521 522 #region Type-level Properties 523 #endregion 524 525 #region Instance Properties 526 public readonly Var2 Variant => (Var2)Var; 527 public readonly Tag Variant0 => Var == Tag.Ascending ? Var : throw new InvalidOperationException($"The ColumnSort variant, {Var.ToString()}, is not Ascending!"); 528 public readonly Tag Variant1 => Var == Tag.Descending ? Var : throw new InvalidOperationException($"The ColumnEncryptionType variant, {Var.ToString()}, is not Descending!"); 529 #endregion 530 531 #region Type-level Functions 532 #endregion 533 534 #region Instance Functions 535 public readonly ColumnSort Clone() => this; 536 public readonly bool Equals(in ColumnSort other) => this == other; 537 public override readonly bool Equals(object? _) => false; 538 public override readonly int GetHashCode() => 0; 539 public readonly Unit Hash<THasher>(ref THasher hasher) where THasher: notnull, IHasher => hasher.WriteUnsafe(this); 540 public readonly ColumnSort Into() => this; 541 public readonly string IntoString() => ToString(); 542 readonly string IInto<string>.Into() => IntoString(); 543 public override readonly string ToString() => $"{Var.ToString()}"; 544 readonly Result<ColumnSort, Bottom> ITryInto<ColumnSort, Bottom>.TryInto() => new(this); 545 readonly Result<string, Bottom> ITryInto<string, Bottom>.TryInto() => new(ToString()); 546 #endregion 547 548 #region Operators 549 public static bool operator !=(ColumnSort val0, ColumnSort val1) => !(val0 == val1); 550 public static bool operator ==(ColumnSort val0, ColumnSort val1) => val0.Var == val1.Var; 551 #endregion 552 553 #region Types 554 public enum Tag: byte { 555 Ascending = byte.MinValue, 556 Descending = 1, 557 } 558 #endregion 559 } 560 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 0)] 561 public readonly struct Database: IClone<Database>, IEq<Database>, IHashable, IInto<Database>, IInto<string> { 562 563 #region Type-level Constructors 564 #endregion 565 566 #region Instance Constructors 567 public Database() => throw new InvalidOperationException("Parameterless constructor is not allowed to be called!"); 568 internal Database(Server server, Server.DatabaseHack database) => (Server, Name, CultureInfo, CompareOptions, IsReadOnly, _schemas, Collation, DatabaseID) = (server, database.Name, database.CultureInfo, database.CompareOptions, database.IsReadOnly, database.Schemas, database.Collation, database.DatabaseID); 569 #endregion 570 571 #region Type-level Fields 572 static readonly Fn<Maybe<Schema>> _noSchema = () => Maybe<Schema>.None(); 573 #endregion 574 575 #region Instance Fields 576 public readonly Server Server; 577 readonly HashMap<CulturedString, int, FNVHasher, RandomFNVHashBuilder> _schemas; 578 public readonly CulturedString Name; 579 public readonly CultureInfo CultureInfo; 580 public readonly string Collation; 581 public readonly CompareOptions CompareOptions; 582 public readonly bool IsReadOnly; 583 public readonly int DatabaseID; 584 #endregion 585 586 #region Type-level Properties 587 #endregion 588 589 #region Instance Properties 590 #endregion 591 592 #region Type-level Functions 593 #endregion 594 595 #region Instance Functions 596 // All properties except CultureInfo are completely safe to return a copy of since they are all immutable as is Database. 597 // Since Database is created via Server.DatabaseHack which in turn always calls CultureInfo.ReadOnly to create the CultureInfo 598 // instance, it too is safe to return a copy of. Similar logic applies to _schemas. 599 public readonly Database Clone() => this; 600 public override readonly bool Equals(object? _) => false; 601 public override readonly int GetHashCode() => 0; 602 public readonly Maybe<Schema> GetSchema(string name) { 603 604 var copy = this; 605 return _schemas.GetKeyValue(new(name, CultureInfo, CompareOptions)).MapOrElse( 606 _noSchema, 607 (val) => new(new(copy, val.Item0, val.Item1)) 608 ); 609 } 610 public readonly Unit Hash<THasher>(ref THasher hasher) where THasher: notnull, IHasher { 611 612 _ = Server.Hash(ref hasher); 613 return hasher.WriteInt(DatabaseID); 614 } 615 public readonly Database Into() => this; 616 public readonly string IntoString() => ToString(); 617 readonly string IInto<string>.Into() => IntoString(); 618 public override readonly string ToString() => $"{Server.IntoString()}.{Name.Value}"; 619 readonly Result<Database, Bottom> ITryInto<Database, Bottom>.TryInto() => new(this); 620 readonly Result<string, Bottom> ITryInto<string, Bottom>.TryInto() => new(ToString()); 621 #endregion 622 623 #region Operators 624 public static bool operator !=(Database val0, Database val1) => !(val0 == val1); 625 public static bool operator ==(Database val0, Database val1) => val0.Server == val1.Server && val0.DatabaseID == val1.DatabaseID; 626 #endregion 627 628 #region Types 629 #endregion 630 } 631 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 0)] 632 public readonly struct FOREIGN_KEY_CONSTRAINT: IClone<FOREIGN_KEY_CONSTRAINT>, IEq<FOREIGN_KEY_CONSTRAINT>, IHashable, IInto<FOREIGN_KEY_CONSTRAINT>, IInto<string>, IObject, IIndex<ushort, Prod<int, int>>, IIntoIterator<Prod<int, int>, Std.Vec.IntoIterator<Prod<int, int>>> { 633 634 #region Type-level Constructors 635 #endregion 636 637 #region Instance Constructors 638 public FOREIGN_KEY_CONSTRAINT() => throw new InvalidOperationException("Parameterless constructor is not allowed to be called!"); 639 internal FOREIGN_KEY_CONSTRAINT(Schema schema, string name, int objectID, int referencedObjectID, int referenceIndexID, int parentObjectID, ReferentialAction delete, ReferentialAction update, Vec<Prod<int, int>> parentAndReferencedColumnIDs, bool isDisabled, bool isNotTrusted) => (Schema, Name, ObjectID, ReferencedObjectID, ReferencedIndexID, ParentObjectID, DELETE, UPDATE, _parentAndReferencedColumnIDs, IsDisabled, IsNotTrusted) = (schema, name, objectID, referencedObjectID, referenceIndexID, parentObjectID, delete, update, parentAndReferencedColumnIDs, isDisabled, isNotTrusted); 640 #endregion 641 642 #region Type-level Fields 643 #endregion 644 645 #region Instance Fields 646 public readonly Schema Schema; 647 public readonly string Name; 648 public readonly int ObjectID; 649 public readonly int ReferencedObjectID; 650 public readonly int ReferencedIndexID; 651 public readonly int ParentObjectID; 652 public readonly ReferentialAction DELETE; 653 public readonly ReferentialAction UPDATE; 654 public readonly bool IsDisabled; 655 public readonly bool IsNotTrusted; 656 readonly Vec<Prod<int, int>> _parentAndReferencedColumnIDs; 657 #endregion 658 659 #region Type-level Properties 660 #endregion 661 662 #region Instance Properties 663 readonly Schema IObject.Schema => Schema; 664 readonly string IObject.Name => Name; 665 readonly int IObject.ObjectID => ObjectID; 666 public readonly ref readonly Prod<int, int> this[ushort index] => ref _parentAndReferencedColumnIDs[index]; 667 public readonly ushort ColumnCount => (ushort)_parentAndReferencedColumnIDs.Len; 668 #endregion 669 670 #region Type-level Functions 671 #endregion 672 673 #region Instance Functions 674 public readonly FOREIGN_KEY_CONSTRAINT Clone() => this; 675 public override readonly bool Equals(object? _) => false; 676 public readonly Maybe<Prod<int, int>> GetParentAndReferencedColumnID(ushort ordinalPosition) => _parentAndReferencedColumnIDs.Get(ordinalPosition); 677 public readonly Maybe<int> GetReferencedColumnID(int parentColumnID) { 678 679 for (var i = uint.MinValue; i < _parentAndReferencedColumnIDs.Len; i++) { 680 ref readonly var col = ref _parentAndReferencedColumnIDs[i]; 681 if (col.Item0 == parentColumnID) { return new(col.Item1); } 682 } 683 return Maybe<int>.None(); 684 } 685 public int GetReferencedColumnIDUnsafe(int parentColumnID) { 686 687 for (var i = uint.MinValue; i < _parentAndReferencedColumnIDs.Len; i++) { 688 ref readonly var col = ref _parentAndReferencedColumnIDs[i]; 689 if (col.Item0 == parentColumnID) { return col.Item1; } 690 } 691 throw new InvalidOperationException($"Parent column_id, {parentColumnID.ToString()}, does not exist in {ToString()}."); 692 } 693 public override readonly int GetHashCode() => 0; 694 public readonly Unit Hash<THasher>(ref THasher hasher) where THasher: notnull, IHasher { 695 696 _ = Schema.Hash(ref hasher); 697 return hasher.WriteInt(ObjectID); 698 } 699 public readonly FOREIGN_KEY_CONSTRAINT Into() => this; 700 public readonly Std.Vec.IntoIterator<Prod<int, int>> IntoIter() => _parentAndReferencedColumnIDs.IntoIter(); 701 public readonly string IntoString() => ToString(); 702 readonly string IInto<string>.Into() => IntoString(); 703 readonly void IObject.Sealed() { } 704 public override readonly string ToString() => Name; 705 readonly Result<FOREIGN_KEY_CONSTRAINT, Bottom> ITryInto<FOREIGN_KEY_CONSTRAINT, Bottom>.TryInto() => new(this); 706 readonly Result<string, Bottom> ITryInto<string, Bottom>.TryInto() => new(ToString()); 707 #endregion 708 709 #region Operators 710 public static bool operator !=(FOREIGN_KEY_CONSTRAINT val0, FOREIGN_KEY_CONSTRAINT val1) => !(val0 == val1); 711 public static bool operator ==(FOREIGN_KEY_CONSTRAINT val0, FOREIGN_KEY_CONSTRAINT val1) => val0.Schema == val1.Schema && val0.ObjectID == val1.ObjectID; 712 #endregion 713 714 #region Types 715 #endregion 716 } 717 public interface IObject { 718 719 #region Type-level Constructors 720 #endregion 721 722 #region Instance Constructors 723 #endregion 724 725 #region Type-level Fields 726 #endregion 727 728 #region Instance Fields 729 #endregion 730 731 #region Type-level Properties 732 #endregion 733 734 #region Instance Properties 735 public abstract Schema Schema { get; } 736 public abstract string Name { get; } 737 public abstract int ObjectID { get; } 738 #endregion 739 740 #region Type-level Functions 741 #endregion 742 743 #region Instance Functions 744 internal abstract void Sealed(); 745 #endregion 746 747 #region Operators 748 #endregion 749 750 #region Types 751 #endregion 752 } 753 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 0)] 754 public readonly struct Index: IClone<Index>, IEq<Index>, IHashable, IIndex<ushort, Prod<Column, Maybe<ColumnSort>>>, IInto<Index>, IInto<string>, IIntoIterator<Prod<Column, Maybe<ColumnSort>>, Std.Vec.IntoIterator<Prod<Column, Maybe<ColumnSort>>>> { 755 756 #region Type-level Constructors 757 #endregion 758 759 #region Instance Constructors 760 public Index() => throw new InvalidOperationException("Parameterless constructor is not allowed to be called!"); 761 internal Index(string name, bool isUnique, IndexType type, Vec<Prod<Column, Maybe<ColumnSort>>> columns, int indexID, bool isDisabled, string? filterDef) => (Name, IsUnique, Type, _columns, IndexID, IsDisabled, _filterDef) = (name, isUnique, type, columns, indexID, isDisabled, filterDef); 762 #endregion 763 764 #region Type-level Fields 765 #endregion 766 767 #region Instance Fields 768 internal readonly Vec<Prod<Column, Maybe<ColumnSort>>> _columns; 769 public readonly string Name; 770 public readonly bool IsUnique; 771 public readonly IndexType Type; 772 public readonly int IndexID; 773 public readonly bool IsDisabled; 774 readonly string? _filterDef; 775 #endregion 776 777 #region Type-level Properties 778 #endregion 779 780 #region Instance Properties 781 public readonly ushort ColumnCount => (ushort)_columns.Len; 782 public readonly ref readonly Prod<Column, Maybe<ColumnSort>> this[ushort index] => ref _columns[index]; 783 public readonly Maybe<string> FilterDefinition => _filterDef is null ? Maybe<string>.None() : new(_filterDef); 784 #endregion 785 786 #region Type-level Functions 787 #endregion 788 789 #region Instance Functions 790 // All fields and properties except _columns are completely safe to return a copy of since they are all immutable as is Index. 791 // Since _columns is not altered after creation and is not altered by internal code, it is also safe to make a copy of it. 792 public readonly Index Clone() => this; 793 public readonly bool ContainsComputedKey() { 794 795 for (var i = uint.MinValue; i < _columns.Len; i++) { 796 ref readonly var col = ref _columns[i]; 797 if (col.Item1.IsNone) { return false; } else if (col.Item0.ComputedInfo.IsSome) { return true; } 798 } 799 return false; 800 } 801 public readonly bool ContainsComputedOrIdentityKey() { 802 803 for (var i = uint.MinValue; i < _columns.Len; i++) { 804 ref readonly var col = ref _columns[i]; 805 if (col.Item1.IsNone) { return false; } else if (col.Item0.ComputedInfo.IsSome || col.Item0.IdentityInfo.IsSome) { return true; } 806 } 807 return false; 808 } 809 public readonly bool ContainsIdentityKey() { 810 811 for (var i = uint.MinValue; i < _columns.Len; i++) { 812 ref readonly var col = ref _columns[i]; 813 if (col.Item1.IsNone) { return false; } else if (col.Item0.IdentityInfo.IsSome) { return true; } 814 } 815 return false; 816 } 817 public override readonly bool Equals(object? _) => false; 818 public readonly Maybe<Prod<Column, Maybe<ColumnSort>>> GetColumn(ushort ordinalPosition) => _columns.Get(ordinalPosition); 819 public readonly Maybe<Prod<Column, Maybe<ColumnSort>>> GetColumn(int parentColumnID) { 820 821 for (var i = uint.MinValue; i < _columns.Len; i++) { 822 ref readonly var col = ref _columns[i]; 823 if (col.Item0.ColumnID == parentColumnID) { return new(col); } 824 } 825 return Maybe<Prod<Column, Maybe<ColumnSort>>>.None(); 826 } 827 public ref readonly Prod<Column, Maybe<ColumnSort>> GetColumnUnsafe(int parentColumnID) { 828 829 for (var i = uint.MinValue; i < _columns.Len; i++) { 830 ref readonly var col = ref _columns[i]; 831 if (col.Item0.ColumnID == parentColumnID) { return ref col; } 832 } 833 throw new InvalidOperationException($"Parent column_id, {parentColumnID.ToString()}, does not exist in {ToString()}."); 834 } 835 public readonly Maybe<Prod<Column, Maybe<ColumnSort>>> GetColumn(ReadOnlySpan<char> columnName) { 836 837 for (var i = uint.MinValue; i < _columns.Len; i++) { 838 ref readonly var col = ref _columns[i]; 839 if (MemoryExtensions.Equals(col.Item0.Name.AsSpan(), columnName, StringComparison.Ordinal)) { return new(col); } 840 } 841 return Maybe<Prod<Column, Maybe<ColumnSort>>>.None(); 842 } 843 public ref readonly Prod<Column, Maybe<ColumnSort>> GetColumnUnsafe(ReadOnlySpan<char> columnName) { 844 845 for (var i = uint.MinValue; i < _columns.Len; i++) { 846 ref readonly var col = ref _columns[i]; 847 if (MemoryExtensions.Equals(col.Item0.Name.AsSpan(), columnName, StringComparison.Ordinal)) { return ref col; } 848 } 849 throw new InvalidOperationException($"Column, {columnName.ToString()}, does not exist in {ToString()}."); 850 } 851 public override readonly int GetHashCode() => 0; 852 public readonly bool HasSameColumns(in Index other) { 853 854 if (ColumnCount == other.ColumnCount) { 855 856 for (var i = uint.MinValue; i < _columns.Len; i++) { 857 ref readonly var col1 = ref _columns[i]; 858 ref readonly var col2 = ref other._columns[i]; 859 if (!(col1.Item0 == col2.Item0 && col1.Item1.Eq(in col2.Item1))) { return false; } 860 } 861 return true; 862 } else { 863 return false; 864 } 865 } 866 public readonly bool HasSameColumnsIgnoreIDENTITYProperty(in Index other) { 867 868 if (ColumnCount == other.ColumnCount) { 869 870 for (var i = uint.MinValue; i < _columns.Len; i++) { 871 ref readonly var col1 = ref _columns[i]; 872 ref readonly var col2 = ref other._columns[i]; 873 if (!(col1.Item0.EqIgnoreIDENTITYProperty(in col2.Item0) && col1.Item1.Eq(in col2.Item1))) { return false; } 874 } 875 return true; 876 } else { 877 return false; 878 } 879 } 880 public readonly Unit Hash<THasher>(ref THasher hasher) where THasher: notnull, IHasher { 881 882 _ = ((@string)Name).Hash(ref hasher); 883 _ = hasher.WriteUnsafe(byte.MinValue); 884 _ = hasher.WriteByte(IsUnique ? byte.MaxValue : byte.MinValue); 885 _ = hasher.WriteByte(IsDisabled ? byte.MaxValue : byte.MinValue); 886 if (_filterDef is null) { 887 _ = hasher.WriteByte(byte.MinValue); 888 } else { 889 _ = hasher.WriteByte(byte.MinValue); 890 _ = hasher.WriteSliceUnsafe(_filterDef.AsSpan()); 891 } 892 _ = Type.Hash(ref hasher); 893 894 for (var i = uint.MinValue; i < _columns.Len; i++) { 895 ref readonly var col = ref _columns[i]; 896 _ = col.Item0.Hash(ref hasher); 897 _ = col.Item1.Hash(ref hasher); 898 } 899 return new Unit(); 900 } 901 public readonly Index Into() => this; 902 public readonly string IntoString() => ToString(); 903 readonly string IInto<string>.Into() => IntoString(); 904 public readonly Std.Vec.IntoIterator<Prod<Column, Maybe<ColumnSort>>> IntoIter() => _columns.IntoIter(); 905 // Only the ColumnIDs of the key columns are checked. 906 // This means elsewhere it is essential that the other components of the Indexes were already checked for equivalence. 907 // Also note that index keys are always listed first in an Index, so we can immediately skip the remaining columns 908 // once the first Maybe<Sort> is None (i.e., it is an included column and not an index key). 909 internal readonly bool IsSubset(in Index other) { 910 911 var i = uint.MinValue; 912 913 outer: while (i < _columns.Len) { 914 ref readonly var tup = ref _columns[i++]; 915 if (tup.Item1.IsNone) { return true; } 916 917 for (var j = uint.MinValue; j < other._columns.Len; j++) { 918 ref readonly var tup2 = ref other._columns[j]; 919 if (tup2.Item1.IsNone) { return false; } else if (tup.Item0.ColumnID == tup2.Item0.ColumnID) { goto outer; } 920 } 921 return false; 922 } 923 return true; 924 } 925 public override readonly string ToString() => Name; 926 readonly Result<Index, Bottom> ITryInto<Index, Bottom>.TryInto() => new(this); 927 readonly Result<string, Bottom> ITryInto<string, Bottom>.TryInto() => new(ToString()); 928 #endregion 929 930 #region Operators 931 public static bool operator !=(Index val0, Index val1) => !(val0 == val1); 932 public static bool operator ==(Index val0, Index val1) => string.Equals(val0.Name, val1.Name, StringComparison.Ordinal) && val0.Type == val1.Type && val0.IsUnique == val1.IsUnique && val0.HasSameColumns(in val1) && val0.IsDisabled == val1.IsDisabled && string.Equals(val0._filterDef, val1._filterDef, StringComparison.Ordinal); 933 #endregion 934 935 #region Types 936 #endregion 937 } 938 [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode, Pack = 1, Size = 1)] 939 public readonly struct IndexType: ISum<IndexType.Tag, IndexType.Tag, IndexType.Tag, IndexType.Tag, IndexType.Tag, IndexType.Tag, IndexType.Tag>, IClone<IndexType>, IHashable, IInto<IndexType>, IInto<string>, IEquality<IndexType> { 940 941 #region Type-level Constructors 942 #endregion 943 944 #region Instance Constructors 945 public IndexType() => throw new InvalidOperationException("Parameterless constructor is not allowed to be called!"); 946 IndexType(Tag flag) => Var = flag; 947 #endregion 948 949 #region Type-level Fields 950 public static readonly IndexType Clustered = new(Tag.Clustered); 951 public static readonly IndexType Nonclustered = new(Tag.Nonclustered); 952 public static readonly IndexType XML = new(Tag.XML); 953 public static readonly IndexType Spatial = new(Tag.Spatial); 954 public static readonly IndexType ClusteredColumnstore = new(Tag.ClusteredColumnstore); 955 public static readonly IndexType NonclusteredColumnstore = new(Tag.NonclusteredColumnstore); 956 public static readonly IndexType NonclusteredHash = new(Tag.NonclusteredHash); 957 #endregion 958 959 #region Instance Fields 960 [FieldOffset(0)] public readonly Tag Var; 961 #endregion 962 963 #region Type-level Properties 964 #endregion 965 966 #region Instance Properties 967 public readonly Var7 Variant => (Var7)Var; 968 public readonly Tag Variant0 => Var == Tag.Clustered ? Var : throw new InvalidOperationException($"The IndexType variant, {Var.ToString()}, is not Clustered!"); 969 public readonly Tag Variant1 => Var == Tag.Nonclustered ? Var : throw new InvalidOperationException($"The IndexType variant, {Var.ToString()}, is not Nonclustered!"); 970 public readonly Tag Variant2 => Var == Tag.XML ? Var : throw new InvalidOperationException($"The IndexType variant, {Var.ToString()}, is not XML!"); 971 public readonly Tag Variant3 => Var == Tag.Spatial ? Var : throw new InvalidOperationException($"The IndexType variant, {Var.ToString()}, is not Spatial!"); 972 public readonly Tag Variant4 => Var == Tag.ClusteredColumnstore ? Var : throw new InvalidOperationException($"The IndexType variant, {Var.ToString()}, is not ClusteredColumnstore!"); 973 public readonly Tag Variant5 => Var == Tag.NonclusteredColumnstore ? Var : throw new InvalidOperationException($"The IndexType variant, {Var.ToString()}, is not NonclusteredColumnstore!"); 974 public readonly Tag Variant6 => Var == Tag.NonclusteredHash ? Var : throw new InvalidOperationException($"The IndexType variant, {Var.ToString()}, is not NonclusteredHash!"); 975 #endregion 976 977 #region Type-level Functions 978 #endregion 979 980 #region Instance Functions 981 public readonly IndexType Clone() => this; 982 public readonly bool Equals(in IndexType other) => this == other; 983 public override readonly bool Equals(object? _) => false; 984 public override readonly int GetHashCode() => 0; 985 public readonly Unit Hash<THasher>(ref THasher hasher) where THasher: notnull, IHasher => hasher.WriteUnsafe(this); 986 public readonly IndexType Into() => this; 987 public readonly string IntoString() => ToString(); 988 readonly string IInto<string>.Into() => IntoString(); 989 public override readonly string ToString() => Var.ToString(); 990 readonly Result<IndexType, Bottom> ITryInto<IndexType, Bottom>.TryInto() => new(this); 991 readonly Result<string, Bottom> ITryInto<string, Bottom>.TryInto() => new(ToString()); 992 #endregion 993 994 #region Operators 995 public static bool operator !=(IndexType val0, IndexType val1) => !(val0 == val1); 996 public static bool operator ==(IndexType val0, IndexType val1) => val0.Var == val1.Var; 997 #endregion 998 999 #region Types 1000 public enum Tag: byte { 1001 Clustered = byte.MinValue, 1002 Nonclustered = 1, 1003 XML = 2, 1004 Spatial = 3, 1005 ClusteredColumnstore = 4, 1006 NonclusteredColumnstore = 5, 1007 NonclusteredHash = 6, 1008 } 1009 #endregion 1010 } 1011 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 0)] 1012 public readonly struct PRIMARY_KEY_CONSTRAINT: IClone<PRIMARY_KEY_CONSTRAINT>, IEq<PRIMARY_KEY_CONSTRAINT>, IHashable, IInto<PRIMARY_KEY_CONSTRAINT>, IInto<string>, IObject { 1013 1014 #region Type-level Constructors 1015 #endregion 1016 1017 #region Instance Constructors 1018 public PRIMARY_KEY_CONSTRAINT() => throw new InvalidOperationException("Parameterless constructor is not allowed to be called!"); 1019 internal PRIMARY_KEY_CONSTRAINT(Schema schema, string name, int objectID, int parentObjectID, int indexID) => (Schema, Name, ObjectID, ParentObjectID, UniqueIndexID) = (schema, name, objectID, parentObjectID, indexID); 1020 #endregion 1021 1022 #region Type-level Fields 1023 #endregion 1024 1025 #region Instance Fields 1026 public readonly Schema Schema; 1027 public readonly string Name; 1028 public readonly int ObjectID; 1029 public readonly int ParentObjectID; 1030 public readonly int UniqueIndexID; 1031 #endregion 1032 1033 #region Type-level Properties 1034 #endregion 1035 1036 #region Instance Properties 1037 readonly Schema IObject.Schema => Schema; 1038 readonly string IObject.Name => Name; 1039 readonly int IObject.ObjectID => ObjectID; 1040 #endregion 1041 1042 #region Type-level Functions 1043 #endregion 1044 1045 #region Instance Functions 1046 public readonly PRIMARY_KEY_CONSTRAINT Clone() => this; 1047 public override readonly bool Equals(object? _) => false; 1048 public override readonly int GetHashCode() => 0; 1049 public readonly Unit Hash<THasher>(ref THasher hasher) where THasher: notnull, IHasher { 1050 1051 _ = Schema.Hash(ref hasher); 1052 return hasher.WriteInt(ObjectID); 1053 } 1054 public readonly PRIMARY_KEY_CONSTRAINT Into() => this; 1055 public readonly string IntoString() => ToString(); 1056 readonly string IInto<string>.Into() => IntoString(); 1057 readonly void IObject.Sealed() { } 1058 public override readonly string ToString() => Name; 1059 readonly Result<PRIMARY_KEY_CONSTRAINT, Bottom> ITryInto<PRIMARY_KEY_CONSTRAINT, Bottom>.TryInto() => new(this); 1060 readonly Result<string, Bottom> ITryInto<string, Bottom>.TryInto() => new(ToString()); 1061 #endregion 1062 1063 #region Operators 1064 public static bool operator !=(PRIMARY_KEY_CONSTRAINT val0, PRIMARY_KEY_CONSTRAINT val1) => !(val0 == val1); 1065 public static bool operator ==(PRIMARY_KEY_CONSTRAINT val0, PRIMARY_KEY_CONSTRAINT val1) => val0.Schema == val1.Schema && val0.ObjectID == val1.ObjectID; 1066 #endregion 1067 1068 #region Types 1069 #endregion 1070 } 1071 [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode, Pack = 1, Size = 1)] 1072 public readonly struct ReferentialAction: ISum<ReferentialAction.Tag, ReferentialAction.Tag, ReferentialAction.Tag, ReferentialAction.Tag>, IClone<ReferentialAction>, IHashable, IInto<ReferentialAction>, IInto<string>, IEquality<ReferentialAction> { 1073 1074 #region Type-level Constructors 1075 #endregion 1076 1077 #region Instance Constructors 1078 public ReferentialAction() => throw new InvalidOperationException("Parameterless constructor is not allowed to be called!"); 1079 ReferentialAction(Tag flag) => Var = flag; 1080 #endregion 1081 1082 #region Type-level Fields 1083 public static readonly ReferentialAction NO_ACTION = new(Tag.NO_ACTION); 1084 public static readonly ReferentialAction CASCADE = new(Tag.CASCADE); 1085 public static readonly ReferentialAction SET_NULL = new(Tag.SET_NULL); 1086 public static readonly ReferentialAction SET_DEFAULT = new(Tag.SET_DEFAULT); 1087 #endregion 1088 1089 #region Instance Fields 1090 [FieldOffset(0)] public readonly Tag Var; 1091 #endregion 1092 1093 #region Type-level Properties 1094 #endregion 1095 1096 #region Instance Properties 1097 public readonly Var4 Variant => (Var4)Var; 1098 public readonly Tag Variant0 => Var == Tag.NO_ACTION ? Var : throw new InvalidOperationException($"The ReferentialAction variant, {Var.ToString()}, is not NO_ACTION!"); 1099 public readonly Tag Variant1 => Var == Tag.CASCADE ? Var : throw new InvalidOperationException($"The ReferentialAction variant, {Var.ToString()}, is not CASCADE!"); 1100 public readonly Tag Variant2 => Var == Tag.SET_NULL ? Var : throw new InvalidOperationException($"The ReferentialAction variant, {Var.ToString()}, is not SET_NULL!"); 1101 public readonly Tag Variant3 => Var == Tag.SET_DEFAULT ? Var : throw new InvalidOperationException($"The ReferentialAction variant, {Var.ToString()}, is not SET_DEFAULT!"); 1102 #endregion 1103 1104 #region Type-level Functions 1105 #endregion 1106 1107 #region Instance Functions 1108 public readonly ReferentialAction Clone() => this; 1109 public readonly bool Equals(in ReferentialAction other) => this == other; 1110 public override readonly bool Equals(object? _) => false; 1111 public override readonly int GetHashCode() => 0; 1112 public readonly Unit Hash<THasher>(ref THasher hasher) where THasher: notnull, IHasher => hasher.WriteUnsafe(this); 1113 public readonly ReferentialAction Into() => this; 1114 public readonly string IntoString() => ToString(); 1115 readonly string IInto<string>.Into() => IntoString(); 1116 public override readonly string ToString() => Var.ToString(); 1117 readonly Result<ReferentialAction, Bottom> ITryInto<ReferentialAction, Bottom>.TryInto() => new(this); 1118 readonly Result<string, Bottom> ITryInto<string, Bottom>.TryInto() => new(ToString()); 1119 #endregion 1120 1121 #region Operators 1122 public static bool operator !=(ReferentialAction val0, ReferentialAction val1) => !(val0 == val1); 1123 public static bool operator ==(ReferentialAction val0, ReferentialAction val1) => val0.Var == val1.Var; 1124 #endregion 1125 1126 #region Types 1127 public enum Tag: byte { 1128 NO_ACTION = byte.MinValue, 1129 CASCADE = 1, 1130 SET_NULL = 2, 1131 SET_DEFAULT = 3, 1132 } 1133 #endregion 1134 } 1135 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 0)] 1136 public readonly struct Schema: IClone<Schema>, IEq<Schema>, IHashable, IInto<Schema>, IInto<string> { 1137 1138 #region Type-level Constructors 1139 #endregion 1140 1141 #region Instance Constructors 1142 public Schema() => throw new InvalidOperationException("Parameterless constructor is not allowed to be called!"); 1143 internal Schema(Database database, CulturedString name, int schemaID) => (Database, Name, SchemaID) = (database, name, schemaID); 1144 #endregion 1145 1146 #region Type-level Fields 1147 static readonly I128 _1 = new(1L, 0L); 1148 static readonly I128 _2To32 = new(0x0100000000L, 0L); 1149 static readonly I128 _2To64 = new(0L, 1L); 1150 static readonly I128 _2To96 = new(0L, 0x0100000000L); 1151 static readonly I128 _Negative_1 = new(-1L); 1152 static readonly Fn<NonZeroUshort, int> _nzUshortToInt = (x) => x.IntoUshort(); 1153 #endregion 1154 1155 #region Instance Fields 1156 public readonly Database Database; 1157 public readonly CulturedString Name; 1158 public readonly int SchemaID; 1159 #endregion 1160 1161 #region Type-level Properties 1162 #endregion 1163 1164 #region Instance Properties 1165 #endregion 1166 1167 #region Type-level Functions 1168 #endregion 1169 1170 #region Instance Functions 1171 public readonly Schema Clone() => this; 1172 public override readonly bool Equals(object? _) => false; 1173 public override readonly int GetHashCode() => 0; 1174 public readonly Maybe<int> GetObjectID(string name, Maybe<NonZeroUshort> timeout, SessionOptions options) { 1175 1176 using var con = Functions.CreateOpenedConnection(in Database, options, false, Maybe<Uri>.None()); 1177 using var txn = con.BeginTransaction(IsolationLevel.Serializable); 1178 return GetObjectID(name, timeout, txn).MapOrElse((err) => { txn.Rollback(); throw new InvalidOperationException(err.IntoString()); }, (obj) => { txn.Commit(); return obj; }); 1179 } 1180 public readonly Result<Maybe<int>, TransactionError> GetObjectID(string name, Maybe<NonZeroUshort> timeout, SqlTransaction txn) { 1181 1182 if (txn.Connection.State != ConnectionState.Open) { 1183 return new(new TransactionError(TransactionError.Tag.SqlConnectionIsNotOpen, new StackTrace(1, true))); 1184 } else if (!string.Equals(txn.Connection.DataSource, $"tcp:{Database.Server.IntoString()}", StringComparison.Ordinal)) { 1185 return new(new TransactionError(TransactionError.Tag.SqlConnectionServerMismatch, new StackTrace(1, true))); 1186 } else if (name.Length > 128) { 1187 return new(Maybe<int>.None()); 1188 } else { 1189 using SqlCommand qry = new("SELECT OBJECT_ID(@dtbName + N'.' + @schemaName + N'.' + @object);", txn.Connection, txn, SqlCommandColumnEncryptionSetting.Disabled) { CommandTimeout = timeout.MapOr(0, _nzUshortToInt), CommandType = CommandType.Text, EnableOptimizedParameterBinding = true }; 1190 _ = qry.Parameters.Add(new SqlParameter("@dtbName", SqlDbType.NVarChar) { SqlValue = Database.Name.Value }); 1191 _ = qry.Parameters.Add(new SqlParameter("@schemaName", SqlDbType.NVarChar) { SqlValue = Name.Value }); 1192 _ = qry.Parameters.Add(new SqlParameter("@object", SqlDbType.NVarChar) { SqlValue = name }); 1193 using var rdr = qry.ExecuteReader(CommandBehavior.SingleResult | CommandBehavior.SingleRow | CommandBehavior.SequentialAccess); 1194 if (rdr.Read()) { 1195 var val = rdr.GetSqlInt32(0); 1196 return new(val.IsNull ? Maybe<int>.None() : new(val.Value)); 1197 } else { 1198 throw new InvalidOperationException($"The query in Schema.GetObjectID returned an empty result set for the object, {name}, which should not happen."); 1199 } 1200 } 1201 } 1202 public readonly Maybe<UserTable> GetUserTable(int objectID, Maybe<NonZeroUshort> timeout, SessionOptions options) { 1203 1204 using var con = Functions.CreateOpenedConnection(in Database, options, false, Maybe<Uri>.None()); 1205 using var txn = con.BeginTransaction(IsolationLevel.Serializable); 1206 return GetUserTable(objectID, timeout, txn).MapOrElse((err) => { txn.Rollback(); throw new InvalidOperationException(err.IntoString()); }, (obj) => { txn.Commit(); return obj; }); 1207 } 1208 [System.Diagnostics.CodeAnalysis.SuppressMessage("Security", "CA2100:Review SQL queries for security vulnerabilities", Justification = "The text is generated internally.")] 1209 public readonly Result<Maybe<UserTable>, TransactionError> GetUserTable(int objectID, Maybe<NonZeroUshort> timeout, SqlTransaction txn) { 1210 1211 if (txn.Connection.State != ConnectionState.Open) { 1212 return new(new TransactionError(TransactionError.Tag.SqlConnectionIsNotOpen, new StackTrace(1, true))); 1213 } else if (!string.Equals(txn.Connection.DataSource, $"tcp:{Database.Server.IntoString()}", StringComparison.Ordinal)) { 1214 return new(new TransactionError(TransactionError.Tag.SqlConnectionServerMismatch, new StackTrace(1, true))); 1215 } else { 1216 using SqlCommand qry = new($@"IF EXISTS ( 1217 SELECT * 1218 FROM [{Database.Name.Value}].sys.tables 1219 WHERE schema_id = @schemaID AND object_id = @objID AND type = 'U ' 1220 ) 1221 BEGIN 1222 SELECT CONVERT(nvarchar(128), name) fstrName --0 nvarchar 1223 FROM [{Database.Name.Value}].sys.tables 1224 WHERE schema_id = @schemaID AND object_id = @objID AND type = 'U '; 1225 1226 DECLARE @tbl AS table (flngID int NOT NULL, flngOrdinal int NOT NULL, flngLCID int NULL, flngComp int NULL); 1227 INSERT INTO @tbl 1228 SELECT column_id, 1229 ROW_NUMBER() OVER(ORDER BY column_id ASC) - 1, 1230 CONVERT(int, COLLATIONPROPERTY(collation_name, 'LCID')), 1231 CONVERT(int, COLLATIONPROPERTY(collation_name, 'ComparisonStyle')) 1232 FROM [{Database.Name.Value}].sys.columns 1233 WHERE object_id = @objID; 1234 1235 SELECT c.max_length, --0 smallint 1236 CONVERT(nvarchar(128), t.name) AS fstrTypeName, --1 nvarchar 1237 CONVERT(nvarchar(128), c.collation_name) AS fstrCollation, --2 nvarchar nullable 1238 CONVERT(decimal(38, 0), id.seed_value) AS seed, --3 decimal nullable 1239 CONVERT(decimal(38, 0), id.increment_value) AS increment, --4 decimal nullable 1240 com.is_persisted, --5 bit nullable 1241 enc.fstrKeyName, --6 nvarchar nullable 1242 CONVERT(nvarchar(128), c.name) AS fstrName, --7 nvarchar 1243 c.is_nullable, --8 bit 1244 c.precision, --9 tinyint 1245 c.scale, --10 tinyint 1246 c.is_filestream, --11 bit 1247 tbl.flngLCID, --12 int nullable 1248 CASE tbl.flngComp 1249 WHEN 0 THEN 1073741824 1250 ELSE (tbl.flngComp & 1) | CASE tbl.flngComp & 65536 1251 WHEN 65536 THEN 8 1252 ELSE 0 1253 END | CASE tbl.flngComp & 131072 1254 WHEN 131072 THEN 16 1255 ELSE 0 1256 END 1257 END AS flngComp, --13 int nullable 1258 com.uses_database_collation, --14 bit nullable 1259 com.definition, --15 nvarchar nullable 1260 c.encryption_type_desc, --16 nvarchar nullable 1261 CONVERT(nvarchar(128), c.encryption_algorithm_name) AS fstrEncryptionAlgoName, --17 nvarchar nullable 1262 CONVERT(bit, enc.allow_enclave_computations) AS fblnEnclave, --18 bit nullable 1263 tbl.flngID --19 int 1264 FROM [{Database.Name.Value}].sys.columns AS c 1265 INNER JOIN @tbl AS tbl 1266 ON c.column_id = tbl.flngID 1267 INNER JOIN [{Database.Name.Value}].sys.types AS t 1268 ON c.user_type_id = t.user_type_id 1269 LEFT JOIN ( 1270 SELECT DISTINCT CONVERT(nvarchar(128), ek.name) AS fstrKeyName, ek.column_encryption_key_id, cm.allow_enclave_computations 1271 FROM [{Database.Name.Value}].sys.column_encryption_keys AS ek 1272 INNER JOIN [{Database.Name.Value}].sys.column_encryption_key_values AS ekv 1273 ON ek.column_encryption_key_id = ekv.column_encryption_key_id 1274 INNER JOIN [{Database.Name.Value}].sys.column_master_keys AS cm 1275 ON ekv.column_master_key_id = cm.column_master_key_id 1276 ) AS enc 1277 ON c.column_encryption_key_id = enc.column_encryption_key_id 1278 LEFT JOIN [{Database.Name.Value}].sys.identity_columns AS id 1279 ON c.object_id = id.object_id AND c.column_id = id.column_id 1280 LEFT JOIN [{Database.Name.Value}].sys.computed_columns AS com 1281 ON c.object_id = com.object_id AND c.column_id = com.column_id 1282 WHERE c.object_id = @objID 1283 ORDER BY c.column_id ASC; 1284 1285 SELECT ix.index_id, --0 int 1286 CONVERT(nvarchar(128), ix.name) AS fstrName, --1 nvarchar 1287 ix.is_unique, --2 bit 1288 ix.is_disabled, --3 bit 1289 ix.type_desc, --4 nvarchar 1290 ix.filter_definition, --5 nvarchar nullable 1291 CASE ic.is_included_column 1292 WHEN 0 THEN ic.is_descending_key 1293 ELSE NULL 1294 END AS fblnIsDescending, --6 bit nullable 1295 tbl.flngOrdinal --7 int 1296 FROM [{Database.Name.Value}].sys.indexes AS ix 1297 INNER JOIN [{Database.Name.Value}].sys.index_columns AS ic 1298 ON ix.object_id = ic.object_id AND ix.index_id = ic.index_id 1299 INNER JOIN @tbl AS tbl 1300 ON ic.column_id = tbl.flngID 1301 WHERE ix.object_id = @objID AND ix.type <> 0 1302 ORDER BY ix.index_id ASC, CASE ic.key_ordinal 1303 WHEN 0 THEN CONVERT(smallint, tbl.flngOrdinal + 256) 1304 ELSE CONVERT(smallint, ic.key_ordinal) 1305 END ASC; 1306 1307 SELECT CONVERT(nvarchar(128), name) AS fstrName, --0 nvarchar 1308 object_id, --1 int 1309 unique_index_id, -- 2 int 1310 type -- 3 char 1311 FROM [{Database.Name.Value}].sys.key_constraints 1312 WHERE schema_id = @schemaID AND parent_object_id = @objID 1313 ORDER BY type ASC, object_id ASC; 1314 1315 SELECT f.object_id, --0 int 1316 CONVERT(nvarchar(128), f.name) AS fstrName, --1 nvarchar 1317 f.referenced_object_id, --2 int 1318 f.key_index_id, --3 int 1319 f.delete_referential_action, --4 tinyint 1320 f.update_referential_action, --5 tinyint 1321 f.is_disabled, -- 6 bit 1322 f.is_not_trusted, --7 bit 1323 f2.parent_column_id, --8 int 1324 f2.referenced_column_id --9 int 1325 FROM [{Database.Name.Value}].sys.foreign_keys AS f 1326 INNER JOIN [{Database.Name.Value}].sys.foreign_key_columns AS f2 1327 ON f.object_id = f2.constraint_object_id 1328 WHERE f.schema_id = @schemaID AND f.parent_object_id = @objID 1329 ORDER BY f.object_id ASC, f2.constraint_column_id ASC; 1330 1331 SELECT CONVERT(nvarchar(128), name) AS fstrName, --0 nvarchar 1332 object_id, --1 int 1333 is_disabled, -- 2 bit 1334 is_not_trusted, --3 bit 1335 parent_column_id, --4 int 1336 definition, --5 nvarchar nullable 1337 uses_database_collation --6 bit 1338 FROM [{Database.Name.Value}].sys.check_constraints 1339 WHERE schema_id = @schemaID AND parent_object_id = @objID 1340 ORDER BY fstrName ASC; 1341 1342 SELECT CONVERT(smallint, COUNT_BIG(*)) AS fintChildObjectCount --0 smallint 1343 FROM [{Database.Name.Value}].sys.objects 1344 WHERE parent_object_id = @objID; 1345 END;", txn.Connection, txn, SqlCommandColumnEncryptionSetting.Disabled) { CommandTimeout = timeout.MapOr(0, _nzUshortToInt), CommandType = CommandType.Text, EnableOptimizedParameterBinding = true }; 1346 _ = qry.Parameters.Add(new SqlParameter("@schemaID", SqlDbType.Int) { SqlValue = SchemaID }); 1347 _ = qry.Parameters.Add(new SqlParameter("@objID", SqlDbType.Int) { SqlValue = objectID }); 1348 using var rdr = qry.ExecuteReader(CommandBehavior.SequentialAccess); 1349 if (!rdr.Read()) { return new(Maybe<UserTable>.None()); } 1350 var name = rdr.GetString(0); 1351 if (!rdr.NextResult()) { throw new InvalidOperationException($"There is a bug in SQLServer.Schema.GetUserTable! The query failed to grab the result set associated with the columns for the object_id {objectID.ToString()} under {IntoString()} despite already verifying that user table exists."); } 1352 var columns = Vec<Column>.WithCapacity(64u); 1353 short maxLen; 1354 string typeName; 1355 SqlString collation; 1356 SqlDecimal seed; 1357 int[]? seedInts; 1358 SqlDecimal inc; 1359 int[]? incInts; 1360 SqlBoolean persisted; 1361 SqlString encKey; 1362 var pos = ushort.MinValue; 1363 SqlString compDef; 1364 1365 while (rdr.Read()) { 1366 maxLen = rdr.GetInt16(0); 1367 typeName = rdr.GetString(1); 1368 typeName = string.Equals("numeric", typeName, StringComparison.Ordinal) ? "decimal" : typeName; 1369 collation = rdr.GetSqlString(2); 1370 seed = rdr.GetSqlDecimal(3); 1371 inc = rdr.GetSqlDecimal(4); 1372 (seedInts, incInts) = seed.IsNull ? (null, null) : (seed.Data, inc.Data); 1373 persisted = rdr.GetSqlBoolean(5); 1374 encKey = rdr.GetSqlString(6); 1375 _ = columns.Push(new(rdr.GetString(7), 1376 rdr.GetBoolean(8), 1377 typeName switch { 1378 "binary" or "char" or "timestamp" => new((ushort)maxLen), 1379 "nchar" => new((ushort)(maxLen / 2)), 1380 "varchar" => maxLen == -1 ? Maybe<ushort>.None() : new((ushort)maxLen), 1381 "nvarchar" => maxLen == -1 ? Maybe<ushort>.None() : new((ushort)(maxLen / 2)), 1382 _ => Maybe<ushort>.None(), 1383 }, 1384 typeName switch { 1385 "decimal" => new(rdr.GetByte(9)), 1386 _ => Maybe<byte>.None(), 1387 }, 1388 typeName switch { 1389 "datetime2" or "datetimeoffset" or "decimal" or "time" => new(rdr.GetByte(10)), 1390 _ => Maybe<byte>.None(), 1391 }, 1392 rdr.GetBoolean(11), 1393 Functions.GetTypeName(typeName), 1394 collation.IsNull ? Maybe<ColumnCollationInfo>.None() : new(new(collation.Value, CultureInfo.ReadOnly(new(rdr.GetInt32(12), false)), (CompareOptions)rdr.GetInt32(13))), 1395 seedInts is null ? Maybe<ColumnIdentityInfo>.None() : new(new((seed.IsPositive ? _1 : _Negative_1) * ((_2To96 * (I128)(uint)seedInts[3]) + (_2To64 * (I128)(uint)seedInts[2]) + (_2To32 * (I128)(uint)seedInts[1]) + (I128)(uint)seedInts[0]), (inc.IsPositive ? _1 : _Negative_1) * ((_2To96 * (I128)(uint)incInts![3]) + (_2To64 * (I128)(uint)incInts[2]) + (_2To32 * (I128)(uint)incInts[1]) + (I128)(uint)incInts[0]))), 1396 persisted.IsNull ? Maybe<ColumnComputedInfo>.None() : new(new(persisted.Value, rdr.GetBoolean(14), (compDef = rdr.GetSqlString(15)).IsNull ? null : compDef.Value)), 1397 encKey.IsNull ? Maybe<ColumnEncryptionInfo>.None() : new(new(encKey.Value, rdr.GetString(16) switch { 1398 "DETERMINISTIC" => ColumnEncryptionType.DETERMINISTIC, 1399 "RANDOMIZED" => ColumnEncryptionType.RANDOMIZED, 1400 _ => throw new InvalidOperationException("Invalid encryption type!") 1401 }, rdr.GetString(17) switch { 1402 "AEAD_AES_256_CBC_HMAC_SHA_256" => ColumnEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_256, 1403 _ => throw new InvalidOperationException("Invalid encryption algorithm!") 1404 }, rdr.GetBoolean(18))), 1405 pos++, 1406 rdr.GetInt32(19) 1407 ) 1408 ); 1409 } 1410 if (columns.Len == uint.MinValue) { throw new InvalidOperationException($"There is a bug in SQLServer.Schema.GetUserTable! The query failed to grab any columns for the object_id {objectID.ToString()} under {IntoString()} despite already verifying that user table exists."); } 1411 if (!rdr.NextResult()) { throw new InvalidOperationException($"There is a bug in SQLServer.Schema.GetUserTable! The query failed to grab the result set associated with any indexes for the object_id {objectID.ToString()} under {IntoString()} despite already verifying that user table exists."); } 1412 Vec<Index> indexes; 1413 if (!rdr.Read()) { 1414 indexes = new Vec<Index>(); 1415 } else { 1416 var indexID = rdr.GetInt32(0); 1417 var indexName = rdr.GetString(1); 1418 var unique = rdr.GetBoolean(2); 1419 var disabled = rdr.GetBoolean(3); 1420 var type = rdr.GetString(4) switch { 1421 "CLUSTERED" => IndexType.Clustered, 1422 "NONCLUSTERED" => IndexType.Nonclustered, 1423 "XML" => IndexType.XML, 1424 "SPATIAL" => IndexType.Spatial, 1425 "CLUSTERED COLUMNSTORE" => IndexType.ClusteredColumnstore, 1426 "NONCLUSTERED COLUMNSTORE" => IndexType.NonclusteredColumnstore, 1427 "NONCLUSTERED HASH" => IndexType.NonclusteredHash, 1428 _ => throw new InvalidOperationException("Invalid index type."), 1429 }; 1430 var filter = rdr.GetSqlString(5); 1431 var ixCols = Vec<Prod<Column, Maybe<ColumnSort>>>.WithCapacity(8u); 1432 var descending = rdr.GetSqlBoolean(6); 1433 _ = ixCols.Push(new(columns[(uint)rdr.GetInt32(7)], descending.IsNull ? Maybe<ColumnSort>.None() : new(descending.Value ? ColumnSort.Descending : ColumnSort.Ascending))); 1434 indexes = Vec<Index>.WithCapacity(8u); 1435 int nextID; 1436 1437 while (rdr.Read()) { 1438 nextID = rdr.GetInt32(0); 1439 1440 if (indexID != nextID) { 1441 _ = indexes.Push(new(indexName, unique, type, ixCols, indexID, disabled, filter.IsNull ? null : filter.Value)); 1442 indexID = nextID; 1443 indexName = rdr.GetString(1); 1444 unique = rdr.GetBoolean(2); 1445 disabled = rdr.GetBoolean(3); 1446 type = rdr.GetString(4) switch { 1447 "CLUSTERED" => IndexType.Clustered, 1448 "NONCLUSTERED" => IndexType.Nonclustered, 1449 "XML" => IndexType.XML, 1450 "SPATIAL" => IndexType.Spatial, 1451 "CLUSTERED COLUMNSTORE" => IndexType.ClusteredColumnstore, 1452 "NONCLUSTERED COLUMNSTORE" => IndexType.NonclusteredColumnstore, 1453 "NONCLUSTERED HASH" => IndexType.NonclusteredHash, 1454 _ => throw new InvalidOperationException("Invalid index type."), 1455 }; 1456 filter = rdr.GetSqlString(5); 1457 ixCols = Vec<Prod<Column, Maybe<ColumnSort>>>.WithCapacity(8u); 1458 } 1459 descending = rdr.GetSqlBoolean(6); 1460 _ = ixCols.Push(new(columns[(uint)rdr.GetInt32(7)], descending.IsNull ? Maybe<ColumnSort>.None() : new(descending.Value ? ColumnSort.Descending : ColumnSort.Ascending))); 1461 } 1462 _ = indexes.Push(new(indexName, unique, type, ixCols, indexID, disabled, filter.IsNull ? null : filter.Value)); 1463 } 1464 if (!rdr.NextResult()) { throw new InvalidOperationException($"There is a bug in SQLServer.Schema.GetUserTable! The query failed to grab the result set associated with any PRIMARY KEY or UNIQUE CONSTRAINTs for the object_id {objectID.ToString()} under {IntoString()} despite already verifying that user table exists."); } 1465 var pk = Maybe<PRIMARY_KEY_CONSTRAINT>.None(); 1466 var uqs = Vec<UNIQUE_CONSTRAINT>.WithCapacity(4u); 1467 1468 if (rdr.Read()) { 1469 var constraintName = rdr.GetString(0); 1470 var objID = rdr.GetInt32(1); 1471 var ixID = rdr.GetInt32(2); 1472 var type = rdr.GetString(3); 1473 if (string.Equals(type, "PK", StringComparison.Ordinal)) { 1474 pk = new(new(this, constraintName, objID, objectID, ixID)); 1475 } else { 1476 _ = uqs.Push(new(this, constraintName, objID, objectID, ixID)); 1477 } 1478 while (rdr.Read()) { 1479 constraintName = rdr.GetString(0); 1480 objID = rdr.GetInt32(1); 1481 ixID = rdr.GetInt32(2); 1482 _ = uqs.Push(new(this, constraintName, objID, objectID, ixID)); 1483 } 1484 } 1485 if (!rdr.NextResult()) { throw new InvalidOperationException($"There is a bug in SQLServer.Schema.GetUserTable! The query failed to grab the result set associated with any FOREIGN KEY CONSTRAINTs for the object_id {objectID.ToString()} under {IntoString()} despite already verifying that user table exists."); } 1486 Vec<FOREIGN_KEY_CONSTRAINT> fks; 1487 if (!rdr.Read()) { 1488 fks = new Vec<FOREIGN_KEY_CONSTRAINT>(); 1489 } else { 1490 var objID = rdr.GetInt32(0); 1491 var constraintName = rdr.GetString(1); 1492 var refObjID = rdr.GetInt32(2); 1493 var indexID = rdr.GetInt32(3); 1494 var action = rdr.GetByte(4); 1495 var del_action = action switch { 1496 byte.MinValue => ReferentialAction.NO_ACTION, 1497 1 => ReferentialAction.CASCADE, 1498 2 => ReferentialAction.SET_NULL, 1499 3 => ReferentialAction.SET_DEFAULT, 1500 _ => throw new InvalidOperationException($"The delete_referential_action, {action.ToString()}, is invalid!"), 1501 }; 1502 action = rdr.GetByte(5); 1503 var up_action = action switch { 1504 byte.MinValue => ReferentialAction.NO_ACTION, 1505 1 => ReferentialAction.CASCADE, 1506 2 => ReferentialAction.SET_NULL, 1507 3 => ReferentialAction.SET_DEFAULT, 1508 _ => throw new InvalidOperationException($"The update_referential_action, {action.ToString()}, is invalid!"), 1509 }; 1510 var disabled = rdr.GetBoolean(6); 1511 var notTrusted = rdr.GetBoolean(7); 1512 var parentAndRefColIDs = Vec<Prod<int, int>>.WithCapacity(2u); 1513 _ = parentAndRefColIDs.Push(new(rdr.GetInt32(8), rdr.GetInt32(9))); 1514 fks = Vec<FOREIGN_KEY_CONSTRAINT>.WithCapacity(2u); 1515 int nextID; 1516 1517 while (rdr.Read()) { 1518 nextID = rdr.GetInt32(0); 1519 1520 if (objID != nextID) { 1521 _ = fks.Push(new(this, constraintName, objID, refObjID, indexID, objectID, del_action, up_action, parentAndRefColIDs, disabled, notTrusted)); 1522 objID = nextID; 1523 constraintName = rdr.GetString(1); 1524 refObjID = rdr.GetInt32(2); 1525 indexID = rdr.GetInt32(3); 1526 action = rdr.GetByte(4); 1527 del_action = action switch { 1528 byte.MinValue => ReferentialAction.NO_ACTION, 1529 1 => ReferentialAction.CASCADE, 1530 2 => ReferentialAction.SET_NULL, 1531 3 => ReferentialAction.SET_DEFAULT, 1532 _ => throw new InvalidOperationException($"The delete_referential_action, {action.ToString()}, is invalid!"), 1533 }; 1534 action = rdr.GetByte(5); 1535 up_action = action switch { 1536 byte.MinValue => ReferentialAction.NO_ACTION, 1537 1 => ReferentialAction.CASCADE, 1538 2 => ReferentialAction.SET_NULL, 1539 3 => ReferentialAction.SET_DEFAULT, 1540 _ => throw new InvalidOperationException($"The update_referential_action, {action.ToString()}, is invalid!"), 1541 }; 1542 disabled = rdr.GetBoolean(6); 1543 notTrusted = rdr.GetBoolean(7); 1544 parentAndRefColIDs = Vec<Prod<int, int>>.WithCapacity(2u); 1545 } 1546 _ = parentAndRefColIDs.Push(new(rdr.GetInt32(8), rdr.GetInt32(9))); 1547 } 1548 _ = fks.Push(new(this, constraintName, objID, refObjID, indexID, objectID, del_action, up_action, parentAndRefColIDs, disabled, notTrusted)); 1549 } 1550 if (!rdr.NextResult()) { throw new InvalidOperationException($"There is a bug in SQLServer.Schema.GetUserTable! The query failed to grab the result set associated with any CHECK CONSTRAINTs for the object_id {objectID.ToString()} under {IntoString()} despite already verifying that user table exists."); } 1551 var cs = Vec<CHECK_CONSTRAINT>.WithCapacity(2u); 1552 SqlString def; 1553 while (rdr.Read()) { _ = cs.Push(new(this, rdr.GetString(0), rdr.GetInt32(1), objectID, rdr.GetBoolean(2), rdr.GetBoolean(3), rdr.GetInt32(4), (def = rdr.GetSqlString(5)).IsNull ? null : def.Value, rdr.GetBoolean(6))); } 1554 return !(rdr.NextResult() && rdr.Read()) ? throw new InvalidOperationException($"There is a bug in SQLServer.Schema.GetUserTable! The query failed to grab the count of child objects associated with object_id, {objectID.ToString()}, under {IntoString()} despite already verifying that user table exists.") : (new(new Maybe<UserTable>(new(this, name, columns, indexes, (ushort)rdr.GetInt16(0), objectID, pk, uqs, fks, cs)))); 1555 } 1556 } 1557 public readonly Maybe<UserTable> GetUserTable(string name, Maybe<NonZeroUshort> timeout, SessionOptions options) { 1558 1559 using var con = Functions.CreateOpenedConnection(in Database, options, false, Maybe<Uri>.None()); 1560 using var txn = con.BeginTransaction(IsolationLevel.Serializable); 1561 return GetUserTable(name, timeout, txn).MapOrElse((err) => { txn.Rollback(); throw new InvalidOperationException(err.IntoString()); }, (obj) => { txn.Commit(); return obj; }); 1562 } 1563 public readonly Result<Maybe<UserTable>, TransactionError> GetUserTable(string name, Maybe<NonZeroUshort> timeout, SqlTransaction txn) { 1564 1565 var id = GetObjectID(name, timeout, txn); 1566 if (id.IsOK) { 1567 var i = id.Unwrap(); 1568 return i.IsSome ? GetUserTable(i.Unwrap(), timeout, txn) : new(Maybe<UserTable>.None()); 1569 } else { 1570 throw new InvalidOperationException(id.UnwrapErr().IntoString()); 1571 } 1572 } 1573 public readonly Maybe<CHECK_CONSTRAINT> GetCHECK_CONSTRAINT(int objectID, Maybe<NonZeroUshort> timeout, SessionOptions options) { 1574 1575 using var con = Functions.CreateOpenedConnection(in Database, options, false, Maybe<Uri>.None()); 1576 using var txn = con.BeginTransaction(IsolationLevel.Serializable); 1577 return GetCHECK_CONSTRAINT(objectID, timeout, txn).MapOrElse((err) => { txn.Rollback(); throw new InvalidOperationException(err.IntoString()); }, (obj) => { txn.Commit(); return obj; }); 1578 } 1579 [System.Diagnostics.CodeAnalysis.SuppressMessage("Security", "CA2100:Review SQL queries for security vulnerabilities", Justification = "The text is generated internally.")] 1580 public readonly Result<Maybe<CHECK_CONSTRAINT>, TransactionError> GetCHECK_CONSTRAINT(int objectID, Maybe<NonZeroUshort> timeout, SqlTransaction txn) { 1581 1582 if (txn.Connection.State != ConnectionState.Open) { 1583 return new(new TransactionError(TransactionError.Tag.SqlConnectionIsNotOpen, new StackTrace(1, true))); 1584 } else if (!string.Equals(txn.Connection.DataSource, $"tcp:{Database.Server.IntoString()}", StringComparison.Ordinal)) { 1585 return new(new TransactionError(TransactionError.Tag.SqlConnectionServerMismatch, new StackTrace(1, true))); 1586 } else { 1587 using SqlCommand qry = new($@"SELECT CONVERT(nvarchar(128), name) AS fstrName, --0 nvarchar 1588 object_id, --1 int 1589 parent_object_id, --2 int 1590 is_disabled, -- 3 bit 1591 is_not_trusted, --4 bit 1592 parent_column_id, --5 int 1593 definition, --6 nvarchar nullable 1594 uses_database_collation --7 bit 1595 FROM [{Database.Name.Value}].sys.check_constraints 1596 WHERE schema_id = @schemaID AND object_id = @objID;", txn.Connection, txn, SqlCommandColumnEncryptionSetting.Disabled) { CommandTimeout = timeout.MapOr(0, _nzUshortToInt), CommandType = CommandType.Text, EnableOptimizedParameterBinding = true }; 1597 _ = qry.Parameters.Add(new SqlParameter("@schemaID", SqlDbType.Int) { SqlValue = SchemaID }); 1598 _ = qry.Parameters.Add(new SqlParameter("@objID", SqlDbType.Int) { SqlValue = objectID }); 1599 using var rdr = qry.ExecuteReader(CommandBehavior.SingleResult | CommandBehavior.SingleRow | CommandBehavior.SequentialAccess); 1600 SqlString def; 1601 return new(rdr.Read() ? new Maybe<CHECK_CONSTRAINT>(new(this, rdr.GetString(0), rdr.GetInt32(1), rdr.GetInt32(2), rdr.GetBoolean(3), rdr.GetBoolean(4), rdr.GetInt32(5), (def = rdr.GetSqlString(6)).IsNull ? null : def.Value, rdr.GetBoolean(7))) : Maybe<CHECK_CONSTRAINT>.None()); 1602 } 1603 } 1604 public readonly Maybe<CHECK_CONSTRAINT> GetCHECK_CONSTRAINT(string name, Maybe<NonZeroUshort> timeout, SessionOptions options) { 1605 1606 using var con = Functions.CreateOpenedConnection(in Database, options, false, Maybe<Uri>.None()); 1607 using var txn = con.BeginTransaction(IsolationLevel.Serializable); 1608 return GetCHECK_CONSTRAINT(name, timeout, txn).MapOrElse((err) => { txn.Rollback(); throw new InvalidOperationException(err.IntoString()); }, (obj) => { txn.Commit(); return obj; }); 1609 } 1610 public readonly Result<Maybe<CHECK_CONSTRAINT>, TransactionError> GetCHECK_CONSTRAINT(string name, Maybe<NonZeroUshort> timeout, SqlTransaction txn) { 1611 1612 var id = GetObjectID(name, timeout, txn); 1613 if (id.IsOK) { 1614 var i = id.Unwrap(); 1615 return i.IsSome ? GetCHECK_CONSTRAINT(i.Unwrap(), timeout, txn) : new(Maybe<CHECK_CONSTRAINT>.None()); 1616 } else { 1617 throw new InvalidOperationException(id.UnwrapErr().IntoString()); 1618 } 1619 } 1620 public readonly Maybe<PRIMARY_KEY_CONSTRAINT> GetPRIMARY_KEY_CONSTRAINT(int objectID, Maybe<NonZeroUshort> timeout, SessionOptions options) { 1621 1622 using var con = Functions.CreateOpenedConnection(in Database, options, false, Maybe<Uri>.None()); 1623 using var txn = con.BeginTransaction(IsolationLevel.Serializable); 1624 return GetPRIMARY_KEY_CONSTRAINT(objectID, timeout, txn).MapOrElse((err) => { txn.Rollback(); throw new InvalidOperationException(err.IntoString()); }, (obj) => { txn.Commit(); return obj; }); 1625 } 1626 [System.Diagnostics.CodeAnalysis.SuppressMessage("Security", "CA2100:Review SQL queries for security vulnerabilities", Justification = "The text is generated internally.")] 1627 public readonly Result<Maybe<PRIMARY_KEY_CONSTRAINT>, TransactionError> GetPRIMARY_KEY_CONSTRAINT(int objectID, Maybe<NonZeroUshort> timeout, SqlTransaction txn) { 1628 1629 if (txn.Connection.State != ConnectionState.Open) { 1630 return new(new TransactionError(TransactionError.Tag.SqlConnectionIsNotOpen, new StackTrace(1, true))); 1631 } else if (!string.Equals(txn.Connection.DataSource, $"tcp:{Database.Server.IntoString()}", StringComparison.Ordinal)) { 1632 return new(new TransactionError(TransactionError.Tag.SqlConnectionServerMismatch, new StackTrace(1, true))); 1633 } else { 1634 using SqlCommand qry = new($@"SELECT CONVERT(nvarchar(128), name) AS fstrName, --0 nvarchar 1635 object_id, --1 int 1636 parent_object_id, --2 int 1637 unique_index_id -- 3 int 1638 FROM [{Database.Name.Value}].sys.key_constraints 1639 WHERE schema_id = @schemaID AND object_id = @objID AND type = 'PK';", txn.Connection, txn, SqlCommandColumnEncryptionSetting.Disabled) { CommandTimeout = timeout.MapOr(0, _nzUshortToInt), CommandType = CommandType.Text, EnableOptimizedParameterBinding = true }; 1640 _ = qry.Parameters.Add(new SqlParameter("@schemaID", SqlDbType.Int) { SqlValue = SchemaID }); 1641 _ = qry.Parameters.Add(new SqlParameter("@objID", SqlDbType.Int) { SqlValue = objectID }); 1642 using var rdr = qry.ExecuteReader(CommandBehavior.SingleResult | CommandBehavior.SingleRow | CommandBehavior.SequentialAccess); 1643 return new(rdr.Read() ? new Maybe<PRIMARY_KEY_CONSTRAINT>(new(this, rdr.GetString(0), rdr.GetInt32(1), rdr.GetInt32(2), rdr.GetInt32(3))) : Maybe<PRIMARY_KEY_CONSTRAINT>.None()); 1644 } 1645 } 1646 public readonly Maybe<PRIMARY_KEY_CONSTRAINT> GetPRIMARY_KEY_CONSTRAINT(string name, Maybe<NonZeroUshort> timeout, SessionOptions options) { 1647 1648 using var con = Functions.CreateOpenedConnection(in Database, options, false, Maybe<Uri>.None()); 1649 using var txn = con.BeginTransaction(IsolationLevel.Serializable); 1650 return GetPRIMARY_KEY_CONSTRAINT(name, timeout, txn).MapOrElse((err) => { txn.Rollback(); throw new InvalidOperationException(err.IntoString()); }, (obj) => { txn.Commit(); return obj; }); 1651 } 1652 public readonly Result<Maybe<PRIMARY_KEY_CONSTRAINT>, TransactionError> GetPRIMARY_KEY_CONSTRAINT(string name, Maybe<NonZeroUshort> timeout, SqlTransaction txn) { 1653 1654 var id = GetObjectID(name, timeout, txn); 1655 if (id.IsOK) { 1656 var i = id.Unwrap(); 1657 return i.IsSome ? GetPRIMARY_KEY_CONSTRAINT(i.Unwrap(), timeout, txn) : new(Maybe<PRIMARY_KEY_CONSTRAINT>.None()); 1658 } else { 1659 throw new InvalidOperationException(id.UnwrapErr().IntoString()); 1660 } 1661 } 1662 public readonly Maybe<UNIQUE_CONSTRAINT> GetUNIQUE_CONSTRAINT(int objectID, Maybe<NonZeroUshort> timeout, SessionOptions options) { 1663 1664 using var con = Functions.CreateOpenedConnection(in Database, options, false, Maybe<Uri>.None()); 1665 using var txn = con.BeginTransaction(IsolationLevel.Serializable); 1666 return GetUNIQUE_CONSTRAINT(objectID, timeout, txn).MapOrElse((err) => { txn.Rollback(); throw new InvalidOperationException(err.IntoString()); }, (obj) => { txn.Commit(); return obj; }); 1667 } 1668 [System.Diagnostics.CodeAnalysis.SuppressMessage("Security", "CA2100:Review SQL queries for security vulnerabilities", Justification = "The text is generated internally.")] 1669 public readonly Result<Maybe<UNIQUE_CONSTRAINT>, TransactionError> GetUNIQUE_CONSTRAINT(int objectID, Maybe<NonZeroUshort> timeout, SqlTransaction txn) { 1670 1671 if (txn.Connection.State != ConnectionState.Open) { 1672 return new(new TransactionError(TransactionError.Tag.SqlConnectionIsNotOpen, new StackTrace(1, true))); 1673 } else if (!string.Equals(txn.Connection.DataSource, $"tcp:{Database.Server.IntoString()}", StringComparison.Ordinal)) { 1674 return new(new TransactionError(TransactionError.Tag.SqlConnectionServerMismatch, new StackTrace(1, true))); 1675 } else { 1676 using SqlCommand qry = new($@"SELECT CONVERT(nvarchar(128), name) AS fstrName, --0 nvarchar 1677 object_id, --1 int 1678 parent_object_id, --2 int 1679 unique_index_id -- 3 int 1680 FROM [{Database.Name.Value}].sys.key_constraints 1681 WHERE schema_id = @schemaID AND object_id = @objID AND type = 'UQ';", txn.Connection, txn, SqlCommandColumnEncryptionSetting.Disabled) { CommandTimeout = timeout.MapOr(0, _nzUshortToInt), CommandType = CommandType.Text, EnableOptimizedParameterBinding = true }; 1682 _ = qry.Parameters.Add(new SqlParameter("@schemaID", SqlDbType.Int) { SqlValue = SchemaID }); 1683 _ = qry.Parameters.Add(new SqlParameter("@objID", SqlDbType.Int) { SqlValue = objectID }); 1684 using var rdr = qry.ExecuteReader(CommandBehavior.SingleResult | CommandBehavior.SingleRow | CommandBehavior.SequentialAccess); 1685 return new(rdr.Read() ? new Maybe<UNIQUE_CONSTRAINT>(new(this, rdr.GetString(0), rdr.GetInt32(1), rdr.GetInt32(2), rdr.GetInt32(3))) : Maybe<UNIQUE_CONSTRAINT>.None()); 1686 } 1687 } 1688 public readonly Maybe<UNIQUE_CONSTRAINT> GetUNIQUE_CONSTRAINT(string name, Maybe<NonZeroUshort> timeout, SessionOptions options) { 1689 1690 using var con = Functions.CreateOpenedConnection(in Database, options, false, Maybe<Uri>.None()); 1691 using var txn = con.BeginTransaction(IsolationLevel.Serializable); 1692 return GetUNIQUE_CONSTRAINT(name, timeout, txn).MapOrElse((err) => { txn.Rollback(); throw new InvalidOperationException(err.IntoString()); }, (obj) => { txn.Commit(); return obj; }); 1693 } 1694 public readonly Result<Maybe<UNIQUE_CONSTRAINT>, TransactionError> GetUNIQUE_CONSTRAINT(string name, Maybe<NonZeroUshort> timeout, SqlTransaction txn) { 1695 1696 var id = GetObjectID(name, timeout, txn); 1697 if (id.IsOK) { 1698 var i = id.Unwrap(); 1699 return i.IsSome ? GetUNIQUE_CONSTRAINT(i.Unwrap(), timeout, txn) : new(Maybe<UNIQUE_CONSTRAINT>.None()); 1700 } else { 1701 throw new InvalidOperationException(id.UnwrapErr().IntoString()); 1702 } 1703 } 1704 public readonly Maybe<FOREIGN_KEY_CONSTRAINT> GetFOREIGN_KEY_CONSTRAINT(int objectID, Maybe<NonZeroUshort> timeout, SessionOptions options) { 1705 1706 using var con = Functions.CreateOpenedConnection(in Database, options, false, Maybe<Uri>.None()); 1707 using var txn = con.BeginTransaction(IsolationLevel.Serializable); 1708 return GetFOREIGN_KEY_CONSTRAINT(objectID, timeout, txn).MapOrElse((err) => { txn.Rollback(); throw new InvalidOperationException(err.IntoString()); }, (obj) => { txn.Commit(); return obj; }); 1709 } 1710 [System.Diagnostics.CodeAnalysis.SuppressMessage("Security", "CA2100:Review SQL queries for security vulnerabilities", Justification = "The text is generated internally.")] 1711 public readonly Result<Maybe<FOREIGN_KEY_CONSTRAINT>, TransactionError> GetFOREIGN_KEY_CONSTRAINT(int objectID, Maybe<NonZeroUshort> timeout, SqlTransaction txn) { 1712 1713 if (txn.Connection.State != ConnectionState.Open) { 1714 return new(new TransactionError(TransactionError.Tag.SqlConnectionIsNotOpen, new StackTrace(1, true))); 1715 } else if (!string.Equals(txn.Connection.DataSource, $"tcp:{Database.Server.IntoString()}", StringComparison.Ordinal)) { 1716 return new(new TransactionError(TransactionError.Tag.SqlConnectionServerMismatch, new StackTrace(1, true))); 1717 } else { 1718 using SqlCommand qry = new($@"SELECT CONVERT(nvarchar(128), f.name) AS fstrName, --0 nvarchar 1719 f.object_id, --1 int 1720 f.referenced_object_id, --2 int 1721 f.key_index_id, --3 int 1722 f.parent_object_id, --4 int 1723 f.delete_referential_action, --5 tinyint 1724 f.update_referential_action, --6 tinyint 1725 f.is_disabled, -- 7 bit 1726 f.is_not_trusted, --8 bit 1727 f2.parent_column_id, --9 int 1728 f2.referenced_column_id --10 int 1729 FROM [{Database.Name.Value}].sys.foreign_keys AS f 1730 INNER JOIN [{Database.Name.Value}].sys.foreign_key_columns AS f2 1731 ON f.object_id = f2.constraint_object_id 1732 WHERE f.schema_id = @schemaID AND f.object_id = @objID 1733 ORDER BY f.object_id ASC, f2.constraint_column_id ASC;", txn.Connection, txn, SqlCommandColumnEncryptionSetting.Disabled) { CommandTimeout = timeout.MapOr(0, _nzUshortToInt), CommandType = CommandType.Text, EnableOptimizedParameterBinding = true }; 1734 _ = qry.Parameters.Add(new SqlParameter("@schemaID", SqlDbType.Int) { SqlValue = SchemaID }); 1735 _ = qry.Parameters.Add(new SqlParameter("@objID", SqlDbType.Int) { SqlValue = objectID }); 1736 using var rdr = qry.ExecuteReader(CommandBehavior.SingleResult | CommandBehavior.SingleRow | CommandBehavior.SequentialAccess); 1737 if (rdr.Read()) { 1738 var name = rdr.GetString(0); 1739 var id = rdr.GetInt32(1); 1740 var ref_id = rdr.GetInt32(2); 1741 var indexID = rdr.GetInt32(3); 1742 var parent_id = rdr.GetInt32(4); 1743 var action = rdr.GetByte(5); 1744 var del_action = action switch { 1745 byte.MinValue => ReferentialAction.NO_ACTION, 1746 1 => ReferentialAction.CASCADE, 1747 2 => ReferentialAction.SET_NULL, 1748 3 => ReferentialAction.SET_DEFAULT, 1749 _ => throw new InvalidOperationException($"The delete_referential_action, {action.ToString()}, is invalid!"), 1750 }; 1751 action = rdr.GetByte(6); 1752 var up_action = action switch { 1753 byte.MinValue => ReferentialAction.NO_ACTION, 1754 1 => ReferentialAction.CASCADE, 1755 2 => ReferentialAction.SET_NULL, 1756 3 => ReferentialAction.SET_DEFAULT, 1757 _ => throw new InvalidOperationException($"The update_referential_action, {action.ToString()}, is invalid!"), 1758 }; 1759 var disabled = rdr.GetBoolean(7); 1760 var notTrusted = rdr.GetBoolean(8); 1761 var parentRefColumnIDs = Vec<Prod<int, int>>.WithCapacity(4u); 1762 _ = parentRefColumnIDs.Push(new(rdr.GetInt32(9), rdr.GetInt32(10))); 1763 while (rdr.Read()) { _ = parentRefColumnIDs.Push(new(rdr.GetInt32(9), rdr.GetInt32(10))); } 1764 return new(new Maybe<FOREIGN_KEY_CONSTRAINT>(new(this, name, id, ref_id, indexID, parent_id, del_action, up_action, parentRefColumnIDs, disabled, notTrusted))); 1765 } else { 1766 return new(Maybe<FOREIGN_KEY_CONSTRAINT>.None()); 1767 } 1768 } 1769 } 1770 public readonly Maybe<FOREIGN_KEY_CONSTRAINT> GetFOREIGN_KEY_CONSTRAINT(string name, Maybe<NonZeroUshort> timeout, SessionOptions options) { 1771 1772 using var con = Functions.CreateOpenedConnection(in Database, options, false, Maybe<Uri>.None()); 1773 using var txn = con.BeginTransaction(IsolationLevel.Serializable); 1774 return GetFOREIGN_KEY_CONSTRAINT(name, timeout, txn).MapOrElse((err) => { txn.Rollback(); throw new InvalidOperationException(err.IntoString()); }, (obj) => { txn.Commit(); return obj; }); 1775 } 1776 public readonly Result<Maybe<FOREIGN_KEY_CONSTRAINT>, TransactionError> GetFOREIGN_KEY_CONSTRAINT(string name, Maybe<NonZeroUshort> timeout, SqlTransaction txn) { 1777 1778 var id = GetObjectID(name, timeout, txn); 1779 if (id.IsOK) { 1780 var i = id.Unwrap(); 1781 return i.IsSome ? GetFOREIGN_KEY_CONSTRAINT(i.Unwrap(), timeout, txn) : new(Maybe<FOREIGN_KEY_CONSTRAINT>.None()); 1782 } else { 1783 throw new InvalidOperationException(id.UnwrapErr().IntoString()); 1784 } 1785 } 1786 public readonly Unit Hash<THasher>(ref THasher hasher) where THasher: notnull, IHasher { 1787 1788 _ = Database.Hash(ref hasher); 1789 return hasher.WriteInt(SchemaID); 1790 } 1791 public readonly Schema Into() => this; 1792 public readonly string IntoString() => ToString(); 1793 readonly string IInto<string>.Into() => IntoString(); 1794 public override readonly string ToString() => $"{Database.IntoString()}.{Name.Value}"; 1795 readonly Result<Schema, Bottom> ITryInto<Schema, Bottom>.TryInto() => new(this); 1796 readonly Result<string, Bottom> ITryInto<string, Bottom>.TryInto() => new(ToString()); 1797 #endregion 1798 1799 #region Operators 1800 public static bool operator !=(Schema val0, Schema val1) => !(val0 == val1); 1801 public static bool operator ==(Schema val0, Schema val1) => val0.Database == val1.Database && val0.SchemaID == val1.SchemaID; 1802 #endregion 1803 1804 #region Types 1805 #endregion 1806 } 1807 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 0)] 1808 public readonly struct Server: IClone<Server>, IEq<Server>, IHashable, IInto<Server>, IInto<string> { 1809 1810 #region Type-level Constructors 1811 #endregion 1812 1813 #region Instance Constructors 1814 public Server() => throw new InvalidOperationException("Parameterless constructor is not allowed to be called!"); 1815 Server(string server, string instance, ushort port, CulturedString serverName, HashSet<CulturedString, FNVHasher, RandomFNVHashBuilder> linkedServers, HashSet<DatabaseHack, XXHasher, RandomXXHashBuilder> databases, string collation) => (Hostname, Instance, Port, Name, _linkedServers, _databases, Collation) = (server, instance, port, serverName, linkedServers, databases, collation); 1816 #endregion 1817 1818 #region Type-level Fields 1819 static readonly Fn<NonZeroUshort, int> _nzUshortToInt = (x) => x.IntoUshort(); 1820 #endregion 1821 1822 #region Instance Fields 1823 readonly HashSet<DatabaseHack, XXHasher, RandomXXHashBuilder> _databases; 1824 readonly HashSet<CulturedString, FNVHasher, RandomFNVHashBuilder> _linkedServers; 1825 public readonly CulturedString Name; 1826 public readonly string Hostname; 1827 public readonly string Instance; 1828 public readonly string Collation; 1829 public readonly ushort Port; 1830 #endregion 1831 1832 #region Type-level Properties 1833 #endregion 1834 1835 #region Instance Properties 1836 #endregion 1837 1838 #region Type-level Functions 1839 // We require TCP connectivity and static ports. 1840 // If the port is not 0, instance is completely ignored. 1841 // If instance is empty and port is 0, port will be changed to 1433. 1842 [System.Diagnostics.CodeAnalysis.SuppressMessage("Security", "CA2100:Review SQL queries for security vulnerabilities", Justification = "The text is generated internally.")] 1843 public static Server New(string server, string instance, ushort port, Maybe<NonZeroUshort> timeout, SessionOptions options) { 1844 1845 using SqlConnection con = new(Functions.CreateConnectionString(server, instance = port > ushort.MinValue ? string.Empty : instance, port = port > ushort.MinValue || instance.Length > 0 ? port : (ushort)1433, false, Maybe<Uri>.None())); 1846 con.Open(); 1847 using var txn = con.BeginTransaction(IsolationLevel.Serializable); 1848 CulturedString serverName; 1849 string serverCollation; 1850 HashSet<CulturedString, FNVHasher, RandomFNVHashBuilder> linked; 1851 HashSet<DatabaseHack, XXHasher, RandomXXHashBuilder> databases; 1852 var timeoutVal = timeout.MapOr(0, _nzUshortToInt); 1853 using (SqlCommand qry = new($@"{Functions.CreateSessionString(options)} 1854 DECLARE @comp AS int = CONVERT(int, SERVERPROPERTY('ComparisonStyle')); 1855 SELECT @@SERVERNAME AS name, --0 nvarchar 1856 CONVERT(int, SERVERPROPERTY('LCID')) AS flngLCID, --1 int 1857 CASE @comp 1858 WHEN 0 THEN 1073741824 1859 ELSE (@comp & 1) | CASE @comp & 65536 1860 WHEN 65536 THEN 8 1861 ELSE 0 1862 END | CASE @comp & 131072 1863 WHEN 131072 THEN 16 1864 ELSE 0 1865 END 1866 END AS flngStringComparison, --2 int 1867 CONVERT(nvarchar(128), SERVERPROPERTY('collation')); --3 nvarchar 1868 1869 SELECT CONVERT(nvarchar(128), name) AS fstrName --0 nvarchar 1870 FROM sys.servers; 1871 1872 DECLARE @tbl AS table (fstrName nvarchar(128) NOT NULL, flngLCID int NULL, flngComp int NOT NULL, is_read_only bit NOT NULL, fstrCollation nvarchar(128) NOT NULL, flngDatabaseID int NOT NULL); 1873 INSERT INTO @tbl 1874 SELECT CONVERT(nvarchar(128), name), 1875 CONVERT(int, COLLATIONPROPERTY(collation_name, 'LCID')), 1876 CONVERT(int, COLLATIONPROPERTY(collation_name, 'ComparisonStyle')), 1877 is_read_only, 1878 CONVERT(nvarchar(128), collation_name), 1879 database_id 1880 FROM sys.databases 1881 WHERE HAS_DBACCESS(name) = 1; 1882 1883 SELECT fstrName, --0 nvarchar 1884 flngLCID, --1 int nullable 1885 CASE flngComp 1886 WHEN 0 THEN 1073741824 1887 ELSE (flngComp & 1) | CASE flngComp & 65536 1888 WHEN 65536 THEN 8 1889 ELSE 0 1890 END | CASE flngComp & 131072 1891 WHEN 131072 THEN 16 1892 ELSE 0 1893 END 1894 END AS flngComp, --2 int 1895 is_read_only, --3 bit 1896 fstrCollation, --4 nvarchar 1897 flngDatabaseID --5 int 1898 FROM @tbl;", con, txn, SqlCommandColumnEncryptionSetting.Disabled) { CommandTimeout = timeoutVal, CommandType = CommandType.Text, EnableOptimizedParameterBinding = true }) { 1899 using var rdr = qry.ExecuteReader(CommandBehavior.SequentialAccess); 1900 if (!rdr.Read()) { throw new InvalidOperationException($@"There is a bug in SQLServer.Server.New! The query failed to grab collation information for {server}{(instance.Length == 0 ? string.Empty : $@"\{instance}")}{(port == ushort.MinValue ? string.Empty : $",{port.ToString()}")} despite successfully connecting to the hostname."); } 1901 // This may cause an Exception if the LCID does not exist. 1902 // Also note that this is only an approximation of how strings are compared in SQL Server since 1903 // collations in SQL Server don't have a perfect mapping to something in .NET. 1904 serverName = new(rdr.GetString(0), new CultureInfo(rdr.GetInt32(1), false), (CompareOptions)rdr.GetInt32(2)); 1905 serverCollation = rdr.GetString(3); 1906 if (!rdr.NextResult()) { throw new InvalidOperationException($@"There is a bug in SQLServer.Server.New! The query failed to return a result set of servers from {server}{(instance.Length == 0 ? string.Empty : $@"\{instance}")}{(port == ushort.MinValue ? string.Empty : $",{port.ToString()}")}.master.sys.servers despite successfully connecting to the hostname."); } 1907 linked = HashSet<CulturedString, FNVHasher, RandomFNVHashBuilder>.WithCapacityAndHasher(8u, RandomFNVHashBuilder.New()); 1908 while (rdr.Read()) { if (!linked.Insert(new(rdr.GetString(0), serverName.Culture, serverName.Options))) { throw new InvalidOperationException($@"Multiple servers from {server}{(instance.Length == 0 ? string.Empty : $@"\{instance}")}{(port == ushort.MinValue ? string.Empty : $",{port.ToString()}")}.master.sys.servers are considered the same in SQLServer.Server.New!"); } } 1909 if (linked.Len == uint.MinValue) { throw new InvalidOperationException($@"There is a bug in SQLServer.Server.New! The query failed to grab a hostname from {server}{(instance.Length == 0 ? string.Empty : $@"\{instance}")}{(port == ushort.MinValue ? string.Empty : $",{port.ToString()}")}.master.sys.servers, but that view is guaranteed to contain at least one row."); } 1910 if (!rdr.NextResult()) { throw new InvalidOperationException($@"There is a bug in SQLServer.Server.New! The query failed to return a result set of databases in {server}{(instance.Length == 0 ? string.Empty : $@"\{instance}")}{(port == ushort.MinValue ? string.Empty : $",{port.ToString()}")} despite successfully connecting to the hostname."); } 1911 databases = HashSet<DatabaseHack, XXHasher, RandomXXHashBuilder>.WithCapacityAndHasher(64u, RandomXXHashBuilder.New()); 1912 CulturedString database; 1913 CultureInfo cultureInfo; 1914 CompareOptions compareOptions; 1915 bool isReadOnly; 1916 HashMap<CulturedString, int, FNVHasher, RandomFNVHashBuilder> schemas; 1917 string collation; 1918 int databaseID; 1919 // Instead of connecting to all the databases separately, we reuse the same connection and transaction. 1920 using SqlConnection con2 = new(Functions.CreateConnectionString(server, instance, port, false, Maybe<Uri>.None())); 1921 con2.Open(); 1922 using var txn2 = con2.BeginTransaction(IsolationLevel.Serializable); 1923 // Set the session settings once. 1924 using (SqlCommand qry2 = new($"{Functions.CreateSessionString(options)}", con2, txn2, SqlCommandColumnEncryptionSetting.Disabled) { CommandTimeout = 60, CommandType = CommandType.Text, EnableOptimizedParameterBinding = true }) { _ = qry2.ExecuteNonQuery(); } 1925 1926 while (rdr.Read()) { 1927 database = new(rdr.GetString(0), serverName.Culture, serverName.Options); 1928 // This may cause an Exception if the LCID does not exist. 1929 // Also note that this is only an approximation of how strings are compared in SQL Server since 1930 // collations in SQL Server don't have a perfect mapping to something in .NET. 1931 cultureInfo = new(rdr.GetInt32(1), false); 1932 compareOptions = (CompareOptions)rdr.GetInt32(2); 1933 isReadOnly = rdr.GetBoolean(3); 1934 collation = rdr.GetString(4); 1935 databaseID = rdr.GetInt32(5); 1936 // Since we reuse the same connection, we must explicitly USE the database. 1937 using (SqlCommand qry2 = new($@"USE [{database.Value}]; 1938 SELECT CONVERT(nvarchar(128), name) AS fstrName, --0 nvarchar 1939 schema_id --1 int 1940 FROM sys.schemas;", con2, txn2, SqlCommandColumnEncryptionSetting.Disabled) { CommandTimeout = timeoutVal, CommandType = CommandType.Text, EnableOptimizedParameterBinding = true }) { 1941 using var rdr2 = qry2.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult); 1942 if (!rdr2.HasRows) { throw new InvalidOperationException($@"There is a bug in SQLServer.Server.New! The query failed to return any schemas associated with {server}{(instance.Length == 0 ? string.Empty : $@"\{instance}")}{(port == ushort.MinValue ? string.Empty : $",{port.ToString()}")}.{database.Value} despite successfully connecting to the database."); } 1943 schemas = HashMap<CulturedString, int, FNVHasher, RandomFNVHashBuilder>.WithCapacityAndHasher(16u, RandomFNVHashBuilder.New()); 1944 while (rdr2.Read()) { if (schemas.Insert(new(rdr2.GetString(0), cultureInfo, compareOptions), rdr2.GetInt32(1)).IsSome) { throw new InvalidOperationException($@"Multiple schemas from {server}{(instance.Length == 0 ? string.Empty : $@"\{instance}")}{(port == ushort.MinValue ? string.Empty : $",{port.ToString()}")}.{database.Value}.sys.schemas are considered the same in SQLServer.Server.New!"); } } 1945 } 1946 if (schemas.Len == uint.MinValue) { throw new InvalidOperationException($@"There is a bug in SQLServer.Server.New! The query failed to grab a schema from {server}{(instance.Length == 0 ? string.Empty : $@"\{instance}")}{(port == ushort.MinValue ? string.Empty : $",{port.ToString()}")}.{database.Value} despite successfully connecting to the database."); } 1947 if (!databases.Insert(new(database, cultureInfo, compareOptions, isReadOnly, schemas, collation, databaseID))) { throw new InvalidOperationException($@"Multiple databases from {server}{(instance.Length == 0 ? string.Empty : $@"\{instance}")}{(port == ushort.MinValue ? string.Empty : $",{port.ToString()}")}.master.sys.databases are considered the same in SQLServer.Server.New!"); } 1948 } 1949 if (databases.Len == uint.MinValue) { throw new InvalidOperationException($@"There is a bug in SQLServer.Server.New! The query failed to grab a database from {server}{(instance.Length == 0 ? string.Empty : $@"\{instance}")}{(port == ushort.MinValue ? string.Empty : $",{port.ToString()}")}.master.sys.databases despite successfully connecting to the server."); } 1950 txn2.Commit(); 1951 } 1952 txn.Commit(); 1953 return new Server(server, instance, port, serverName, linked, databases, serverCollation); 1954 } 1955 #endregion 1956 1957 #region Instance Functions 1958 // All properties except Databases and LinkedServers are completely safe to return a copy of since they are all immutable as is Server. 1959 // Since both Databases and LinkedServers are not mutated internally and not exposed, it is safe to copy them as well. 1960 public readonly Server Clone() => this; 1961 public override readonly bool Equals(object? _) => false; 1962 public readonly Maybe<Database> GetDatabase(string name) { 1963 // Safe to use default for the HashSet since the HashSet is not used in Hash or Eq. 1964 var val = _databases.Get(new(new(name, Name.Culture, Name.Options), Name.Culture, Name.Options, false, default, string.Empty, 0)); 1965 return val.IsNone ? Maybe<Database>.None() : new(new(this, val.Unwrap())); 1966 } 1967 public override readonly int GetHashCode() => 0; 1968 public readonly Maybe<string> GetLinkedServerName(in Server server) => this == server ? new(Name.Value) : _linkedServers.Contains(in server.Name) ? new(server.Name.Value) : _linkedServers.Contains(new($"{server.Name.Value}{(server.Port == ushort.MinValue ? string.Empty : $",{server.Port.ToString()}")}", Name.Culture, Name.Options)) ? new($"{server.Name.Value}{(server.Port == ushort.MinValue ? string.Empty : $",{server.Port.ToString()}")}") : Maybe<string>.None(); 1969 public readonly Unit Hash<THasher>(ref THasher hasher) where THasher: notnull, IHasher { 1970 1971 _ = new CulturedString(Hostname, CultureInfo.InvariantCulture, CompareOptions.OrdinalIgnoreCase).Hash(ref hasher); 1972 _ = new CulturedString(Instance, CultureInfo.InvariantCulture, CompareOptions.OrdinalIgnoreCase).Hash(ref hasher); 1973 return hasher.WriteUshort(Port); 1974 } 1975 public readonly Server Into() => this; 1976 public readonly string IntoString() => ToString(); 1977 readonly string IInto<string>.Into() => IntoString(); 1978 public override readonly string ToString() => $"{Hostname}{(Instance.Length == 0 ? string.Empty : $@"\{Instance}")}{(Port == ushort.MinValue ? string.Empty : $",{Port.ToString()}")}"; 1979 readonly Result<Server, Bottom> ITryInto<Server, Bottom>.TryInto() => new(this); 1980 readonly Result<string, Bottom> ITryInto<string, Bottom>.TryInto() => new(ToString()); 1981 #endregion 1982 1983 #region Operators 1984 public static bool operator !=(Server val0, Server val1) => !(val0 == val1); 1985 public static bool operator ==(Server val0, Server val1) => string.Equals(val0.Hostname, val1.Hostname, StringComparison.OrdinalIgnoreCase) && string.Equals(val0.Instance, val1.Instance, StringComparison.OrdinalIgnoreCase) && val0.Port == val1.Port; 1986 #endregion 1987 1988 #region Types 1989 // Ideally, Server would contain a HashSet that contains Databases; but since Databases contain a Server instance, this makes 1990 // Server and Database recursive structs. This should not be a problem since internally HashSet is just an array; but due to a 1991 // bug (https://github.com/dotnet/runtime/issues/5479, https://github.com/dotnet/runtime/issues/6924, https://github.com/dotnet/runtime/issues/12024) in CoreCLR, this causes a System.TypeLoadException. 1992 // Consequently, we have to hack around this bug by creating a type that is basically identical to Database but that doesn't contain 1993 // a Server instance. 1994 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 0)] 1995 internal readonly struct DatabaseHack: IEq<DatabaseHack>, IHashable, IInto<DatabaseHack>, IInto<string> { 1996 1997 #region Type-level Constructors 1998 #endregion 1999 2000 #region Instance Constructors 2001 public DatabaseHack() => throw new InvalidOperationException("Parameterless constructor is not allowed to be called!"); 2002 internal DatabaseHack(CulturedString name, CultureInfo culture, CompareOptions cmp, bool isReadOnly, HashMap<CulturedString, int, FNVHasher, RandomFNVHashBuilder> schemas, string collation, int databaseID) => (Name, CultureInfo, CompareOptions, IsReadOnly, Schemas, Collation, DatabaseID) = (name, CultureInfo.ReadOnly(culture), cmp, isReadOnly, schemas, collation, databaseID); 2003 #endregion 2004 2005 #region Type-level Fields 2006 #endregion 2007 2008 #region Instance Fields 2009 internal readonly HashMap<CulturedString, int, FNVHasher, RandomFNVHashBuilder> Schemas; 2010 internal readonly CulturedString Name; 2011 internal readonly CultureInfo CultureInfo; 2012 internal readonly string Collation; 2013 internal readonly CompareOptions CompareOptions; 2014 internal readonly bool IsReadOnly; 2015 internal readonly int DatabaseID; 2016 #endregion 2017 2018 #region Type-level Properties 2019 #endregion 2020 2021 #region Instance Properties 2022 #endregion 2023 2024 #region Type-level Functions 2025 #endregion 2026 2027 #region Instance Functions 2028 public override readonly bool Equals(object? _) => false; 2029 public override readonly int GetHashCode() => 0; 2030 public readonly Unit Hash<THasher>(ref THasher hasher) where THasher: notnull, IHasher => Name.Hash(ref hasher); 2031 public readonly DatabaseHack Into() => this; 2032 public readonly string IntoString() => ToString(); 2033 readonly string IInto<string>.Into() => IntoString(); 2034 public override readonly string ToString() => Name.Value; 2035 readonly Result<DatabaseHack, Bottom> ITryInto<DatabaseHack, Bottom>.TryInto() => new(this); 2036 readonly Result<string, Bottom> ITryInto<string, Bottom>.TryInto() => new(ToString()); 2037 #endregion 2038 2039 #region Operators 2040 public static bool operator !=(DatabaseHack val0, DatabaseHack val1) => !(val0 == val1); 2041 // Server.GetDatabase is based on strings not database IDs; so we need Eq and Hash to use Name and not DatabaseID; 2042 public static bool operator ==(DatabaseHack val0, DatabaseHack val1) => val0.Name == val1.Name; 2043 #endregion 2044 2045 #region Types 2046 #endregion 2047 } 2048 #endregion 2049 } 2050 [Flags()] 2051 public enum SessionOptions: uint { 2052 DEFAULT = uint.MinValue, 2053 ANSI_NULL_DFLT_OFF = 0x1u, 2054 ANSI_NULLS_OFF = 0x2u, 2055 ANSI_PADDING_OFF = 0x4u, 2056 ANSI_WARNINGS_OFF = 0x8u, 2057 ARITHABORT_OFF = 0x10u, 2058 CONCAT_NULL_YIELDS_NULL_OFF = 0x20u, 2059 NOCOUNT_OFF = 0x40u, 2060 NUMERIC_ROUNDABORT_OFF = 0x80u, 2061 QUOTED_IDENTIFIER_OFF = 0x100u, 2062 XACT_ABORT_OFF = 0x200u, 2063 ARITHIGNORE_ON = 0x400u, 2064 CURSOR_CLOSE_ON_COMMIT_ON = 0x800u, 2065 FMTONLY_ON = 0x1000u, 2066 FORCEPLAN_ON = 0x2000u, 2067 IMPLICIT_TRANSACTIONS_ON = 0x4000u, 2068 NOEXEC_ON = 0x8000u, 2069 PARSEONLY_ON = 0x10000u, 2070 } 2071 [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode, Pack = 8, Size = 16)] 2072 public readonly struct TransactionError: ISum<StackTrace, StackTrace>, IError, IInto<TransactionError> { 2073 2074 #region Type-level Constructors 2075 #endregion 2076 2077 #region Instance Constructors 2078 public TransactionError() => throw new InvalidOperationException("Parameterless constructor is not allowed to be called!"); 2079 internal TransactionError(Tag flag, StackTrace trace) => (Var, Backtrace) = (flag, trace); 2080 #endregion 2081 2082 #region Type-level Fields 2083 #endregion 2084 2085 #region Instance Fields 2086 [FieldOffset(0)] public readonly Tag Var; 2087 [FieldOffset(8)] public readonly StackTrace Backtrace; 2088 #endregion 2089 2090 #region Type-level Properties 2091 #endregion 2092 2093 #region Instance Properties 2094 public readonly Var2 Variant => (Var2)Var; 2095 public readonly StackTrace Variant0 => Var == Tag.SqlConnectionIsNotOpen ? Backtrace : throw new InvalidOperationException($"The TransactionError variant, {Var.ToString()}, is not SqlConnectionIsNotOpen!"); 2096 public readonly StackTrace Variant1 => Var == Tag.SqlConnectionServerMismatch ? Backtrace : throw new InvalidOperationException($"The TransactionError variant, {Var.ToString()}, is not SqlConnectionServerMismatch!"); 2097 #endregion 2098 2099 #region Type-level Functions 2100 #endregion 2101 2102 #region Instance Functions 2103 public override readonly bool Equals(object? _) => false; 2104 public override readonly int GetHashCode() => 0; 2105 public readonly TransactionError Into() => this; 2106 public readonly string IntoString() => ToString(); 2107 readonly string IInto<string>.Into() => IntoString(); 2108 public readonly Maybe<IError> Source() => Maybe<IError>.None(); 2109 public readonly Maybe<StackTrace> StackTrace() => new(Backtrace); 2110 public override readonly string ToString() => $"{Var.ToString()}({Backtrace.ToString()})"; 2111 readonly Result<TransactionError, Bottom> ITryInto<TransactionError, Bottom>.TryInto() => new(this); 2112 readonly Result<string, Bottom> ITryInto<string, Bottom>.TryInto() => new(ToString()); 2113 #endregion 2114 2115 #region Operators 2116 #endregion 2117 2118 #region Types 2119 public enum Tag: ulong { 2120 SqlConnectionIsNotOpen = ulong.MinValue, 2121 SqlConnectionServerMismatch = 1ul, 2122 } 2123 #endregion 2124 } 2125 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 0)] 2126 public readonly struct UNIQUE_CONSTRAINT: IClone<UNIQUE_CONSTRAINT>, IEq<UNIQUE_CONSTRAINT>, IHashable, IInto<UNIQUE_CONSTRAINT>, IInto<string>, IObject { 2127 2128 #region Type-level Constructors 2129 #endregion 2130 2131 #region Instance Constructors 2132 public UNIQUE_CONSTRAINT() => throw new InvalidOperationException("Parameterless constructor is not allowed to be called!"); 2133 internal UNIQUE_CONSTRAINT(Schema schema, string name, int objectID, int parentObjectID, int indexID) => (Schema, Name, ObjectID, ParentObjectID, UniqueIndexID) = (schema, name, objectID, parentObjectID, indexID); 2134 #endregion 2135 2136 #region Type-level Fields 2137 #endregion 2138 2139 #region Instance Fields 2140 public readonly Schema Schema; 2141 public readonly string Name; 2142 public readonly int ObjectID; 2143 public readonly int ParentObjectID; 2144 public readonly int UniqueIndexID; 2145 #endregion 2146 2147 #region Type-level Properties 2148 #endregion 2149 2150 #region Instance Properties 2151 readonly Schema IObject.Schema => Schema; 2152 readonly string IObject.Name => Name; 2153 readonly int IObject.ObjectID => ObjectID; 2154 #endregion 2155 2156 #region Type-level Functions 2157 #endregion 2158 2159 #region Instance Functions 2160 public readonly UNIQUE_CONSTRAINT Clone() => this; 2161 public override readonly bool Equals(object? _) => false; 2162 public override readonly int GetHashCode() => 0; 2163 public readonly Unit Hash<THasher>(ref THasher hasher) where THasher: notnull, IHasher { 2164 2165 _ = Schema.Hash(ref hasher); 2166 return hasher.WriteInt(ObjectID); 2167 } 2168 public readonly UNIQUE_CONSTRAINT Into() => this; 2169 public readonly string IntoString() => ToString(); 2170 readonly string IInto<string>.Into() => IntoString(); 2171 readonly void IObject.Sealed() { } 2172 public override readonly string ToString() => Name; 2173 readonly Result<UNIQUE_CONSTRAINT, Bottom> ITryInto<UNIQUE_CONSTRAINT, Bottom>.TryInto() => new(this); 2174 readonly Result<string, Bottom> ITryInto<string, Bottom>.TryInto() => new(ToString()); 2175 #endregion 2176 2177 #region Operators 2178 public static bool operator !=(UNIQUE_CONSTRAINT val0, UNIQUE_CONSTRAINT val1) => !(val0 == val1); 2179 public static bool operator ==(UNIQUE_CONSTRAINT val0, UNIQUE_CONSTRAINT val1) => val0.Schema == val1.Schema && val0.ObjectID == val1.ObjectID; 2180 #endregion 2181 2182 #region Types 2183 #endregion 2184 } 2185 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 0)] 2186 public readonly struct UserTable: IClone<UserTable>, IEq<UserTable>, IIndex<ushort, Column>, IInto<string>, IInto<UserTable>, IIntoIterator<Column, Std.Vec.IntoIterator<Column>>, IHashable, IObject { 2187 2188 #region Type-level Constructors 2189 #endregion 2190 2191 #region Instance Constructors 2192 public UserTable() => throw new InvalidOperationException("Parameterless constructor is not allowed to be called!"); 2193 internal UserTable(Schema schema, string name, Vec<Column> columns, Vec<Index> indexes, ushort childObjectCount, int objectID, Maybe<PRIMARY_KEY_CONSTRAINT> pk, Vec<UNIQUE_CONSTRAINT> uqs, Vec<FOREIGN_KEY_CONSTRAINT> fks, Vec<CHECK_CONSTRAINT> cs) => (Schema, Name, _columns, _indexes, ChildObjectCount, ObjectID, PRIMARY_KEY_CONSTRAINT, _uqs, _fks, _cs) = (schema, name, columns, indexes, childObjectCount, objectID, pk, uqs, fks, cs); 2194 #endregion 2195 2196 #region Type-level Fields 2197 static readonly Fn<Result<ulong, DELETEError>> _resDelErr0 = () => new(ulong.MinValue); 2198 static readonly Fn<Result<ulong, DELETETransactionError>> _resDelTxnErr0 = () => new(ulong.MinValue); 2199 static readonly Fn<Result<ulong, INSERTError>> _resInsErr0 = () => new(ulong.MinValue); 2200 static readonly Fn<Result<ulong, INSERTTransactionError>> _resInsTxnErr0 = () => new(ulong.MinValue); 2201 static readonly Fn<NonZeroUshort, int> _nzUshortToInt = (x) => x.IntoUshort(); 2202 static readonly Fn<ulong, bool> _countEq0 = (x) => x == ulong.MinValue; 2203 static readonly Fn<Index, Maybe<Index>> _maybeClusteredIndex = (ix) => ix.Type.Var is IndexType.Tag.Clustered or IndexType.Tag.ClusteredColumnstore ? new(ix) : Maybe<Index>.None(); 2204 #endregion 2205 2206 #region Instance Fields 2207 public readonly Schema Schema; 2208 readonly Vec<Column> _columns; 2209 readonly Vec<Index> _indexes; 2210 public readonly string Name; 2211 public readonly ushort ChildObjectCount; 2212 public readonly int ObjectID; 2213 public readonly Maybe<PRIMARY_KEY_CONSTRAINT> PRIMARY_KEY_CONSTRAINT; 2214 readonly Vec<UNIQUE_CONSTRAINT> _uqs; 2215 readonly Vec<FOREIGN_KEY_CONSTRAINT> _fks; 2216 readonly Vec<CHECK_CONSTRAINT> _cs; 2217 #endregion 2218 2219 #region Type-level Properties 2220 #endregion 2221 2222 #region Instance Properties 2223 readonly Schema IObject.Schema => Schema; 2224 readonly string IObject.Name => Name; 2225 readonly int IObject.ObjectID => ObjectID; 2226 public readonly ushort ColumnCount => (ushort)_columns.Len; 2227 public readonly ushort IndexCount => (ushort)_indexes.Len; 2228 public readonly ushort FOREIGN_KEY_CONSTRAINT_Count => (ushort)_fks.Len; 2229 public readonly ushort UNIQUE_CONSTRAINT_Count => (ushort)_uqs.Len; 2230 public readonly ushort CHECK_CONSTRAINT_Count => (ushort)_cs.Len; 2231 public readonly ref readonly Column this[ushort index] => ref _columns[index]; 2232 #endregion 2233 2234 #region Type-level Functions 2235 public static bool IsValidBulkLoadingTableRequireCLUSTEREDIndex(in UserTable bulkTable, in UserTable actual) { 2236 2237 if (bulkTable.ChildObjectCount == ushort.MinValue && bulkTable.Schema == actual.Schema && bulkTable.Schema.Name.Culture.CompareInfo.Compare(bulkTable.Name, actual.Name, bulkTable.Schema.Name.Options) != 0 && bulkTable.HasSameColumns(in actual)) { 2238 if (bulkTable.IndexCount != 1 || actual.IndexCount == ushort.MinValue) { return false; } 2239 ref readonly var ix = ref bulkTable._indexes[uint.MinValue]; 2240 ref readonly var ix2 = ref actual._indexes[uint.MinValue]; 2241 return (ix.Type.Var is IndexType.Tag.Clustered or IndexType.Tag.ClusteredColumnstore) && !ix.IsUnique && ix.Type == ix2.Type && ix.HasSameColumns(in ix2); 2242 } else { 2243 return false; 2244 } 2245 } 2246 public static bool IsValidBulkLoadingTableRequireCLUSTEREDIndexIgnoreIDENTITYProperty(in UserTable bulkTable, in UserTable actual) { 2247 2248 if (bulkTable.ChildObjectCount == ushort.MinValue && bulkTable.Schema == actual.Schema && bulkTable.Schema.Name.Culture.CompareInfo.Compare(bulkTable.Name, actual.Name, bulkTable.Schema.Name.Options) != 0 && bulkTable.HasSameColumnsIgnoreIDENTITYProperty(in actual)) { 2249 if (bulkTable.IndexCount != 1 || actual.IndexCount == ushort.MinValue) { return false; } 2250 ref readonly var ix = ref bulkTable._indexes[uint.MinValue]; 2251 ref readonly var ix2 = ref actual._indexes[uint.MinValue]; 2252 return (ix.Type.Var is IndexType.Tag.Clustered or IndexType.Tag.ClusteredColumnstore) && !ix.IsUnique && ix.Type == ix2.Type && ix.HasSameColumnsIgnoreIDENTITYProperty(in ix2); 2253 } else { 2254 return false; 2255 } 2256 } 2257 static bool IsValidBulkLoadingTableForbidCLUSTEREDIndexInternal(in UserTable bulkTable, in UserTable actual) => bulkTable.IndexCount == ushort.MinValue && bulkTable.ChildObjectCount == ushort.MinValue && bulkTable.Schema == actual.Schema && bulkTable.Schema.Name.Culture.CompareInfo.Compare(bulkTable.Name, actual.Name, bulkTable.Schema.Name.Options) != 0; 2258 public static bool IsValidBulkLoadingTableForbidCLUSTEREDIndex(in UserTable bulkTable, in UserTable actual) => IsValidBulkLoadingTableForbidCLUSTEREDIndexInternal(in bulkTable, in actual) && bulkTable.HasSameColumns(in actual); 2259 public static bool IsValidBulkLoadingTableForbidCLUSTEREDIndexIgnoreIDENTITYProperty(in UserTable bulkTable, in UserTable actual) => IsValidBulkLoadingTableForbidCLUSTEREDIndexInternal(in bulkTable, in actual) && bulkTable.HasSameColumnsIgnoreIDENTITYProperty(in actual); 2260 #endregion 2261 2262 #region Instance Functions 2263 // All properties and fields except _columns and _indexes are completely safe to return a copy of since they are all immutable as is UserTable. 2264 // Since _columns and _indexes are not exposed nor are they mutated internally, they too are safe to return a copy of. 2265 public readonly UserTable Clone() => this; 2266 public readonly bool ContainsClusteredIndex() => GetClusteredIndex().IsSome; 2267 public readonly bool ContainsColumnThatIsNotComputedOrFILESTREAMOrIDENTITY() { 2268 2269 for (var i = uint.MinValue; i < _columns.Len; i++) { 2270 ref readonly var col = ref _columns[i]; 2271 if (!(col.ComputedInfo.IsSome || col.IsFilestream || col.IdentityInfo.IsSome)) { return true; } 2272 } 2273 return false; 2274 } 2275 public readonly bool ContainsColumnThatIsNotComputedOrIDENTITY() { 2276 2277 for (var i = uint.MinValue; i < _columns.Len; i++) { 2278 ref readonly var col = ref _columns[i]; 2279 if (!(col.ComputedInfo.IsSome || col.IdentityInfo.IsSome)) { return true; } 2280 } 2281 return false; 2282 } 2283 public readonly bool ContainsComputedColumn() { 2284 for (var i = uint.MinValue; i < _columns.Len; i++) { if (_columns[i].ComputedInfo.IsSome) { return true; } } 2285 return false; 2286 } 2287 public readonly bool ContainsComputedOrFILESTREAMOrIDENTITYColumn() { 2288 2289 for (var i = uint.MinValue; i < _columns.Len; i++) { 2290 ref readonly var col = ref _columns[i]; 2291 if (col.ComputedInfo.IsSome || col.IsFilestream || col.IdentityInfo.IsSome) { return true; } 2292 } 2293 return false; 2294 } 2295 public readonly bool ContainsDeterministicEnclaveEncryptedColumn() { 2296 2297 Maybe<ColumnEncryptionInfo> maybeCe; 2298 ColumnEncryptionInfo ce; 2299 for (var i = uint.MinValue; i < _columns.Len; i++) { if ((maybeCe = _columns[i].EncryptionInfo).IsSome && (ce = maybeCe.Unwrap()).Type.Var == ColumnEncryptionType.Tag.DETERMINISTIC && ce.IsEnclaveEnabled) { return true; } } 2300 return false; 2301 } 2302 public readonly bool ContainsDeterministicEncryptedColumn() { 2303 2304 Maybe<ColumnEncryptionInfo> maybeCe; 2305 for (var i = uint.MinValue; i < _columns.Len; i++) { if ((maybeCe = _columns[i].EncryptionInfo).IsSome && maybeCe.Unwrap().Type.Var == ColumnEncryptionType.Tag.DETERMINISTIC) { return true; } } 2306 return false; 2307 } 2308 public readonly bool ContainsDeterministicNonEnclaveEncryptedColumn() { 2309 2310 Maybe<ColumnEncryptionInfo> maybeCe; 2311 ColumnEncryptionInfo ce; 2312 for (var i = uint.MinValue; i < _columns.Len; i++) { if ((maybeCe = _columns[i].EncryptionInfo).IsSome && (ce = maybeCe.Unwrap()).Type.Var == ColumnEncryptionType.Tag.DETERMINISTIC && !ce.IsEnclaveEnabled) { return true; } } 2313 return false; 2314 } 2315 public readonly bool ContainsEnclaveEncryptedColumn() { 2316 2317 Maybe<ColumnEncryptionInfo> maybeCe; 2318 for (var i = uint.MinValue; i < _columns.Len; i++) { if ((maybeCe = _columns[i].EncryptionInfo).IsSome && maybeCe.Unwrap().IsEnclaveEnabled) { return true; } } 2319 return false; 2320 } 2321 public readonly bool ContainsEncryptedColumn() { 2322 for (var i = uint.MinValue; i < _columns.Len; i++) { if (_columns[i].EncryptionInfo.IsSome) { return true; } } 2323 return false; 2324 } 2325 public readonly bool ContainsFILESTREAMColumn() { 2326 for (var i = uint.MinValue; i < _columns.Len; i++) { if (_columns[i].IsFilestream) { return true; } } 2327 return false; 2328 } 2329 public readonly bool ContainsIDENTITYColumn() { 2330 for (var i = uint.MinValue; i < _columns.Len; i++) { if (_columns[i].IdentityInfo.IsSome) { return true; } } 2331 return false; 2332 } 2333 public readonly bool ContainsNonEnclaveEncryptedColumn() { 2334 2335 Maybe<ColumnEncryptionInfo> maybeCe; 2336 for (var i = uint.MinValue; i < _columns.Len; i++) { if ((maybeCe = _columns[i].EncryptionInfo).IsSome && !maybeCe.Unwrap().IsEnclaveEnabled) { return true; } } 2337 return false; 2338 } 2339 public readonly bool ContainsNullableColumns() { 2340 for (var i = uint.MinValue; i < _columns.Len; i++) { if (_columns[i].IsNullable) { return true; } } 2341 return false; 2342 } 2343 public readonly bool ContainsRandomizedEnclaveEncryptedColumn() { 2344 2345 Maybe<ColumnEncryptionInfo> maybeCe; 2346 ColumnEncryptionInfo ce; 2347 for (var i = uint.MinValue; i < _columns.Len; i++) { if ((maybeCe = _columns[i].EncryptionInfo).IsSome && (ce = maybeCe.Unwrap()).Type.Var == ColumnEncryptionType.Tag.RANDOMIZED && ce.IsEnclaveEnabled) { return true; } } 2348 return false; 2349 } 2350 public readonly bool ContainsRandomizedEncryptedColumn() { 2351 2352 Maybe<ColumnEncryptionInfo> maybeCe; 2353 for (var i = uint.MinValue; i < _columns.Len; i++) { if ((maybeCe = _columns[i].EncryptionInfo).IsSome && maybeCe.Unwrap().Type.Var == ColumnEncryptionType.Tag.RANDOMIZED) { return true; } } 2354 return false; 2355 } 2356 public readonly bool ContainsRandomizedNonEnclaveEncryptedColumn() { 2357 2358 Maybe<ColumnEncryptionInfo> maybeCe; 2359 ColumnEncryptionInfo ce; 2360 for (var i = uint.MinValue; i < _columns.Len; i++) { if ((maybeCe = _columns[i].EncryptionInfo).IsSome && (ce = maybeCe.Unwrap()).Type.Var == ColumnEncryptionType.Tag.RANDOMIZED && !ce.IsEnclaveEnabled) { return true; } } 2361 return false; 2362 } 2363 public readonly Result<ulong, DELETEError> DELETE_FROM_EXISTS(in UserTable existingRows, Maybe<NonZeroUshort> timeout, SessionOptions options, bool ignoreIdentity, Maybe<Uri> attestation) { 2364 2365 using var con = Functions.CreateOpenedConnection(in Schema.Database, options, false, ContainsRandomizedEnclaveEncryptedColumn() ? attestation : Maybe<Uri>.None()); 2366 using var txn = con.BeginTransaction(IsolationLevel.Serializable); 2367 return DELETE_FROM_EXISTS(in existingRows, timeout, txn, ignoreIdentity).MapErr((e) => { txn.Rollback(); return e.Var switch { 2368 DELETETransactionError.Tag.DatabaseIsReadOnly => new DELETEError(DELETEError.Tag.DatabaseIsReadOnly, e.Backtrace), 2369 DELETETransactionError.Tag.SchemasMismatch => new DELETEError(DELETEError.Tag.SchemasMismatch, e.Backtrace), 2370 DELETETransactionError.Tag.TablesAreEqual => new DELETEError(DELETEError.Tag.TablesAreEqual, e.Backtrace), 2371 DELETETransactionError.Tag.ContainsRandomizedNonEnclaveEncryptedColumn => new DELETEError(DELETEError.Tag.ContainsRandomizedNonEnclaveEncryptedColumn, e.Backtrace), 2372 DELETETransactionError.Tag.ColumnsMismatch => new DELETEError(DELETEError.Tag.ColumnsMismatch, e.Backtrace), 2373 _ => throw new InvalidOperationException($"The DELETETransactionError variant, {e.Var.ToString()}, is unexpected."), 2374 }; }).Map((count) => { txn.Commit(); return count; }); 2375 } 2376 [System.Diagnostics.CodeAnalysis.SuppressMessage("Security", "CA2100:Review SQL queries for security vulnerabilities", Justification = "The text is generated internally.")] 2377 public readonly Result<ulong, DELETETransactionError> DELETE_FROM_EXISTS(in UserTable existingRows, Maybe<NonZeroUshort> timeout, SqlTransaction txn, bool ignoreIdentity) { 2378 2379 if (Schema.Database.IsReadOnly) { 2380 return new(new DELETETransactionError(DELETETransactionError.Tag.DatabaseIsReadOnly, new StackTrace(1, true))); 2381 } else if (txn.Connection.State != ConnectionState.Open) { 2382 return new(new DELETETransactionError(DELETETransactionError.Tag.SqlConnectionIsNotOpen, new StackTrace(1, true))); 2383 } else if (!string.Equals(txn.Connection.DataSource, $"tcp:{Schema.Database.Server.IntoString()}", StringComparison.Ordinal)) { 2384 return new(new DELETETransactionError(DELETETransactionError.Tag.SqlConnectionServerMismatch, new StackTrace(1, true))); 2385 } else if (Schema != existingRows.Schema) { 2386 return new(new DELETETransactionError(DELETETransactionError.Tag.SchemasMismatch, new StackTrace(1, true))); 2387 } else if (ObjectID == existingRows.ObjectID) { 2388 return new(new DELETETransactionError(DELETETransactionError.Tag.TablesAreEqual, new StackTrace(1, true))); 2389 } else if (ContainsRandomizedNonEnclaveEncryptedColumn()) { 2390 return new(new DELETETransactionError(DELETETransactionError.Tag.ContainsRandomizedNonEnclaveEncryptedColumn, new StackTrace(1, true))); 2391 } else if (_columns.Ne(in existingRows._columns)) { 2392 return new(new DELETETransactionError(DELETETransactionError.Tag.ColumnsMismatch, new StackTrace(1, true))); 2393 } else { 2394 var containsEnclaveEncrypted = ContainsRandomizedEnclaveEncryptedColumn(); 2395 if (containsEnclaveEncrypted && !txn.Connection.ConnectionString.Contains("Attestation Protocol=HGS;Enclave Attestation Url=", StringComparison.Ordinal)) { return new(new DELETETransactionError(DELETETransactionError.Tag.ContainsRandomizedEnclaveEncryptedColumnButWasNotPassedAttesationURL, new StackTrace(1, true))); } 2396 var ignoreIdentCopy = ignoreIdentity; 2397 var tableCopy = this; 2398 return GenDeleteFromExists(existingRows.Name, ignoreIdentCopy).MapOrElse( 2399 _resDelTxnErr0, 2400 (qryText) => { 2401 using SqlCommand qry2 = new(qryText.ToString(), txn.Connection, txn, containsEnclaveEncrypted ? SqlCommandColumnEncryptionSetting.Enabled : SqlCommandColumnEncryptionSetting.Disabled) { CommandTimeout = timeout.MapOr(0, _nzUshortToInt), CommandType = CommandType.Text, EnableOptimizedParameterBinding = true }; 2402 return new((ulong)(long)qry2.ExecuteScalar()); 2403 } 2404 ); 2405 } 2406 } 2407 // MUST ensure the table does not have a RANDOMIZED non-enclave encrypted column before calling! 2408 // Returns None if there is only one column which has the IDENTITY property and we are told to ignore IDENTITY columns. 2409 // Otherwise returns the query: 2410 // DELETE s0 2411 // FROM [<Schema.Database.Value.Value>].[<Schema.Value.Value>].[<Value>] AS s0 2412 // WHERE EXISTS ( 2413 // SELECT * 2414 // FROM [<Schema.Database.Value.Value>].[<Schema.Value.Value>].[<existsName>] AS s1 2415 // WHERE ((s0.[<col0>] IS NULL AND s1.[<col0>] IS NULL) OR s0.[<col0>] = s1.[<col0>]) AND ((s0.[<col1>] IS NULL AND s1.[<col1>] IS NULL) OR s0.[<col1>] = s1.[<col1>]) AND ... AND ((s0.[<coln>] IS NULL AND s1.[<coln>] IS NULL) OR s0.[<coln>] = s1.[<coln>]) 2416 // ); 2417 // SELECT ROWCOUNT_BIG() AS fi64Count; 2418 // where <coli> is the ith column and n is either the total number of columns or one less than the total number of columns 2419 // when there is no ignored IDENITY column or when there is an ignored IDENTITY column respectively. 2420 readonly Maybe<StringBuilder> DeleteQuery(string existsName, bool ignoreIdent) { 2421 // Nothing to delete since the lone column is ignored. 2422 if (_columns.Len == 1 && ignoreIdent && ContainsIDENTITYColumn()) { return Maybe<StringBuilder>.None(); } 2423 StringBuilder query = new($@"DELETE s0 2424 FROM [{Schema.Database.Name.Value}].[{Schema.Name.Value}].[{Name}] AS s0 2425 WHERE EXISTS ( 2426 SELECT * 2427 FROM [{Schema.Database.Name.Value}].[{Schema.Name.Value}].[{existsName}] AS s1 2428 WHERE ", 4096); 2429 2430 for (var i = uint.MinValue; i < ColumnCount; i++) { 2431 ref readonly var col = ref _columns[i]; 2432 // Only include the column in the EXISTS clause if it is either not an IDENTITY column or we were told to not ignore it. 2433 // Use simpler predicate if the column is not nullable. 2434 if (!(col.IdentityInfo.IsSome && ignoreIdent)) { _ = col.IsNullable ? query.Append($"((s0.[{col.Name}] IS NULL AND s1.[{col.Name}] IS NULL) OR s0.[{col.Name}] = s1.[{col.Name}]) AND ") : query.Append($"s0.[{col.Name}] = s1.[{col.Name}] AND "); } 2435 } 2436 // Remove the trailing " AND " before appending the closing ")" of the EXISTS predicate as well as the query to grab the number 2437 // of rows affected. 2438 return new(query.Remove(query.Length - 5, 5).Append($@"); 2439 SELECT ROWCOUNT_BIG() AS fi64Count;")); 2440 } 2441 public override readonly bool Equals(object? _) => false; 2442 // MUST ensure that there does not exist a RANDOMIZED non-enclave encrypted column before calling! 2443 // TODO: Provide a way for downstream code to dictate if an equivalent TRUNCATE query should be used. 2444 // Since downstream code will frequently know or at least have confidence in their knowledge about the 2445 // proportion of rows that will be DELETEd relative to the total number of rows, it would be nice to generate 2446 // more efficient queries based on that knowledge. In particular, if a majority of rows will be DELETEd, 2447 // then it would be much more efficient if we instead INSERTed the rows we won't DELETE into a temp table. 2448 // After which, we can TRUNCATE the table before INSERTing the rows from the temp table back in. 2449 // One has to take care to avoid issues related to encrypted columns though as we cannot assume 2450 // tempdb will have access to the encryption keys (since Always Encrypted keys are database-scoped). 2451 // In the event there are encrypted columns, then one has to instead create varbinary(max) columns 2452 // in the place of the encrypted columns; after which, code needs to read the data, without decrypting it, 2453 // into an SqlBulkCopy which will then INSERT the "raw" encrypted data into the temp table. 2454 // After which, one can TRUNCATE the table before doing the reverse (i.e., reading the encrypted data from the temp table 2455 // into an SqlBulkCopy which will INSERT the "raw" encrypted data back into the table). 2456 // We do not want to perform this heuristic ourself though as TRUNCATEs have slightly different semantics 2457 // than DELETEs, and we do not want to "surprise" downstream code. Also, it would slow down code since 2458 // we would have to first run COUNT_BIG(*) queries. 2459 readonly Maybe<StringBuilder> GenDeleteFromExists(string existsName, bool ignoreIdentity) => DeleteQuery(existsName, ignoreIdentity); 2460 // If there is an IDENTITY column that we don't insert and 0 non-computed columns, then we return None. 2461 // If there are 0 computed columns or IDENTITY columns, then the query looks like: 2462 // 2463 // INSERT INTO [<Schema.Database.Value.Value>].[<Schema.Value.Value>].[<destName>] 2464 // SELECT * 2465 // FROM [<Schema.Database.Value.Value>].[<Schema.Value.Value>].[<Value>]; 2466 // 2467 // SELECT ROWCOUNT_BIG() AS fi64Count; 2468 // 2469 // If there are 0 computed columns but an IDENTITY column which we do insert, then the query looks like: 2470 // 2471 // INSERT INTO [<Schema.Database.Value.Value>].[<Schema.Value.Value>].[<destName>]([<col0>], [<col1>], ..., [<coln>]) 2472 // SELECT * 2473 // FROM [<Schema.Database.Value.Value>].[<Schema.Value.Value>].[<Value>]; 2474 // 2475 // SELECT ROWCOUNT_BIG() AS fi64Count; 2476 // 2477 // If there is at least 1 computed column and also an IDENTITY column which we do insert, then the query looks like: 2478 // 2479 // INSERT INTO [<Schema.Database.Value.Value>].[<Schema.Value.Value>].[<destName>]([<col0>], [<col1>], ..., [<coln>]) 2480 // SELECT [<col0>], [<col1>], ..., [<coln>] 2481 // FROM [<Schema.Database.Value.Value>].[<Schema.Value.Value>].[<Value>]; 2482 // 2483 // SELECT ROWCOUNT_BIG() AS fi64Count; 2484 // Otherwise the query looks like: 2485 // 2486 // INSERT INTO [<Schema.Database.Value.Value>].[<Schema.Value.Value>].[<destName>] 2487 // SELECT [<col0>], [<col1>], ..., [<coln>] 2488 // FROM [<Schema.Database.Value.Value>].[<Schema.Value.Value>].[<Value>]; 2489 // 2490 // SELECT ROWCOUNT_BIG() AS fi64Count; 2491 // where <coli> is the name of ith column in the current instance which is not a computed or IDENTITY column we are not supposed to insert. 2492 readonly Maybe<StringBuilder> GenInsertInto(string destName, bool containsIdentity, bool identityInsert) { 2493 2494 if (ContainsComputedColumn()) { 2495 2496 if (containsIdentity) { 2497 2498 if (identityInsert) { 2499 // Table contains a computed column and we also must insert IDENTITY values, so we must have a column list 2500 // and the SELECT must contain all non-computed columns. 2501 StringBuilder cols = new($"INSERT INTO [{Schema.Database.Name.Value}].[{Schema.Name.Value}].[{destName}](", 128 + (32 * ColumnCount)); 2502 StringBuilder qrySELECT = new(@") 2503 SELECT ", 16 * ColumnCount); 2504 string colName; 2505 2506 for (var i = uint.MinValue; i < ColumnCount; i++) { 2507 ref readonly var col = ref _columns[i]; 2508 // Since we are inserting IDENTITY values, we must include that column; thus we only skip computed columns. 2509 if (col.ComputedInfo.IsNone) { 2510 colName = $"[{col.Name}], "; 2511 _ = cols.Append(colName); 2512 _ = qrySELECT.Append(colName); 2513 } 2514 } 2515 return new(cols.Remove(cols.Length - 2, 2).Append(qrySELECT.Remove(qrySELECT.Length - 2, 2).Append($@" 2516 FROM [{Schema.Database.Name.Value}].[{Schema.Name.Value}].[{Name}]; 2517 2518 SELECT ROWCOUNT_BIG() AS fi64Count;"))); 2519 } else if (!ContainsColumnThatIsNotComputedOrIDENTITY()) { 2520 // We "skip" all columns since there are only computed columns and IDENTITY columns, 2521 // and we were told to not insert IDENTITY values. 2522 return Maybe<StringBuilder>.None(); 2523 } 2524 } 2525 } else if (containsIdentity) { 2526 2527 if (identityInsert) { 2528 // We don't have any computed columns, but we are inserting IDENTITY values. 2529 // We must use a column list but our SELECT can be a simple SELECT * (since no columns need to be skipped). 2530 StringBuilder cols = new($"INSERT INTO [{Schema.Database.Name.Value}].[{Schema.Name.Value}].[{destName}](", 128 + (16 * ColumnCount)); 2531 2532 for (var i = uint.MinValue; i < ColumnCount; i++) { 2533 ref readonly var col = ref _columns[i]; 2534 // We don't have any computed columns to worry about and we are inserting the IDENTITY column, 2535 // so we must grab all column names. 2536 _ = cols.Append($"[{col.Name}], "); 2537 } 2538 return new(cols.Remove(cols.Length - 2, 2).Append($@") 2539 SELECT * 2540 FROM [{Schema.Database.Name.Value}].[{Schema.Name.Value}].[{Name}]; 2541 2542 SELECT ROWCOUNT_BIG() AS fi64Count;")); 2543 } else if (_columns.Len == 1u) { 2544 // We "skip" all columns since there is only one column which also is an IDENTITY column which 2545 // we were told not to insert. 2546 return Maybe<StringBuilder>.None(); 2547 } 2548 } else { 2549 // Table doesn't contain any computed or IDENTITY columns, so we can avoid the column list and use SELECT *. 2550 return new(new($@"INSERT INTO [{Schema.Database.Name.Value}].[{Schema.Name.Value}].[{destName}] 2551 SELECT * 2552 FROM [{Schema.Database.Name.Value}].[{Schema.Name.Value}].[{Name}]; 2553 2554 SELECT ROWCOUNT_BIG() AS fi64Count;")); 2555 } 2556 // We are not inserting IDENTITY values, so we don't need a column list. 2557 // We do have at least one computed column or IDENTITY column though, thus we must only SELECT those columns 2558 // that are neither. 2559 StringBuilder qry = new($@"INSERT INTO [{Schema.Database.Name.Value}].[{Schema.Name.Value}].[{destName}] 2560 SELECT ", 128 + (16 * ColumnCount)); 2561 2562 for (var i = uint.MinValue; i < ColumnCount; i++) { 2563 ref readonly var col = ref _columns[i]; 2564 // We must not grab computed or IDENTITY columns. 2565 if (!(col.ComputedInfo.IsSome || col.IdentityInfo.IsSome)) { _ = qry.Append($"[{col.Name}], "); } 2566 } 2567 return new(qry.Remove(qry.Length - 2, 2).Append($@" 2568 FROM [{Schema.Database.Name.Value}].[{Schema.Name.Value}].[{Name}]; 2569 2570 SELECT ROWCOUNT_BIG() AS fi64Count;")); 2571 } 2572 // This function MUST only be called AFTER the UserTable corresponding to destName has been shown to have at least one unique index we need to worry about. 2573 // The query looks like: 2574 // 2575 // INSERT INTO [<Schema.Database.Value.Value>].[<Schema.Value.Value>].[<destName>] 2576 // SELECT f0, f1, ..., fn 2577 // FROM ( 2578 // SELECT [<col0>] AS f0, [<col1>] AS f1, ..., [<coln>] AS fn, COUNT_BIG(*) OVER(PARTITION BY [<ux0>]) AS c0, COUNT_BIG(*) OVER(PARTITION BY [<ux1>]) AS c1, ..., COUNT_BIG(*) OVER(PARTITION BY [<uxm>]) AS cm 2579 // FROM [<Schema.Database.Value.Value>].[<Schema.Value.Value>].[<Value>] 2580 // ) AS s0 2581 // WHERE s0.c0 = 1 AND s0.c1 = 1 AND ... AND s0.cm = 1 AND NOT EXISTS ( 2582 // SELECT * 2583 // FROM [<Schema.Database.Value.Value>].[<Schema.Value.Value>].[<destName>] AS s1 2584 // WHERE ((<s0.ux0> IS NULL AND <s1.[ux0]> IS NULL) OR <s0.ux0> = <s1.[ux0]>) OR ((<s0.[ux1]> IS NULL AND <s1.[ux1]> IS NULL) OR <s0.[ux1]> = <s1.[ux1]>) OR ... OR ((<s0.[uxm]> IS NULL AND <s1.[uxm]> IS NULL) OR <s0.[uxm]> = <s1.[uxm]>) 2585 // ); 2586 // 2587 // SELECT ROWCOUNT_BIG() AS fi64Count; 2588 // where <coli> is the name of ith column in the current instance, 2589 // <uxi> is the comma-delimited sequence of index key columns in the ith unique index of the destination table, and 2590 // <s0.uxi> = <s1.uxi> is of the form s0.<uj> = s1.<ucolj> AND s0.<uk> = s1.<ucolk> AND ... AND s0.<um> = s1.<ucolm> 2591 // and <s0.uxi> IS NULL AND <s1.uxi> IS NULL is of the form s0.<uj> IS NULL AND s1.<ucolj> IS NULL AND s0.<uk> IS NULL AND s1.<ucolk> IS NULL AND ... AND s0.<um> IS NULL s1.<ucolm> IS NULL 2592 // where <up> is the alias name in s0 for the pth index key of the ith unique index and <ucolp> is the name of the pth index key of the ith unique index. 2593 // In the event that a column exists that is computed or has the IDENTITY property which are to not insert, it is skipped and not included in the SELECT. 2594 // Finally, the minimum number of unique indexes necessary to ensure a unique violation will not occur is used. 2595 // The top-most subquery is used to restrict the rows in the current instance to those that don't violate any 2596 // unique index in the destination table. 2597 // The bottom subquery is of course to ensure there will not be a unique violation when inserting the restricted set of 2598 // rows from the top subquery into the destination table. 2599 // unique MUST be created via GetMinUniqueCoveringIndexes. 2600 readonly StringBuilder GenInsertUnique(string destName, Vec<Index> unique, bool identityINSERT) { 2601 2602 StringBuilder insertINTO = new($@"INSERT INTO [{Schema.Database.Name.Value}].[{Schema.Name.Value}].[{destName}]", 8192); 2603 StringBuilder colList = ContainsIDENTITYColumn() && identityINSERT ? new("(", ColumnCount * 16) : new(0); 2604 StringBuilder topSELECT = new(@" 2605 SELECT ", 2048); 2606 StringBuilder midSELECT = new(@" 2607 FROM ( 2608 SELECT ", 4096); 2609 string colAlias; 2610 2611 for (var i = uint.MinValue; i < ColumnCount; i++) { 2612 ref readonly var col = ref _columns[i]; 2613 colAlias = $"f{col.ColumnID.ToString()}, "; 2614 // We grab all columns no matter what; furthermore, since we will create additional columns 2615 // which are named c0, c1, ..., cn, we must avoid the possibility that one of the existing columns is named 2616 // one of those. To achieve that, we alias all the columns f0, f1, ..., fn. 2617 _ = midSELECT.Append($"[{col.Name}] AS {colAlias}"); 2618 // Only need to add the column name or column alias if the column is not a computed column. 2619 if (col.ComputedInfo.IsNone) { 2620 // If colList has a postitive length, then we know we must create a column list (since we are inserting an IDENTITY value). 2621 // We also know we must include the IDENTITY column alias in the SELECT. 2622 if (colList.Length > 0) { 2623 _ = colList.Append($"[{col.Name}], "); 2624 _ = topSELECT.Append(colAlias); 2625 } else if (col.IdentityInfo.IsNone) { 2626 // We are not inserting IDENTITY values, so we must ensure the column is not an IDENTITY column. 2627 _ = topSELECT.Append(colAlias); 2628 } 2629 } 2630 } 2631 // At this point topSELECT looks like "SELECT f0, f1, ..., fn, " (we will later remove the trailing ", "). 2632 // midSELECT looks like "<newline>FROM (<newline> SELECT [<col0>] AS f0, [<col1>] AS f1, ..., [<coln>] AS fn, " 2633 // We still need to add the PARTITION BY clauses to midSELECT; thus we keep the trailing ", ". 2634 Vec<Prod<Column, Maybe<ColumnSort>>> ixCols; 2635 Prod<Column, Maybe<ColumnSort>> ixCol; 2636 StringBuilder topQueryWHERE = new($@" 2637 FROM [{Schema.Database.Name.Value}].[{Schema.Name.Value}].[{Name}] 2638 ) AS s0 2639 WHERE ", 1024); 2640 StringBuilder bottomSubquery = new($@"SELECT * 2641 FROM [{Schema.Database.Name.Value}].[{Schema.Name.Value}].[{destName}] AS s1 2642 WHERE ", 2048); 2643 string ixColNum; 2644 2645 for (var i = uint.MinValue; i < unique.Len; i++) { 2646 _ = midSELECT.Append("COUNT_BIG(*) OVER(PARTITION BY "); 2647 // At this point midSELECT looks like the following (it is all one line but broken up into multiple here): 2648 // "<newline>FROM (<newline> SELECT [<col0>] AS f0, [<col1>] AS f1, ..., [<coln>] AS fn, 2649 // COUNT_BIG(*) OVER(PARTITION BY [<ix0_col0>], [<ix0_col1>], ..., [<ix0_cola>]) AS c0, 2650 // COUNT_BIG(*) OVER(PARTITION BY [<ix1_col0>], [<ix1_col1>], ..., [<ix1_colb>]) AS c1, ... 2651 // COUNT_BIG(*) OVER(PARTITION BY [<ix{j-1}_col0>], [<ix{j-1}_col1>], ..., [<ix{j-1}_colp>]) AS c{j-1}, 2652 // COUNT_BIG(*) OVER(PARTITION BY " 2653 colAlias = $"c{i.ToString()}"; 2654 _ = topQueryWHERE.Append($"s0.{colAlias} = 1 AND "); 2655 // At this point topQueryWHERE looks like the following: 2656 // "<newline>FROM [<Schema.Database.Value.Value>].[<Schema.Value.Value>].[<Value>]<newline>) as s0<newline>WHERE s0.c0 = 1 AND s0.c1 = 1 AND ... AND s0.cj = 1 AND " 2657 ixCols = unique[i]._columns; 2658 _ = bottomSubquery.Append('('); 2659 // At this point bottomSubquery looks like the following (it is all one line but broken up into multiple here): 2660 // "SELECT *<newline>FROM [<Schema.Database.Value.Value>].[<Schema.Value.Value>].[<destName>] AS s1<newline>WHERE (( 2661 // ((s0.<ix0_col0_alias> IS NULL AND s1.[<ix0_col0>] IS NULL) OR s0.<ix0_col0_alias> = s1.[<ix0_col0>]) AND 2662 // ((s0.<ix0_col1_alias> IS NULL AND s1.[<ix0_col1>] IS NULL) OR s0.<ix0_col1_alias> = s1.[<ix0_col1>]) AND ... 2663 // ((s0.<ix0_cola_alias> IS NULL AND s1.[<ix0_cola>] IS NULL) OR s0.<ix0_cola_alias> = s1.[<ix0_cola>])) OR ( 2664 // ((s0.<ix1_col0_alias> IS NULL AND s1.[<ix1_col0>] IS NULL) OR s0.<ix1_col0_alias> = s1.[<ix1_col0>]) AND 2665 // ((s0.<ix1_col1_alias> IS NULL AND s1.[<ix1_col1>] IS NULL) OR s0.<ix1_col1_alias> = s1.[<ix1_col1>]) AND ... 2666 // ((s0.<ix1_colb_alias> IS NULL AND s1.[<ix1_colb>] IS NULL) OR s0.<ix1_colb_alias> = s1.[<ix1_colb>])) OR ... ( 2667 // ((s0.<ix{j-1}_col0_alias> IS NULL AND s1.[<ix{j-1}_col0>] IS NULL) OR s0.<ix{j-1}_col0_alias> = s1.[<ix{j-1}_col0>]) AND 2668 // ((s0.<ix{j-1}_col1_alias> IS NULL AND s1.[<ix{j-1}_col1>] IS NULL) OR s0.<ix{j-1}_col1_alias> = s1.[<ix{j-1}_col1>]) AND ... 2669 // ((s0.<ix{j-1}_colp_alias> IS NULL AND s1.[<ix{j-1}_colp>] IS NULL) OR s0.<ix{j-1}_colp_alias> = s1.[<ix{j-1}_colp>])) OR (" 2670 for (var j = uint.MinValue; j < ixCols.Len; j++) { 2671 ixCol = ixCols[j]; 2672 // If the ColumnSort is None, then there are no more key columns, so we can move on to the next Index. 2673 // Note that since key columns are always first, we are guaranteed to hit the else block 2674 // at least one time before this (if we ever hit this). 2675 if (ixCol.Item1.IsNone) { 2676 break; 2677 } else { 2678 _ = midSELECT.Append($"[{ixCol.Item0.Name}], "); 2679 // At this point midSELECT looks like the following (it is all one line but broken up into multiple here): 2680 // "<newline>FROM (<newline> SELECT [<col0>] AS f0, [<col1>] AS f1, ..., [<coln>] AS fn, 2681 // COUNT_BIG(*) OVER(PARTITION BY [<ix0_col0>], [<ix0_col1>], ..., [<ix0_cola>]) AS c0, 2682 // COUNT_BIG(*) OVER(PARTITION BY [<ix1_col0>], [<ix1_col1>], ..., [<ix1_colb>]) AS c1, ... 2683 // COUNT_BIG(*) OVER(PARTITION BY [<ixj_col0>], [<ixj_col1>], ..., [<ixj_colq>], " 2684 ixColNum = ixCol.Item0.ColumnID.ToString(); 2685 // We must treat NULLs as equal to other NULLs and not equal to non-NULLs since we are dealing with Index keys. 2686 _ = ixCol.Item0.IsNullable ? bottomSubquery.Append($"((s0.f{ixColNum} IS NULL AND s1.[{ixCol.Item0.Name}] IS NULL) OR s0.f{ixColNum} = s1.[{ixCol.Item0.Name}]) AND ") : bottomSubquery.Append($"s0.f{ixColNum} = s1.[{ixCol.Item0.Name}] AND "); 2687 // At this point bottomSubquery looks like the following (it is all one line but broken up into multiple here): 2688 // "SELECT *<newline>FROM [<Schema.Database.Value.Value>].[<Schema.Value.Value>].[<destName>] AS s1<newline>WHERE (( 2689 // ((s0.<ix0_col0_alias> IS NULL AND s1.[<ix0_col0>] IS NULL) OR s0.<ix0_col0_alias> = s1.[<ix0_col0>]) AND 2690 // ((s0.<ix0_col1_alias> IS NULL AND s1.[<ix0_col1>] IS NULL) OR s0.<ix0_col1_alias> = s1.[<ix0_col1>]) AND ... 2691 // ((s0.<ix0_cola_alias> IS NULL AND s1.[<ix0_cola>] IS NULL) OR s0.<ix0_cola_alias> = s1.[<ix0_cola>])) OR ( 2692 // ((s0.<ix1_col0_alias> IS NULL AND s1.[<ix1_col0>] IS NULL) OR s0.<ix1_col0_alias> = s1.[<ix1_col0>]) AND 2693 // ((s0.<ix1_col1_alias> IS NULL AND s1.[<ix1_col1>] IS NULL) OR s0.<ix1_col1_alias> = s1.[<ix1_col1>]) AND ... 2694 // ((s0.<ix1_colb_alias> IS NULL AND s1.[<ix1_colb>] IS NULL) OR s0.<ix1_colb_alias> = s1.[<ix1_colb>])) OR ... ( 2695 // ((s0.<ixj_col0_alias> IS NULL AND s1.[<ixj_col0>] IS NULL) OR s0.<ixj_col0_alias> = s1.[<ixj_col0>]) AND 2696 // ((s0.<ixj_col1_alias> IS NULL AND s1.[<ixj_col1>] IS NULL) OR s0.<ixj_col1_alias> = s1.[<ixj_col1>]) AND ... 2697 // ((s0.<ixj_colq_alias> IS NULL AND s1.[<ixj_colq>] IS NULL) OR s0.<ixj_colq_alias> = s1.[<ixj_colq>]) AND " 2698 } 2699 } 2700 // We must remove the comma and space as well as add ") AS c<index_number>, ". 2701 // This is safe since we know there will be at least one unique index. 2702 _ = midSELECT.Remove(midSELECT.Length - 2, 2).Append($") AS {colAlias}, "); 2703 // At this point midSELECT looks like the following (it is all one line but broken up into multiple here): 2704 // "<newline>FROM (<newline> SELECT [<col0>] AS f0, [<col1>] AS f1, ..., [<coln>] AS fn, 2705 // COUNT_BIG(*) OVER(PARTITION BY [<ix0_col0>], [<ix0_col1>], ..., [<ix0_cola>]) AS c0, 2706 // COUNT_BIG(*) OVER(PARTITION BY [<ix1_col0>], [<ix1_col1>], ..., [<ix1_colb>]) AS c1, ... 2707 // COUNT_BIG(*) OVER(PARTITION BY [<ixj_col0>], [<ixj_col1>], ..., [<ixj_colq>], ..., [<ixj_colr>]) AS cj, " 2708 2709 // We must remove " AND " and add ") OR ". 2710 // This is safe since we know there will be at least one unique index. 2711 _ = bottomSubquery.Remove(bottomSubquery.Length - 5, 5).Append(") OR "); 2712 // At this point bottomSubquery looks like the following (it is all one line but broken up into multiple here): 2713 // "SELECT *<newline>FROM [<Schema.Database.Value.Value>].[<Schema.Value.Value>].[<destName>] AS s1<newline>WHERE (( 2714 // ((s0.<ix0_col0_alias> IS NULL AND s1.[<ix0_col0>] IS NULL) OR s0.<ix0_col0_alias> = s1.[<ix0_col0>]) AND 2715 // ((s0.<ix0_col1_alias> IS NULL AND s1.[<ix0_col1>] IS NULL) OR s0.<ix0_col1_alias> = s1.[<ix0_col1>]) AND ... 2716 // ((s0.<ix0_cola_alias> IS NULL AND s1.[<ix0_cola>] IS NULL) OR s0.<ix0_cola_alias> = s1.[<ix0_cola>])) OR ( 2717 // ((s0.<ix1_col0_alias> IS NULL AND s1.[<ix1_col0>] IS NULL) OR s0.<ix1_col0_alias> = s1.[<ix1_col0>]) AND 2718 // ((s0.<ix1_col1_alias> IS NULL AND s1.[<ix1_col1>] IS NULL) OR s0.<ix1_col1_alias> = s1.[<ix1_col1>]) AND ... 2719 // ((s0.<ix1_colb_alias> IS NULL AND s1.[<ix1_colb>] IS NULL) OR s0.<ix1_colb_alias> = s1.[<ix1_colb>])) OR ... ( 2720 // ((s0.<ixj_col0_alias> IS NULL AND s1.[<ixj_col0>] IS NULL) OR s0.<ixj_col0_alias> = s1.[<ixj_col0>]) AND 2721 // ((s0.<ixj_col1_alias> IS NULL AND s1.[<ixj_col1>] IS NULL) OR s0.<ixj_col1_alias> = s1.[<ixj_col1>]) AND ... 2722 // ((s0.<ixj_colq_alias> IS NULL AND s1.[<ixj_colq>] IS NULL) OR s0.<ixj_colq_alias> = s1.[<ixj_colq>]) AND ... 2723 // ((s0.<ixj_colr_alias> IS NULL AND s1.[<ixj_colr>] IS NULL) OR s0.<ixj_colr_alias> = s1.[<ixj_colr>])) OR " 2724 } 2725 // insertINTO looks like "INSERT INTO [<Schema.Database.Value.Value>].[<Schema.Value.Value>].[<destName>]" 2726 2727 // colList looks like either "([<col0>], [<col1>], ..., [<colt>], " or "". 2728 // In the former case, we must remove the trailing ", " and append ")". 2729 2730 // topSELECT looks like "SELECT f0, f1, ..., fn, ", so we must remove the trailing ", ". 2731 2732 // midSELECT looks like the following (it is all one line but broken up into multiple here): 2733 // "<newline>FROM (<newline> SELECT [<col0>] AS f0, [<col1>] AS f1, ..., [<coln>] AS fn, 2734 // COUNT_BIG(*) OVER(PARTITION BY [<ix0_col0>], [<ix0_col1>], ..., [<ix0_cola>]) AS c0, 2735 // COUNT_BIG(*) OVER(PARTITION BY [<ix1_col0>], [<ix1_col1>], ..., [<ix1_colb>]) AS c1, ... 2736 // COUNT_BIG(*) OVER(PARTITION BY [<ixk_col0>], [<ixk_col1>], ..., [<ixk_cols>]) AS ck, " 2737 // Thus we must remove the trailing ", ". 2738 2739 // topQueryWHERE looks like the following: 2740 // "<newline>FROM [<Schema.Database.Value.Value>].[<Schema.Value.Value>].[<Value>]<newline>) AS s0<newline>WHERE s0.c0 = 1 AND s0.c1 = 1 AND ... AND s0.ck = 1 AND " 2741 // Thus we must append "NOT EXISTS (". 2742 2743 // bottomSubquery looks like the following (it is all one line but broken up into multiple here): 2744 // "SELECT *<newline>FROM [<Schema.Database.Value.Value>].[<Schema.Value.Value>].[<destName>] AS s1<newline>WHERE (( 2745 // ((s0.<ix0_col0_alias> IS NULL AND s1.[<ix0_col0>] IS NULL) OR s0.<ix0_col0_alias> = s1.[<ix0_col0>]) AND 2746 // ((s0.<ix0_col1_alias> IS NULL AND s1.[<ix0_col1>] IS NULL) OR s0.<ix0_col1_alias> = s1.[<ix0_col1>]) AND ... 2747 // ((s0.<ix0_cola_alias> IS NULL AND s1.[<ix0_cola>] IS NULL) OR s0.<ix0_cola_alias> = s1.[<ix0_cola>])) OR ( 2748 // ((s0.<ix1_col0_alias> IS NULL AND s1.[<ix1_col0>] IS NULL) OR s0.<ix1_col0_alias> = s1.[<ix1_col0>]) AND 2749 // ((s0.<ix1_col1_alias> IS NULL AND s1.[<ix1_col1>] IS NULL) OR s0.<ix1_col1_alias> = s1.[<ix1_col1>]) AND ... 2750 // ((s0.<ix1_colb_alias> IS NULL AND s1.[<ix1_colb>] IS NULL) OR s0.<ix1_colb_alias> = s1.[<ix1_colb>])) OR ... ( 2751 // ((s0.<ixk_col0_alias> IS NULL AND s1.[<ixk_col0>] IS NULL) OR s0.<ixk_col0_alias> = s1.[<ixk_col0>]) AND 2752 // ((s0.<ixk_col1_alias> IS NULL AND s1.[<ixk_col1>] IS NULL) OR s0.<ixk_col1_alias> = s1.[<ixk_col1>]) AND ... 2753 // ((s0.<ixk_cols_alias> IS NULL AND s1.[<ixk_cols>] IS NULL) OR s0.<ixk_cols_alias> = s1.[<ixk_cols>])) OR " 2754 // Thus we must remove " OR " and append "<newline>);<newline><newline>SELECT ROWCOUNT_BIG() AS fi64Count;". 2755 2756 // We finally return <insertINTO><colList><topSELECT><midSELECT><topQueryWHERE><bottomSubquery>. 2757 return insertINTO.Append(colList.Length > 0 ? colList.Remove(colList.Length - 2, 2).Append(')') : colList).Append(topSELECT.Remove(topSELECT.Length - 2, 2).Append(midSELECT.Remove(midSELECT.Length - 2, 2)).Append(topQueryWHERE.Append("NOT EXISTS (")).Append(bottomSubquery.Remove(bottomSubquery.Length - 4, 4).Append(@" 2758 ); 2759 2760 SELECT ROWCOUNT_BIG() AS fi64Count;"))); 2761 } 2762 public readonly Maybe<Index> GetClusteredIndex() => _indexes.Get(uint.MinValue).AndThen(_maybeClusteredIndex); 2763 public readonly ref readonly Index GetClusteredIndexUnsafe() { 2764 2765 ref readonly var ix = ref _indexes[uint.MinValue]; 2766 if (ix.Type.Var is IndexType.Tag.Clustered or IndexType.Tag.ClusteredColumnstore) { 2767 return ref ix; 2768 } else { 2769 throw new InvalidOperationException($"A CLUSTERED INDEX does not exist on {ToString()}."); 2770 } 2771 } 2772 public readonly Maybe<Column> GetColumn(int columnID) { 2773 2774 for (var i = uint.MinValue; i < _columns.Len; i++) { 2775 ref readonly var col = ref _columns[i]; 2776 if (col.ColumnID == columnID) { return new(col); } 2777 } 2778 return Maybe<Column>.None(); 2779 } 2780 public ref readonly Column GetColumnUnsafe(int columnID) { 2781 2782 for (var i = uint.MinValue; i < _columns.Len; i++) { 2783 ref readonly var col = ref _columns[i]; 2784 if (col.ColumnID == columnID) { return ref col; } 2785 } 2786 throw new InvalidOperationException($"ColumnID, {columnID.ToString()}, does not exist in {ToString()}."); 2787 } 2788 public readonly Maybe<Column> GetColumn(ushort ordinalPosition) => _columns.Get(ordinalPosition); 2789 public readonly Maybe<Column> GetColumn(ReadOnlySpan<char> columnName) { 2790 2791 for (var i = uint.MinValue; i < _columns.Len; i++) { 2792 ref readonly var col = ref _columns[i]; 2793 if (MemoryExtensions.Equals(col.Name.AsSpan(), columnName, StringComparison.Ordinal)) { return new(col); } 2794 } 2795 return Maybe<Column>.None(); 2796 } 2797 public ref readonly Column GetColumnUnsafe(ReadOnlySpan<char> columnName) { 2798 2799 for (var i = uint.MinValue; i < _columns.Len; i++) { 2800 ref readonly var col = ref _columns[i]; 2801 if (MemoryExtensions.Equals(col.Name.AsSpan(), columnName, StringComparison.Ordinal)) { return ref col; } 2802 } 2803 throw new InvalidOperationException($"{columnName.ToString()} does not exist in {ToString()}."); 2804 } 2805 public override readonly int GetHashCode() => 0; 2806 public readonly Maybe<Index> GetIndex(int indexID) { 2807 2808 for (var i = uint.MinValue; i < _indexes.Len; i++) { 2809 ref readonly var ix = ref _indexes[i]; 2810 if (ix.IndexID == indexID) { return new(ix); } 2811 } 2812 return Maybe<Index>.None(); 2813 } 2814 public readonly ref readonly Index GetIndexUnsafe(int indexID) { 2815 2816 for (var i = uint.MinValue; i < _indexes.Len; i++) { 2817 ref readonly var ix = ref _indexes[i]; 2818 if (ix.IndexID == indexID) { return ref ix; } 2819 } 2820 throw new InvalidOperationException($"Index ID, {indexID.ToString()}, does not exist in {ToString()}."); 2821 } 2822 public readonly Maybe<CHECK_CONSTRAINT> GetCHECK_CONSTRAINT(int objectID) { 2823 2824 for (var i = uint.MinValue; i < _cs.Len; i++) { 2825 ref readonly var c = ref _cs[i]; 2826 if (c.ObjectID == objectID) { return new(c); } 2827 } 2828 return Maybe<CHECK_CONSTRAINT>.None(); 2829 } 2830 public ref readonly CHECK_CONSTRAINT GetCHECK_CONSTRAINTUnsafe(int objectID) { 2831 2832 for (var i = uint.MinValue; i < _cs.Len; i++) { 2833 ref readonly var c = ref _cs[i]; 2834 if (c.ObjectID == objectID) { return ref c; } 2835 } 2836 throw new InvalidOperationException($"CHECK CONSTRAINT object_id, {objectID.ToString()}, does not exist in {ToString()}."); 2837 } 2838 public readonly Maybe<UNIQUE_CONSTRAINT> GetUNIQUE_CONSTRAINT(int objectID) { 2839 2840 for (var i = uint.MinValue; i < _uqs.Len; i++) { 2841 ref readonly var uq = ref _uqs[i]; 2842 if (uq.ObjectID == objectID) { return new(uq); } 2843 } 2844 return Maybe<UNIQUE_CONSTRAINT>.None(); 2845 } 2846 public ref readonly UNIQUE_CONSTRAINT GetUNIQUE_CONSTRAINTUnsafe(int objectID) { 2847 2848 for (var i = uint.MinValue; i < _uqs.Len; i++) { 2849 ref readonly var uq = ref _uqs[i]; 2850 if (uq.ObjectID == objectID) { return ref uq; } 2851 } 2852 throw new InvalidOperationException($"UNIQUE CONSTRAINT object_id, {objectID.ToString()}, does not exist in {ToString()}."); 2853 } 2854 public readonly Maybe<FOREIGN_KEY_CONSTRAINT> GetFOREIGN_KEY_CONSTRAINT(int objectID) { 2855 2856 for (var i = uint.MinValue; i < _fks.Len; i++) { 2857 ref readonly var fk = ref _fks[i]; 2858 if (fk.ObjectID == objectID) { return new(fk); } 2859 } 2860 return Maybe<FOREIGN_KEY_CONSTRAINT>.None(); 2861 } 2862 public ref readonly FOREIGN_KEY_CONSTRAINT GetFOREIGN_KEY_CONSTRAINTUnsafe(int objectID) { 2863 2864 for (var i = uint.MinValue; i < _fks.Len; i++) { 2865 ref readonly var fk = ref _fks[i]; 2866 if (fk.ObjectID == objectID) { return ref fk; } 2867 } 2868 throw new InvalidOperationException($"FOREIGN KEY CONSTRAINT object_id, {objectID.ToString()}, does not exist in {ToString()}."); 2869 } 2870 public readonly Maybe<Index> GetIndex(ReadOnlySpan<char> indexName) { 2871 2872 for (var i = uint.MinValue; i < _indexes.Len; i++) { 2873 ref readonly var ix = ref _indexes[i]; 2874 if (MemoryExtensions.Equals(ix.Name.AsSpan(), indexName, StringComparison.Ordinal)) { return new(ix); } 2875 } 2876 return Maybe<Index>.None(); 2877 } 2878 public ref readonly Index GetIndexUnsafe(ReadOnlySpan<char> indexName) { 2879 2880 for (var i = uint.MinValue; i < _indexes.Len; i++) { 2881 ref readonly var ix = ref _indexes[i]; 2882 if (MemoryExtensions.Equals(ix.Name.AsSpan(), indexName, StringComparison.Ordinal)) { return ref ix; } 2883 } 2884 throw new InvalidOperationException($"Index, {indexName.ToString()}, does not exist in {ToString()}."); 2885 } 2886 public readonly Maybe<CHECK_CONSTRAINT> GetCHECK_CONSTRAINT(ReadOnlySpan<char> constraintName) { 2887 2888 for (var i = uint.MinValue; i < _cs.Len; i++) { 2889 ref readonly var c = ref _cs[i]; 2890 if (MemoryExtensions.Equals(c.Name.AsSpan(), constraintName, StringComparison.Ordinal)) { return new(c); } 2891 } 2892 return Maybe<CHECK_CONSTRAINT>.None(); 2893 } 2894 public ref readonly CHECK_CONSTRAINT GetCHECK_CONSTRAINTUnsafe(ReadOnlySpan<char> constraintName) { 2895 2896 for (var i = uint.MinValue; i < _cs.Len; i++) { 2897 ref readonly var c = ref _cs[i]; 2898 if (MemoryExtensions.Equals(c.Name.AsSpan(), constraintName, StringComparison.Ordinal)) { return ref c; } 2899 } 2900 throw new InvalidOperationException($"CHECK CONSTRAINT, {constraintName.ToString()}, does not exist in {ToString()}."); 2901 } 2902 public readonly Maybe<UNIQUE_CONSTRAINT> GetUNIQUE_CONSTRAINT(ReadOnlySpan<char> constraintName) { 2903 2904 for (var i = uint.MinValue; i < _uqs.Len; i++) { 2905 ref readonly var uq = ref _uqs[i]; 2906 if (MemoryExtensions.Equals(uq.Name.AsSpan(), constraintName, StringComparison.Ordinal)) { return new(uq); } 2907 } 2908 return Maybe<UNIQUE_CONSTRAINT>.None(); 2909 } 2910 public ref readonly UNIQUE_CONSTRAINT GetUNIQUE_CONSTRAINTUnsafe(ReadOnlySpan<char> constraintName) { 2911 2912 for (var i = uint.MinValue; i < _uqs.Len; i++) { 2913 ref readonly var uq = ref _uqs[i]; 2914 if (MemoryExtensions.Equals(uq.Name.AsSpan(), constraintName, StringComparison.Ordinal)) { return ref uq; } 2915 } 2916 throw new InvalidOperationException($"UNIQUE CONSTRAINT, {constraintName.ToString()}, does not exist in {ToString()}."); 2917 } 2918 public readonly Maybe<FOREIGN_KEY_CONSTRAINT> GetFOREIGN_KEY_CONSTRAINT(ReadOnlySpan<char> constraintName) { 2919 2920 for (var i = uint.MinValue; i < _fks.Len; i++) { 2921 ref readonly var fk = ref _fks[i]; 2922 if (MemoryExtensions.Equals(fk.Name.AsSpan(), constraintName, StringComparison.Ordinal)) { return new(fk); } 2923 } 2924 return Maybe<FOREIGN_KEY_CONSTRAINT>.None(); 2925 } 2926 public ref readonly FOREIGN_KEY_CONSTRAINT GetFOREIGN_KEY_CONSTRAINTUnsafe(ReadOnlySpan<char> constraintName) { 2927 2928 for (var i = uint.MinValue; i < _fks.Len; i++) { 2929 ref readonly var fk = ref _fks[i]; 2930 if (MemoryExtensions.Equals(fk.Name.AsSpan(), constraintName, StringComparison.Ordinal)) { return ref fk; } 2931 } 2932 throw new InvalidOperationException($"FOREIGN KEY CONSTRAINT, {constraintName.ToString()}, does not exist in {ToString()}."); 2933 } 2934 public readonly Std.Vec.IntoIterator<CHECK_CONSTRAINT> GetCHECK_CONSTRAINTS() => _cs.IntoIter(); 2935 public readonly Std.Vec.IntoIterator<UNIQUE_CONSTRAINT> GetUNIQUE_CONSTRAINTS() => _uqs.IntoIter(); 2936 public readonly Std.Vec.IntoIterator<FOREIGN_KEY_CONSTRAINT> GetFOREIGN_KEY_CONSTRAINTS() => _fks.IntoIter(); 2937 public readonly Std.Vec.IntoIterator<Index> GetIndexes() => _indexes.IntoIter(); 2938 // Efficiently scans unique Indexes and only retains the minimum number 2939 // of Indexes necessary to avoid unique violations. This is done in a single pass through of the Indexes. 2940 // Note that this means only the Index key columns are relevant and that the order of the columns is not. 2941 // Furthermore it is trivial to see that the retained Indexes will precisely be all Indexes that are not a superset 2942 // of any other Index where superset is defined purely on the Index key columns. 2943 // Note that any filters are ignored! 2944 readonly Vec<Index> GetMinUniqueCoveringIndexes(bool ignoreIDENTITYIndexes) { 2945 2946 var ixes = Vec<Index>.WithCapacity(_indexes.Len); 2947 var i = uint.MinValue; 2948 uint j; 2949 2950 outer: while (i < _indexes.Len) { 2951 // Notice that we always increment i. 2952 ref readonly var next = ref _indexes[i++]; 2953 // If the Index is not unique or contains an identity key column and ignoreIDENTITYIndexes is true, then the Index will not be a problem. 2954 if (!next.IsUnique || (ignoreIDENTITYIndexes && next.ContainsIdentityKey())) { continue; } 2955 j = uint.MinValue; 2956 2957 while (j < ixes.Len) { 2958 ref readonly var cur = ref ixes[j]; 2959 // If the candidate Index, next, is a superset of another Index; then we can immediately discard it. 2960 if (cur.IsSubset(in next)) { 2961 goto outer; 2962 // If the candidate Index is a proper subset of a currently maintained one, 2963 // then we know we must remove the currently maintained one from ixes. 2964 // We pop off the last Index in ixes and replace the current one with it before 2965 // continuing our subset checks in place (i.e., we will begin our check with the aforementioned 2966 // popped Index since we already checked the previous ones). 2967 } else if (next.IsSubset(in cur)) { 2968 // We must be careful of the possibility that cur is the last Index in ixes. 2969 // If so, we only pop it off. 2970 _ = ixes.Pop().MapOr(new Unit(), (ix) => { if (j == ixes.Len) { return new Unit(); } else { ixes.ItemMut(j) = ix; return new Unit(); } }); 2971 continue; 2972 } 2973 j++; 2974 } 2975 // If we have reached this far, then we know the candidate Index, next, 2976 // is not a superset of an existing Index in ixes; thus we add it to ixes. 2977 // Note that as the above code shows, we will later remove it iff we later encounter an Index 2978 // that is a proper subset of it. 2979 _ = ixes.Push(next); 2980 } 2981 return ixes; 2982 } 2983 public readonly bool HasSameColumns(in UserTable other) => _columns.Eq(in other._columns); 2984 public readonly bool HasSameColumnsIgnoreComputedColumnDefinitionIfNone(in UserTable other) { 2985 2986 if (_columns.Len != other._columns.Len) { return false; } 2987 for (var i = uint.MinValue; i < _columns.Len; i++) { if (!_columns[i].EqIgnoreComputedColumnDefinitionIfNone(in other._columns[i])) { return false; } } 2988 return true; 2989 } 2990 public readonly bool HasSameColumnsIgnoreEncryptionInfo(in UserTable other) { 2991 2992 if (_columns.Len != other._columns.Len) { return false; } 2993 for (var i = uint.MinValue; i < _columns.Len; i++) { if (!_columns[i].EqIgnoreEncryptionInfo(in other._columns[i])) { return false; } } 2994 return true; 2995 } 2996 public readonly bool HasSameColumnsIgnoreIDENTITYProperty(in UserTable other) { 2997 2998 if (_columns.Len != other._columns.Len) { return false; } 2999 for (var i = uint.MinValue; i < _columns.Len; i++) { if (!_columns[i].EqIgnoreIDENTITYProperty(in other._columns[i])) { return false; } } 3000 return true; 3001 } 3002 public readonly bool HasSamePRIMARY_KEY_CONSTRAINT(in UserTable other) => (PRIMARY_KEY_CONSTRAINT.IsSome && other.PRIMARY_KEY_CONSTRAINT.IsSome && GetIndexUnsafe(PRIMARY_KEY_CONSTRAINT.Unwrap().UniqueIndexID) == other.GetIndexUnsafe(other.PRIMARY_KEY_CONSTRAINT.Unwrap().UniqueIndexID)) || (PRIMARY_KEY_CONSTRAINT.IsNone && other.PRIMARY_KEY_CONSTRAINT.IsNone); 3003 public readonly bool HasSameUNIQUE_CONSTRAINTs(in UserTable other) { 3004 3005 if (_uqs.Len == other._uqs.Len) { 3006 var eq = false; 3007 for (var i = uint.MinValue; i < _uqs.Len; i++) { 3008 ref readonly var ix = ref GetIndexUnsafe(_uqs[i].UniqueIndexID); 3009 for (var j = uint.MinValue; j < other._uqs.Len; j++) { 3010 eq = ix == other.GetIndexUnsafe(other._uqs[j].UniqueIndexID); 3011 if (eq) { break; } 3012 } 3013 if (!eq) { return false; } 3014 } 3015 return true; 3016 } else { 3017 return false; 3018 } 3019 } 3020 public readonly bool HasSameCHECK_CONSTRAINTs(in UserTable other) { 3021 3022 if (_cs.Len == other._cs.Len) { 3023 for (var i = uint.MinValue; i < _cs.Len; i++) { 3024 ref readonly var c = ref _cs[i]; 3025 ref readonly var c2 = ref other._cs[i]; 3026 if (!(string.Equals(c.Name, c2.Name, StringComparison.Ordinal) && c.IsDisabled == c2.IsDisabled && c.IsNotTrusted == c2.IsNotTrusted && string.Equals(c._definition, c2._definition, StringComparison.Ordinal) && c.UsesDatabaseCollation == c2.UsesDatabaseCollation && ((c.ParentColumnID == 0 && c2.ParentColumnID == 0) || (c.ParentColumnID != 0 && c2.ParentColumnID != 0 && GetColumnUnsafe(c.ParentColumnID) == other.GetColumnUnsafe(c2.ParentColumnID))))) { return false; } 3027 } 3028 return true; 3029 } else { 3030 return false; 3031 } 3032 } 3033 public readonly bool HasSameCHECK_CONSTRAINTsIgnoreDefinitionIfNone(in UserTable other) { 3034 3035 if (_cs.Len == other._cs.Len) { 3036 for (var i = uint.MinValue; i < _cs.Len; i++) { 3037 ref readonly var c = ref _cs[i]; 3038 ref readonly var c2 = ref other._cs[i]; 3039 if (!(string.Equals(c.Name, c2.Name, StringComparison.Ordinal) && c.IsDisabled == c2.IsDisabled && c.IsNotTrusted == c2.IsNotTrusted && (string.Equals(c._definition, c2._definition, StringComparison.Ordinal) || c._definition is null || c2._definition is null) && c.UsesDatabaseCollation == c2.UsesDatabaseCollation && ((c.ParentColumnID == 0 && c2.ParentColumnID == 0) || (c.ParentColumnID != 0 && c2.ParentColumnID != 0 && GetColumnUnsafe(c.ParentColumnID) == other.GetColumnUnsafe(c2.ParentColumnID))))) { return false; } 3040 } 3041 return true; 3042 } else { 3043 return false; 3044 } 3045 } 3046 public readonly bool HasSameIndexes(in UserTable other) { 3047 3048 if (_indexes.Len == other._indexes.Len) { 3049 var eq = false; 3050 for (var i = uint.MinValue; i < _indexes.Len; i++) { 3051 for (var j = uint.MinValue; j < other._indexes.Len; j++) { 3052 eq = _indexes[i] == other._indexes[j]; 3053 if (eq) { break; } 3054 } 3055 if (!eq) { return false; } 3056 } 3057 return true; 3058 } else { 3059 return false; 3060 } 3061 } 3062 // Since creation of UserTable is restricted internally thus requiring one to use an existing Database instance, we simply need to hash Schema and Value. 3063 public readonly Unit Hash<THasher>(ref THasher hasher) where THasher: notnull, IHasher { 3064 3065 _ = Schema.Hash(ref hasher); 3066 return hasher.WriteInt(ObjectID); 3067 } 3068 public readonly Result<ulong, INSERTError> INSERT_INTO(in UserTable dest, Maybe<NonZeroUshort> timeout, SessionOptions options, bool identityINSERT) { 3069 3070 using var con = Functions.CreateOpenedConnection(in dest.Schema.Database, options, false, Maybe<Uri>.None()); 3071 using var txn = con.BeginTransaction(IsolationLevel.Serializable); 3072 return INSERT_INTO(in dest, timeout, txn, identityINSERT).MapErr((e) => { 3073 txn.Rollback(); return e.Var switch { 3074 INSERTTransactionError.Tag.DatabaseIsReadOnly => new INSERTError(INSERTError.Tag.DatabaseIsReadOnly, e.Backtrace), 3075 INSERTTransactionError.Tag.SchemasMismatch => new INSERTError(INSERTError.Tag.SchemasMismatch, e.Backtrace), 3076 INSERTTransactionError.Tag.TablesAreEqual => new INSERTError(INSERTError.Tag.TablesAreEqual, e.Backtrace), 3077 INSERTTransactionError.Tag.ColumnsMismatch => new INSERTError(INSERTError.Tag.ColumnsMismatch, e.Backtrace), 3078 _ => throw new InvalidOperationException($"The INSERTTransactionError variant, {e.Var.ToString()}, is unexpected."), 3079 }; 3080 }).Map((count) => { txn.Commit(); return count; }); 3081 } 3082 [System.Diagnostics.CodeAnalysis.SuppressMessage("Security", "CA2100:Review SQL queries for security vulnerabilities", Justification = "The text is generated internally.")] 3083 public readonly Result<ulong, INSERTTransactionError> INSERT_INTO(in UserTable dest, Maybe<NonZeroUshort> timeout, SqlTransaction txn, bool identityINSERT) { 3084 3085 if (Schema.Database.IsReadOnly) { 3086 return new(new INSERTTransactionError(INSERTTransactionError.Tag.DatabaseIsReadOnly, new StackTrace(1, true))); 3087 } else if (txn.Connection.State != ConnectionState.Open) { 3088 return new(new INSERTTransactionError(INSERTTransactionError.Tag.SqlConnectionIsNotOpen, new StackTrace(1, true))); 3089 } else if (!string.Equals(txn.Connection.DataSource, $"tcp:{Schema.Database.Server.IntoString()}", StringComparison.Ordinal)) { 3090 return new(new INSERTTransactionError(INSERTTransactionError.Tag.SqlConnectionServerMismatch, new StackTrace(1, true))); 3091 } else if (Schema != dest.Schema) { 3092 return new(new INSERTTransactionError(INSERTTransactionError.Tag.SchemasMismatch, new StackTrace(1, true))); 3093 } else if (ObjectID == dest.ObjectID) { 3094 return new(new INSERTTransactionError(INSERTTransactionError.Tag.TablesAreEqual, new StackTrace(1, true))); 3095 } else if (_columns.Ne(in dest._columns)) { 3096 return new(new INSERTTransactionError(INSERTTransactionError.Tag.ColumnsMismatch, new StackTrace(1, true))); 3097 } else { 3098 var identInsertCopy = identityINSERT; 3099 var containsIdent = ContainsIDENTITYColumn(); 3100 var destCopy = dest; 3101 return GenInsertInto(dest.Name, containsIdent, identInsertCopy).MapOrElse( 3102 _resInsTxnErr0, 3103 (qryText) => { 3104 if (identityINSERT && containsIdent) { 3105 using (SqlCommand qry = new($"SET IDENTITY_INSERT [{destCopy.Schema.Database.Name.Value}].[{destCopy.Schema.Name.Value}].[{destCopy.Name}] ON;", txn.Connection, txn, SqlCommandColumnEncryptionSetting.Disabled) { CommandTimeout = timeout.MapOr(0, _nzUshortToInt), CommandType = CommandType.Text, EnableOptimizedParameterBinding = true }) { 3106 _ = qry.ExecuteNonQuery(); 3107 } 3108 ulong count; 3109 using (SqlCommand qry2 = new(qryText.ToString(), txn.Connection, txn, SqlCommandColumnEncryptionSetting.Disabled) { CommandTimeout = timeout.MapOr(0, _nzUshortToInt), CommandType = CommandType.Text, EnableOptimizedParameterBinding = true }) { 3110 count = (ulong)(long)qry2.ExecuteScalar(); 3111 } 3112 using SqlCommand qry3 = new($"SET IDENTITY_INSERT [{destCopy.Schema.Database.Name.Value}].[{destCopy.Schema.Name.Value}].[{destCopy.Name}] OFF;", txn.Connection, txn, SqlCommandColumnEncryptionSetting.Disabled) { CommandTimeout = timeout.MapOr(0, _nzUshortToInt), CommandType = CommandType.Text, EnableOptimizedParameterBinding = true }; 3113 _ = qry3.ExecuteNonQuery(); 3114 return new(count); 3115 } else { 3116 using SqlCommand qry2 = new(qryText.ToString(), txn.Connection, txn, SqlCommandColumnEncryptionSetting.Disabled) { CommandTimeout = timeout.MapOr(0, _nzUshortToInt), CommandType = CommandType.Text, EnableOptimizedParameterBinding = true }; 3117 return new((ulong)(long)qry2.ExecuteScalar()); 3118 } 3119 } 3120 ); 3121 } 3122 } 3123 public readonly Result<ulong, INSERTError> INSERT_INTO_AVOID_UNIQUE_VIOLATIONS(in UserTable dest, Maybe<NonZeroUshort> timeout, SessionOptions options, bool identityINSERT) { 3124 3125 using var con = Functions.CreateOpenedConnection(in dest.Schema.Database, options, false, Maybe<Uri>.None()); 3126 using var txn = con.BeginTransaction(IsolationLevel.Serializable); 3127 return INSERT_INTO_AVOID_UNIQUE_VIOLATIONS(in dest, timeout, txn, identityINSERT).MapErr((e) => { 3128 txn.Rollback(); return e.Var switch { 3129 INSERTTransactionError.Tag.DatabaseIsReadOnly => new INSERTError(INSERTError.Tag.DatabaseIsReadOnly, e.Backtrace), 3130 INSERTTransactionError.Tag.SchemasMismatch => new INSERTError(INSERTError.Tag.SchemasMismatch, e.Backtrace), 3131 INSERTTransactionError.Tag.TablesAreEqual => new INSERTError(INSERTError.Tag.TablesAreEqual, e.Backtrace), 3132 INSERTTransactionError.Tag.ColumnsMismatch => new INSERTError(INSERTError.Tag.ColumnsMismatch, e.Backtrace), 3133 _ => throw new InvalidOperationException($"The INSERTTransactionError variant, {e.Var.ToString()}, is unexpected."), 3134 }; 3135 }).Map((count) => { txn.Commit(); return count; }); 3136 } 3137 // Note that any filters that may exist on a UNIQUE INDEX are ignored! 3138 // This means there will be times rows could have been INSERTed that weren't as the rows were not subject to the filter 3139 // and thus not subject to uniqueness. 3140 [System.Diagnostics.CodeAnalysis.SuppressMessage("Security", "CA2100:Review SQL queries for security vulnerabilities", Justification = "The text is generated internally.")] 3141 public readonly Result<ulong, INSERTTransactionError> INSERT_INTO_AVOID_UNIQUE_VIOLATIONS(in UserTable dest, Maybe<NonZeroUshort> timeout, SqlTransaction txn, bool identityINSERT) { 3142 3143 var ixes = dest.GetMinUniqueCoveringIndexes(!identityINSERT); 3144 // We don't have to worry about violations due to unique indexes or constraints. 3145 if (ixes.Len == uint.MinValue) { 3146 return INSERT_INTO(dest, timeout, txn, identityINSERT); 3147 } else if (Schema.Database.IsReadOnly) { 3148 return new(new INSERTTransactionError(INSERTTransactionError.Tag.DatabaseIsReadOnly, new StackTrace(1, true))); 3149 } else if (txn.Connection.State != ConnectionState.Open) { 3150 return new(new INSERTTransactionError(INSERTTransactionError.Tag.SqlConnectionIsNotOpen, new StackTrace(1, true))); 3151 } else if (!string.Equals(txn.Connection.DataSource, $"tcp:{Schema.Database.Server.IntoString()}", StringComparison.Ordinal)) { 3152 return new(new INSERTTransactionError(INSERTTransactionError.Tag.SqlConnectionServerMismatch, new StackTrace(1, true))); 3153 } else if (Schema != dest.Schema) { 3154 return new(new INSERTTransactionError(INSERTTransactionError.Tag.SchemasMismatch, new StackTrace(1, true))); 3155 } else if (ObjectID == dest.ObjectID) { 3156 return new(new INSERTTransactionError(INSERTTransactionError.Tag.TablesAreEqual, new StackTrace(1, true))); 3157 } else if (_columns.Ne(in dest._columns)) { 3158 return new(new INSERTTransactionError(INSERTTransactionError.Tag.ColumnsMismatch, new StackTrace(1, true))); 3159 } else if (ContainsColumnThatIsNotComputedOrIDENTITY() || identityINSERT) { 3160 // We know there is at least one column that we cannot "skip", so we are guaranteed to generate a query. 3161 if (identityINSERT && ContainsIDENTITYColumn()) { 3162 3163 using (SqlCommand qry = new($"SET IDENTITY_INSERT [{Schema.Database.Name.Value}].[{Schema.Name.Value}].[{dest.Name}] ON;", txn.Connection, txn, SqlCommandColumnEncryptionSetting.Disabled) { CommandTimeout = timeout.MapOr(0, _nzUshortToInt), CommandType = CommandType.Text, EnableOptimizedParameterBinding = true }) { 3164 _ = qry.ExecuteNonQuery(); 3165 } 3166 ulong count; 3167 3168 using (SqlCommand qry2 = new(GenInsertUnique(dest.Name, ixes, identityINSERT).ToString(), txn.Connection, txn, SqlCommandColumnEncryptionSetting.Disabled) { CommandTimeout = timeout.MapOr(0, _nzUshortToInt), CommandType = CommandType.Text, EnableOptimizedParameterBinding = true }) { 3169 count = (ulong)(long)qry2.ExecuteScalar(); 3170 } 3171 using SqlCommand qry3 = new($"SET IDENTITY_INSERT [{Schema.Database.Name.Value}].[{Schema.Name.Value}].[{dest.Name}] OFF;", txn.Connection, txn, SqlCommandColumnEncryptionSetting.Disabled) { CommandTimeout = timeout.MapOr(0, _nzUshortToInt), CommandType = CommandType.Text, EnableOptimizedParameterBinding = true }; 3172 _ = qry3.ExecuteNonQuery(); 3173 return new(count); 3174 } else { 3175 using SqlCommand qry2 = new(GenInsertUnique(dest.Name, ixes, identityINSERT).ToString(), txn.Connection, txn, SqlCommandColumnEncryptionSetting.Disabled) { CommandTimeout = timeout.MapOr(0, _nzUshortToInt), CommandType = CommandType.Text, EnableOptimizedParameterBinding = true }; 3176 return new((ulong)(long)qry2.ExecuteScalar()); 3177 } 3178 } else { 3179 // Even though the table has at least one unique index, we are dealing with a table 3180 // that only contains an IDENTITY column and 0 or more computed columns and 0 non-computed columns; 3181 // furthermore we were instructed to not insert values into the IDENTITY column. This means we "skip" all columns. 3182 return new(ulong.MinValue); 3183 } 3184 } 3185 public readonly UserTable Into() => this; 3186 public readonly string IntoString() => ToString(); 3187 readonly string IInto<string>.Into() => IntoString(); 3188 public readonly Std.Vec.IntoIterator<Column> IntoIter() => _columns.IntoIter(); 3189 public readonly bool IsEmpty(Maybe<NonZeroUshort> timeout, SessionOptions options) => SELECT_COUNT_BIG(timeout, options) == ulong.MinValue; 3190 public readonly Result<bool, TransactionError> IsEmpty(Maybe<NonZeroUshort> timeout, SqlTransaction txn) => SELECT_COUNT_BIG(timeout, txn).Map(_countEq0); 3191 readonly void IObject.Sealed(){} 3192 public readonly ulong SELECT_COUNT_BIG(Maybe<NonZeroUshort> timeout, SessionOptions options) { 3193 3194 using var con = Functions.CreateOpenedConnection(in Schema.Database, options, false, Maybe<Uri>.None()); 3195 using var txn = con.BeginTransaction(IsolationLevel.Serializable); 3196 var count = SELECT_COUNT_BIG(timeout, txn).Unwrap(); 3197 txn.Commit(); 3198 return count; 3199 } 3200 [System.Diagnostics.CodeAnalysis.SuppressMessage("Security", "CA2100:Review SQL queries for security vulnerabilities", Justification = "The text is generated internally.")] 3201 public readonly Result<ulong, TransactionError> SELECT_COUNT_BIG(Maybe<NonZeroUshort> timeout, SqlTransaction txn) { 3202 3203 if (txn.Connection.State != ConnectionState.Open) { 3204 return new(new TransactionError(TransactionError.Tag.SqlConnectionIsNotOpen, new StackTrace(1, true))); 3205 } else if (!string.Equals(txn.Connection.DataSource, $"tcp:{Schema.Database.Server.IntoString()}", StringComparison.Ordinal)) { 3206 return new(new TransactionError(TransactionError.Tag.SqlConnectionServerMismatch, new StackTrace(1, true))); 3207 } else { 3208 using SqlCommand qry = new($@"SELECT COUNT_BIG(*) AS fi64Count 3209 FROM [{Schema.Database.Name.Value}].[{Schema.Name.Value}].[{Name}];", txn.Connection, txn, SqlCommandColumnEncryptionSetting.Disabled) { CommandTimeout = timeout.MapOr(0, _nzUshortToInt), CommandType = CommandType.Text, EnableOptimizedParameterBinding = true }; 3210 return new((ulong)(long)qry.ExecuteScalar()); 3211 } 3212 } 3213 public override readonly string ToString() => $"{Schema.IntoString()}.{Name}"; 3214 [System.Diagnostics.CodeAnalysis.SuppressMessage("Security", "CA2100:Review SQL queries for security vulnerabilities", Justification = "The text is generated internally.")] 3215 public readonly Result<Unit, TRUNCATEError> TRUNCATE(Maybe<NonZeroUshort> timeout, SessionOptions options) { 3216 3217 if (Schema.Database.IsReadOnly) { return new(new TRUNCATEError(new StackTrace(1, true))); } 3218 using var con = Functions.CreateOpenedConnection(in Schema.Database, options, false, Maybe<Uri>.None()); 3219 using var txn = con.BeginTransaction(IsolationLevel.Serializable); 3220 using (SqlCommand qry = new($"TRUNCATE TABLE [{Schema.Name.Value}].[{Name}];", con, txn, SqlCommandColumnEncryptionSetting.Disabled) { CommandTimeout = timeout.MapOr(0, _nzUshortToInt), CommandType = CommandType.Text, EnableOptimizedParameterBinding = true }) { _ = qry.ExecuteNonQuery(); } 3221 txn.Commit(); 3222 return new(new Unit()); 3223 } 3224 [System.Diagnostics.CodeAnalysis.SuppressMessage("Security", "CA2100:Review SQL queries for security vulnerabilities", Justification = "The text is generated internally.")] 3225 public readonly Result<Unit, TRUNCATETransactionError> TRUNCATE(Maybe<NonZeroUshort> timeout, SqlTransaction txn) { 3226 3227 if (Schema.Database.IsReadOnly) { 3228 return new(new TRUNCATETransactionError(TRUNCATETransactionError.Tag.DatabaseIsReadOnly, new StackTrace(1, true))); 3229 } else if (txn.Connection.State != ConnectionState.Open) { 3230 return new(new TRUNCATETransactionError(TRUNCATETransactionError.Tag.SqlConnectionIsNotOpen, new StackTrace(1, true))); 3231 } else if (!string.Equals(txn.Connection.DataSource, $"tcp:{Schema.Database.Server.IntoString()}", StringComparison.Ordinal)) { 3232 return new(new TRUNCATETransactionError(TRUNCATETransactionError.Tag.SqlConnectionServerMismatch, new StackTrace(1, true))); 3233 } else { 3234 using SqlCommand qry = new($"TRUNCATE TABLE [{Schema.Database.Name.Value}].[{Schema.Name.Value}].[{Name}];", txn.Connection, txn, SqlCommandColumnEncryptionSetting.Disabled) { CommandTimeout = timeout.MapOr(0, _nzUshortToInt), CommandType = CommandType.Text, EnableOptimizedParameterBinding = true }; 3235 _ = qry.ExecuteNonQuery(); 3236 return new(new Unit()); 3237 } 3238 } 3239 readonly Result<UserTable, Bottom> ITryInto<UserTable, Bottom>.TryInto() => new(this); 3240 readonly Result<string, Bottom> ITryInto<string, Bottom>.TryInto() => new(ToString()); 3241 #endregion 3242 3243 #region Operators 3244 public static bool operator !=(UserTable val0, UserTable val1) => !(val0 == val1); 3245 // Since creation of UserTables is restricted internally thus requiring one to use an existing Schema instance, we simply need to compare Schemas and Names. 3246 public static bool operator ==(UserTable val0, UserTable val1) => val0.Schema == val1.Schema && val0.ObjectID == val1.ObjectID; 3247 #endregion 3248 3249 #region Types 3250 [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode, Pack = 8, Size = 8)] 3251 public readonly struct TRUNCATEError: IError, IInto<TRUNCATEError> { 3252 3253 #region Type-level Constructors 3254 #endregion 3255 3256 #region Instance Constructors 3257 public TRUNCATEError() => throw new InvalidOperationException("Parameterless constructor is not allowed to be called!"); 3258 internal TRUNCATEError(StackTrace trace) => Backtrace = trace; 3259 #endregion 3260 3261 #region Type-level Fields 3262 #endregion 3263 3264 #region Instance Fields 3265 [FieldOffset(0)] public readonly StackTrace Backtrace; 3266 #endregion 3267 3268 #region Type-level Properties 3269 #endregion 3270 3271 #region Instance Properties 3272 #endregion 3273 3274 #region Type-level Functions 3275 #endregion 3276 3277 #region Instance Functions 3278 public override readonly bool Equals(object? _) => false; 3279 public override readonly int GetHashCode() => 0; 3280 public readonly TRUNCATEError Into() => this; 3281 public readonly string IntoString() => ToString(); 3282 readonly string IInto<string>.Into() => IntoString(); 3283 public readonly Maybe<IError> Source() => Maybe<IError>.None(); 3284 readonly Maybe<StackTrace> IError.StackTrace() => new(Backtrace); 3285 public override readonly string ToString() => Backtrace.ToString(); 3286 readonly Result<TRUNCATEError, Bottom> ITryInto<TRUNCATEError, Bottom>.TryInto() => new(this); 3287 readonly Result<string, Bottom> ITryInto<string, Bottom>.TryInto() => new(ToString()); 3288 #endregion 3289 3290 #region Operators 3291 #endregion 3292 3293 #region Types 3294 #endregion 3295 } 3296 [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode, Pack = 8, Size = 16)] 3297 public readonly struct TRUNCATETransactionError: ISum<StackTrace, StackTrace, StackTrace>, IError, IInto<TRUNCATETransactionError> { 3298 3299 #region Type-level Constructors 3300 #endregion 3301 3302 #region Instance Constructors 3303 public TRUNCATETransactionError() => throw new InvalidOperationException("Parameterless constructor is not allowed to be called!"); 3304 internal TRUNCATETransactionError(Tag flag, StackTrace trace) => (Var, Backtrace) = (flag, trace); 3305 #endregion 3306 3307 #region Type-level Fields 3308 #endregion 3309 3310 #region Instance Fields 3311 [FieldOffset(0)] public readonly Tag Var; 3312 [FieldOffset(8)] public readonly StackTrace Backtrace; 3313 #endregion 3314 3315 #region Type-level Properties 3316 #endregion 3317 3318 #region Instance Properties 3319 public readonly Var3 Variant => (Var3)Var; 3320 public readonly StackTrace Variant0 => Var == Tag.DatabaseIsReadOnly ? Backtrace : throw new InvalidOperationException($"The TRUNCATETransactionError variant, {Var.ToString()}, is not DatabaseIsReadOnly!"); 3321 public readonly StackTrace Variant1 => Var == Tag.SqlConnectionIsNotOpen ? Backtrace : throw new InvalidOperationException($"The TRUNCATETransactionError variant, {Var.ToString()}, is not SqlConnectionIsNotOpen!"); 3322 public readonly StackTrace Variant2 => Var == Tag.SqlConnectionServerMismatch ? Backtrace : throw new InvalidOperationException($"The TRUNCATETransactionError variant, {Var.ToString()}, is not SqlConnectionServerMismatch!"); 3323 #endregion 3324 3325 #region Type-level Functions 3326 #endregion 3327 3328 #region Instance Functions 3329 public override readonly bool Equals(object? _) => false; 3330 public override readonly int GetHashCode() => 0; 3331 public readonly TRUNCATETransactionError Into() => this; 3332 public readonly string IntoString() => ToString(); 3333 readonly string IInto<string>.Into() => IntoString(); 3334 public readonly Maybe<IError> Source() => Maybe<IError>.None(); 3335 public readonly Maybe<StackTrace> StackTrace() => new(Backtrace); 3336 public override readonly string ToString() => $"{Var.ToString()}({Backtrace.ToString()})"; 3337 readonly Result<TRUNCATETransactionError, Bottom> ITryInto<TRUNCATETransactionError, Bottom>.TryInto() => new(this); 3338 readonly Result<string, Bottom> ITryInto<string, Bottom>.TryInto() => new(ToString()); 3339 #endregion 3340 3341 #region Operators 3342 #endregion 3343 3344 #region Types 3345 public enum Tag: ulong { 3346 DatabaseIsReadOnly = ulong.MinValue, 3347 SqlConnectionIsNotOpen = 1ul, 3348 SqlConnectionServerMismatch = 2ul, 3349 } 3350 #endregion 3351 } 3352 [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode, Pack = 8, Size = 16)] 3353 public readonly struct DELETEError: ISum<StackTrace, StackTrace, StackTrace, StackTrace, StackTrace, StackTrace>, IError, IInto<DELETEError> { 3354 3355 #region Type-level Constructors 3356 #endregion 3357 3358 #region Instance Constructors 3359 public DELETEError() => throw new InvalidOperationException("Parameterless constructor is not allowed to be called!"); 3360 internal DELETEError(Tag flag, StackTrace trace) => (Var, Backtrace) = (flag, trace); 3361 #endregion 3362 3363 #region Type-level Fields 3364 #endregion 3365 3366 #region Instance Fields 3367 [FieldOffset(0)] public readonly Tag Var; 3368 [FieldOffset(8)] public readonly StackTrace Backtrace; 3369 #endregion 3370 3371 #region Type-level Properties 3372 #endregion 3373 3374 #region Instance Properties 3375 public readonly Var6 Variant => (Var6)Var; 3376 public readonly StackTrace Variant0 => Var == Tag.DatabaseIsReadOnly ? Backtrace : throw new InvalidOperationException($"The DELETEError variant, {Var.ToString()}, is not DatabaseIsReadOnly!"); 3377 public readonly StackTrace Variant1 => Var == Tag.SchemasMismatch ? Backtrace : throw new InvalidOperationException($"The DELETEError variant, {Var.ToString()}, is not SchemasMismatch!"); 3378 public readonly StackTrace Variant2 => Var == Tag.TablesAreEqual ? Backtrace : throw new InvalidOperationException($"The DELETEError variant, {Var.ToString()}, is not TablesAreEqual!"); 3379 public readonly StackTrace Variant3 => Var == Tag.ContainsRandomizedNonEnclaveEncryptedColumn ? Backtrace : throw new InvalidOperationException($"The DELETEError variant, {Var.ToString()}, is not ContainsRandomizedNonEnclaveEncryptedColumn!"); 3380 public readonly StackTrace Variant4 => Var == Tag.ColumnsMismatch ? Backtrace : throw new InvalidOperationException($"The DELETEError variant, {Var.ToString()}, is not ColumnsMismatch!"); 3381 public readonly StackTrace Variant5 => Var == Tag.ContainsRandomizedEnclaveEncryptedColumnButWasNotPassedAttesationURL ? Backtrace : throw new InvalidOperationException($"The DELETEError variant, {Var.ToString()}, is not ContainsRandomizedEnclaveEncryptedColumnButWasNotPassedAttesationURL!"); 3382 #endregion 3383 3384 #region Type-level Functions 3385 #endregion 3386 3387 #region Instance Functions 3388 public override readonly bool Equals(object? _) => false; 3389 public override readonly int GetHashCode() => 0; 3390 public readonly DELETEError Into() => this; 3391 public readonly string IntoString() => ToString(); 3392 readonly string IInto<string>.Into() => IntoString(); 3393 public readonly Maybe<IError> Source() => Maybe<IError>.None(); 3394 public readonly Maybe<StackTrace> StackTrace() => new(Backtrace); 3395 public override readonly string ToString() => $"{Var.ToString()}({Backtrace.ToString()})"; 3396 readonly Result<DELETEError, Bottom> ITryInto<DELETEError, Bottom>.TryInto() => new(this); 3397 readonly Result<string, Bottom> ITryInto<string, Bottom>.TryInto() => new(ToString()); 3398 #endregion 3399 3400 #region Operators 3401 #endregion 3402 3403 #region Types 3404 public enum Tag: ulong { 3405 DatabaseIsReadOnly = ulong.MinValue, 3406 SchemasMismatch = 1ul, 3407 TablesAreEqual = 2ul, 3408 ContainsRandomizedNonEnclaveEncryptedColumn = 3ul, 3409 ColumnsMismatch = 4ul, 3410 ContainsRandomizedEnclaveEncryptedColumnButWasNotPassedAttesationURL = 5ul, 3411 } 3412 #endregion 3413 } 3414 [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode, Pack = 8, Size = 16)] 3415 public readonly struct DELETETransactionError: ISum<StackTrace, StackTrace, StackTrace, StackTrace, StackTrace, StackTrace, StackTrace, StackTrace>, IError, IInto<DELETETransactionError> { 3416 3417 #region Type-level Constructors 3418 #endregion 3419 3420 #region Instance Constructors 3421 public DELETETransactionError() => throw new InvalidOperationException("Parameterless constructor is not allowed to be called!"); 3422 internal DELETETransactionError(Tag flag, StackTrace trace) => (Var, Backtrace) = (flag, trace); 3423 #endregion 3424 3425 #region Type-level Fields 3426 #endregion 3427 3428 #region Instance Fields 3429 [FieldOffset(0)] public readonly Tag Var; 3430 [FieldOffset(8)] public readonly StackTrace Backtrace; 3431 #endregion 3432 3433 #region Type-level Properties 3434 #endregion 3435 3436 #region Instance Properties 3437 public readonly Var8 Variant => (Var8)Var; 3438 public readonly StackTrace Variant0 => Var == Tag.DatabaseIsReadOnly ? Backtrace : throw new InvalidOperationException($"The DELETETransactionError variant, {Var.ToString()}, is not DatabaseIsReadOnly!"); 3439 public readonly StackTrace Variant1 => Var == Tag.SqlConnectionIsNotOpen ? Backtrace : throw new InvalidOperationException($"The DELETETransactionError variant, {Var.ToString()}, is not SqlConnectionIsNotOpen!"); 3440 public readonly StackTrace Variant2 => Var == Tag.SqlConnectionServerMismatch ? Backtrace : throw new InvalidOperationException($"The DELETETransactionError variant, {Var.ToString()}, is not SqlConnectionServerMismatch!"); 3441 public readonly StackTrace Variant3 => Var == Tag.SchemasMismatch ? Backtrace : throw new InvalidOperationException($"The DELETETransactionError variant, {Var.ToString()}, is not SchemasMismatch!"); 3442 public readonly StackTrace Variant4 => Var == Tag.TablesAreEqual ? Backtrace : throw new InvalidOperationException($"The DELETETransactionError variant, {Var.ToString()}, is not TablesAreEqual!"); 3443 public readonly StackTrace Variant5 => Var == Tag.ContainsRandomizedNonEnclaveEncryptedColumn ? Backtrace : throw new InvalidOperationException($"The DELETETransactionError variant, {Var.ToString()}, is not ContainsRandomizedNonEnclaveEncryptedColumn!"); 3444 public readonly StackTrace Variant6 => Var == Tag.ColumnsMismatch ? Backtrace : throw new InvalidOperationException($"The DELETETransactionError variant, {Var.ToString()}, is not ColumnsMismatch!"); 3445 public readonly StackTrace Variant7 => Var == Tag.ContainsRandomizedEnclaveEncryptedColumnButWasNotPassedAttesationURL ? Backtrace : throw new InvalidOperationException($"The DELETETransactionError variant, {Var.ToString()}, is not ContainsRandomizedEnclaveEncryptedColumnButWasNotPassedAttesationURL!"); 3446 #endregion 3447 3448 #region Type-level Functions 3449 #endregion 3450 3451 #region Instance Functions 3452 public override readonly bool Equals(object? _) => false; 3453 public override readonly int GetHashCode() => 0; 3454 public readonly DELETETransactionError Into() => this; 3455 public readonly string IntoString() => ToString(); 3456 readonly string IInto<string>.Into() => IntoString(); 3457 public readonly Maybe<IError> Source() => Maybe<IError>.None(); 3458 public readonly Maybe<StackTrace> StackTrace() => new(Backtrace); 3459 public override readonly string ToString() => $"{Var.ToString()}({Backtrace.ToString()})"; 3460 readonly Result<DELETETransactionError, Bottom> ITryInto<DELETETransactionError, Bottom>.TryInto() => new(this); 3461 readonly Result<string, Bottom> ITryInto<string, Bottom>.TryInto() => new(ToString()); 3462 #endregion 3463 3464 #region Operators 3465 #endregion 3466 3467 #region Types 3468 public enum Tag: ulong { 3469 DatabaseIsReadOnly = ulong.MinValue, 3470 SqlConnectionIsNotOpen = 1ul, 3471 SqlConnectionServerMismatch = 2ul, 3472 SchemasMismatch = 3ul, 3473 TablesAreEqual = 4ul, 3474 ContainsRandomizedNonEnclaveEncryptedColumn = 5ul, 3475 ColumnsMismatch = 6ul, 3476 ContainsRandomizedEnclaveEncryptedColumnButWasNotPassedAttesationURL = 7ul, 3477 } 3478 #endregion 3479 } 3480 [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode, Pack = 8, Size = 16)] 3481 public readonly struct INSERTError: ISum<StackTrace, StackTrace, StackTrace, StackTrace>, IError, IInto<INSERTError> { 3482 3483 #region Type-level Constructors 3484 #endregion 3485 3486 #region Instance Constructors 3487 public INSERTError() => throw new InvalidOperationException("Parameterless constructor is not allowed to be called!"); 3488 internal INSERTError(Tag flag, StackTrace trace) => (Var, Backtrace) = (flag, trace); 3489 #endregion 3490 3491 #region Type-level Fields 3492 #endregion 3493 3494 #region Instance Fields 3495 [FieldOffset(0)] public readonly Tag Var; 3496 [FieldOffset(8)] public readonly StackTrace Backtrace; 3497 #endregion 3498 3499 #region Type-level Properties 3500 #endregion 3501 3502 #region Instance Properties 3503 public readonly Var4 Variant => (Var4)Var; 3504 public readonly StackTrace Variant0 => Var == Tag.DatabaseIsReadOnly ? Backtrace : throw new InvalidOperationException($"The INSERTError variant, {Var.ToString()}, is not DatabaseIsReadOnly!"); 3505 public readonly StackTrace Variant1 => Var == Tag.SchemasMismatch ? Backtrace : throw new InvalidOperationException($"The INSERTError variant, {Var.ToString()}, is not SchemasMismatch!"); 3506 public readonly StackTrace Variant2 => Var == Tag.TablesAreEqual ? Backtrace : throw new InvalidOperationException($"The INSERTError variant, {Var.ToString()}, is not TablesAreEqual!"); 3507 public readonly StackTrace Variant3 => Var == Tag.ColumnsMismatch ? Backtrace : throw new InvalidOperationException($"The INSERTError variant, {Var.ToString()}, is not ColumnsMismatch!"); 3508 #endregion 3509 3510 #region Type-level Functions 3511 #endregion 3512 3513 #region Instance Functions 3514 public override readonly bool Equals(object? _) => false; 3515 public override readonly int GetHashCode() => 0; 3516 public readonly INSERTError Into() => this; 3517 public readonly string IntoString() => ToString(); 3518 readonly string IInto<string>.Into() => IntoString(); 3519 public readonly Maybe<IError> Source() => Maybe<IError>.None(); 3520 public readonly Maybe<StackTrace> StackTrace() => new(Backtrace); 3521 public override readonly string ToString() => $"{Var.ToString()}({Backtrace.ToString()})"; 3522 readonly Result<INSERTError, Bottom> ITryInto<INSERTError, Bottom>.TryInto() => new(this); 3523 readonly Result<string, Bottom> ITryInto<string, Bottom>.TryInto() => new(ToString()); 3524 #endregion 3525 3526 #region Operators 3527 #endregion 3528 3529 #region Types 3530 public enum Tag: ulong { 3531 DatabaseIsReadOnly = ulong.MinValue, 3532 SchemasMismatch = 1ul, 3533 TablesAreEqual = 2ul, 3534 ColumnsMismatch = 3ul, 3535 } 3536 #endregion 3537 } 3538 [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode, Pack = 8, Size = 16)] 3539 public readonly struct INSERTTransactionError: ISum<StackTrace, StackTrace, StackTrace, StackTrace, StackTrace, StackTrace>, IError, IInto<INSERTTransactionError> { 3540 3541 #region Type-level Constructors 3542 #endregion 3543 3544 #region Instance Constructors 3545 public INSERTTransactionError() => throw new InvalidOperationException("Parameterless constructor is not allowed to be called!"); 3546 internal INSERTTransactionError(Tag flag, StackTrace trace) => (Var, Backtrace) = (flag, trace); 3547 #endregion 3548 3549 #region Type-level Fields 3550 #endregion 3551 3552 #region Instance Fields 3553 [FieldOffset(0)] public readonly Tag Var; 3554 [FieldOffset(8)] public readonly StackTrace Backtrace; 3555 #endregion 3556 3557 #region Type-level Properties 3558 #endregion 3559 3560 #region Instance Properties 3561 public readonly Var6 Variant => (Var6)Var; 3562 public readonly StackTrace Variant0 => Var == Tag.DatabaseIsReadOnly ? Backtrace : throw new InvalidOperationException($"The INSERTTransactionError variant, {Var.ToString()}, is not DatabaseIsReadOnly!"); 3563 public readonly StackTrace Variant1 => Var == Tag.SqlConnectionIsNotOpen ? Backtrace : throw new InvalidOperationException($"The INSERTTransactionError variant, {Var.ToString()}, is not SqlConnectionIsNotOpen!"); 3564 public readonly StackTrace Variant2 => Var == Tag.SqlConnectionServerMismatch ? Backtrace : throw new InvalidOperationException($"The INSERTTransactionError variant, {Var.ToString()}, is not SqlConnectionServerMismatch!"); 3565 public readonly StackTrace Variant3 => Var == Tag.SchemasMismatch ? Backtrace : throw new InvalidOperationException($"The INSERTTransactionError variant, {Var.ToString()}, is not SchemasMismatch!"); 3566 public readonly StackTrace Variant4 => Var == Tag.TablesAreEqual ? Backtrace : throw new InvalidOperationException($"The INSERTTransactionError variant, {Var.ToString()}, is not TablesAreEqual!"); 3567 public readonly StackTrace Variant5 => Var == Tag.ColumnsMismatch ? Backtrace : throw new InvalidOperationException($"The INSERTTransactionError variant, {Var.ToString()}, is not ColumnsMismatch!"); 3568 #endregion 3569 3570 #region Type-level Functions 3571 #endregion 3572 3573 #region Instance Functions 3574 public override readonly bool Equals(object? _) => false; 3575 public override readonly int GetHashCode() => 0; 3576 public readonly INSERTTransactionError Into() => this; 3577 public readonly string IntoString() => ToString(); 3578 readonly string IInto<string>.Into() => IntoString(); 3579 public readonly Maybe<IError> Source() => Maybe<IError>.None(); 3580 public readonly Maybe<StackTrace> StackTrace() => new(Backtrace); 3581 public override readonly string ToString() => $"{Var.ToString()}({Backtrace.ToString()})"; 3582 readonly Result<INSERTTransactionError, Bottom> ITryInto<INSERTTransactionError, Bottom>.TryInto() => new(this); 3583 readonly Result<string, Bottom> ITryInto<string, Bottom>.TryInto() => new(ToString()); 3584 #endregion 3585 3586 #region Operators 3587 #endregion 3588 3589 #region Types 3590 public enum Tag: ulong { 3591 DatabaseIsReadOnly = ulong.MinValue, 3592 SqlConnectionIsNotOpen = 1ul, 3593 SqlConnectionServerMismatch = 2ul, 3594 SchemasMismatch = 3ul, 3595 TablesAreEqual = 4ul, 3596 ColumnsMismatch = 5ul, 3597 } 3598 #endregion 3599 } 3600 #endregion 3601 } 3602 public static class Functions { 3603 3604 #region Type-level Constructors 3605 #endregion 3606 3607 #region Instance Constructors 3608 #endregion 3609 3610 #region Type-level Fields 3611 static readonly Fn<Uri, string> _attest = (uri) => $"Attestation Protocol=HGS;Enclave Attestation Url={uri.ToString()};"; 3612 static readonly Prod<SessionOptions, string>[] _sessionOff = new Prod<SessionOptions, string>[9] { 3613 new(SessionOptions.ANSI_NULLS_OFF, "ANSI_NULLS, "), 3614 new(SessionOptions.ANSI_PADDING_OFF, "ANSI_PADDING, "), 3615 new(SessionOptions.ANSI_WARNINGS_OFF, "ANSI_WARNINGS, "), 3616 new(SessionOptions.ARITHABORT_OFF, "ARITHABORT, "), 3617 new(SessionOptions.CONCAT_NULL_YIELDS_NULL_OFF, "CONCAT_NULL_YIELDS_NULL, "), 3618 new(SessionOptions.NOCOUNT_OFF, "NOCOUNT, "), 3619 new(SessionOptions.NUMERIC_ROUNDABORT_OFF, "NUMERIC_ROUNDABORT, "), 3620 new(SessionOptions.QUOTED_IDENTIFIER_OFF, "QUOTED_IDENTIFIER, "), 3621 new(SessionOptions.XACT_ABORT_OFF, "XACT_ABORT, ") 3622 }; 3623 static readonly Prod<SessionOptions, string>[] _sessionOn = new Prod<SessionOptions, string>[7] { 3624 new(SessionOptions.ARITHIGNORE_ON, "ARITHIGNORE, "), 3625 new(SessionOptions.CURSOR_CLOSE_ON_COMMIT_ON, "CURSOR_CLOSE_ON_COMMIT, "), 3626 new(SessionOptions.FMTONLY_ON, "FMTONLY, "), 3627 new(SessionOptions.FORCEPLAN_ON, "FORCEPLAN, "), 3628 new(SessionOptions.IMPLICIT_TRANSACTIONS_ON, "IMPLICIT_TRANSACTIONS, "), 3629 new(SessionOptions.NOEXEC_ON, "NOEXEC, "), 3630 new(SessionOptions.PARSEONLY_ON, "PARSEONLY, ") 3631 }; 3632 #endregion 3633 3634 #region Instance Fields 3635 #endregion 3636 3637 #region Type-level Properties 3638 #endregion 3639 3640 #region Instance Properties 3641 #endregion 3642 3643 #region Type-level Functions 3644 // instance MUST be blank if port is not 0. 3645 // instance MUST NOT be blank or port MUST NOT be 0. 3646 internal static string CreateConnectionString(string hostname, string instance, ushort port, bool encryption, Maybe<Uri> attestation) => $"Application Name=.NET SQLClient Data Provider;Application Intent=ReadWrite;Column Encryption Setting={(encryption ? "enabled" : "disabled")};{attestation.MapOr(string.Empty, _attest)}Command Timeout=30;Connect Timeout=15;Connection Lifetime=0;Connect Retry Count=1;Connect Retry Interval=10;Current Language=us_english;Data Source=tcp:{hostname}{(instance.Length == 0 ? string.Empty : $@"\{instance}")}{(port == ushort.MinValue ? string.Empty : $",{port.ToString()}")};Encrypt=strict;Enlist=false;Initial Catalog=master;Integrated Security=sspi;Max Pool Size=128;Min Pool Size=0;Multiple Active Result Sets=false;Multi Subnet Failover=false;Packet Size=8000;Persist Security Info=false;Pool Blocking Period=AlwaysBlock;Pooling=true;Replication=false;Transaction Binding=Implicit Unbind;Trust Server Certificate=false;User Instance=false;WSID={Environment.MachineName}"; 3647 [System.Diagnostics.CodeAnalysis.SuppressMessage("Security", "CA2100:Review SQL queries for security vulnerabilities", Justification = "The text is generated internally.")] 3648 public static SqlConnection CreateOpenedConnection(in Database database, SessionOptions options, bool encryption, Maybe<Uri> attestation) { 3649 3650 SqlConnection con = new(CreateConnectionString(database.Server.Hostname, database.Server.Instance, database.Server.Port, encryption, attestation)); 3651 try { 3652 con.Open(); 3653 using var txn = con.BeginTransaction(IsolationLevel.Serializable); 3654 using (SqlCommand qry = new($@"USE [{database.Name.Value}]; 3655 {CreateSessionString(options)}", con, txn, SqlCommandColumnEncryptionSetting.Disabled) { CommandTimeout = 60, CommandType = CommandType.Text, EnableOptimizedParameterBinding = true }) { 3656 _ = qry.ExecuteNonQuery(); 3657 } 3658 txn.Commit(); 3659 return con; 3660 } catch (Exception) { 3661 con.Dispose(); 3662 throw; 3663 } 3664 } 3665 internal static string CreateSessionString(SessionOptions options) { 3666 3667 StringBuilder builder = new(512); 3668 3669 if (options == SessionOptions.DEFAULT) { 3670 _ = builder.Append(@"SET ANSI_NULL_DFLT_ON, ANSI_NULLS, ANSI_PADDING, ANSI_WARNINGS, ARITHABORT, CONCAT_NULL_YIELDS_NULL, NOCOUNT, NUMERIC_ROUNDABORT, QUOTED_IDENTIFIER, XACT_ABORT ON; 3671 SET ANSI_NULL_DFLT_OFF, ARITHIGNORE, CURSOR_CLOSE_ON_COMMIT, FMTONLY, FORCEPLAN, IMPLICIT_TRANSACTIONS, NOEXEC, PARSEONLY OFF; 3672 "); 3673 } else { 3674 _ = (options & SessionOptions.ANSI_NULL_DFLT_OFF) == SessionOptions.ANSI_NULL_DFLT_OFF ? builder.Append("SET ANSI_NULL_DFLT_OFF, ") : builder.Append("SET ANSI_NULL_DFLT_ON, "); 3675 Prod<SessionOptions, string> val; 3676 3677 for (var i = 0; i < _sessionOn.Length; i++) { 3678 val = _sessionOn[i]; 3679 if (val.Item0 == (val.Item0 & options)) { _ = builder.Append(val.Item1); } 3680 } 3681 3682 for (var i = 0; i < _sessionOff.Length; i++) { 3683 val = _sessionOff[i]; 3684 if (val.Item0 != (val.Item0 & options)) { _ = builder.Append(val.Item1); } 3685 } 3686 _ = builder.Remove(builder.Length - 2, 2).Append(@" ON; 3687 "); 3688 _ = (options & SessionOptions.ANSI_NULL_DFLT_OFF) == SessionOptions.ANSI_NULL_DFLT_OFF ? builder.Append("SET ANSI_NULL_DFLT_ON, ") : builder.Append("SET ANSI_NULL_DFLT_OFF, "); 3689 3690 for (var i = 0; i < _sessionOff.Length; i++) { 3691 val = _sessionOff[i]; 3692 if (val.Item0 == (val.Item0 & options)) { _ = builder.Append(val.Item1); } 3693 } 3694 3695 for (var i = 0; i < _sessionOn.Length; i++) { 3696 val = _sessionOn[i]; 3697 if (val.Item0 != (val.Item0 & options)) { _ = builder.Append(val.Item1); } 3698 } 3699 _ = builder.Remove(builder.Length - 2, 2).Append(@" OFF; 3700 "); 3701 } 3702 return builder.Append(@"SET DATEFIRST 7; 3703 SET DATEFORMAT mdy; 3704 SET DEADLOCK_PRIORITY HIGH; 3705 SET FIPS_FLAGGER OFF; 3706 SET LOCK_TIMEOUT -1; 3707 SET QUERY_GOVERNOR_COST_LIMIT 0; 3708 SET ROWCOUNT 0; 3709 SET TEXTSIZE -1;").ToString(); 3710 } 3711 internal static Type GetTypeName(string sqlTypeName) => sqlTypeName switch { 3712 "bigint" => typeof(bigint), 3713 "binary" => typeof(binary), 3714 "bit" => typeof(bit), 3715 "char" => typeof(@char), 3716 "date" => typeof(date), 3717 "datetime" => typeof(datetime), 3718 "datetime2" => typeof(datetime2), 3719 "datetimeoffset" => typeof(datetimeoffset), 3720 "decimal" => typeof(@decimal), 3721 "float" => typeof(@float), 3722 "geography" => throw new InvalidOperationException("geography is not supported."), 3723 "geometry" => throw new InvalidOperationException("geometry is not supported."), 3724 "hierarchyid" => throw new InvalidOperationException("hierarchyid is not supported."), 3725 "image" => typeof(image), 3726 "int" => typeof(@int), 3727 "money" => typeof(money), 3728 "nchar" => typeof(nchar), 3729 "ntext" => typeof(ntext), 3730 "nvarchar" => typeof(nvarchar), 3731 "real" => typeof(real), 3732 "smalldatetime" => typeof(smalldatetime), 3733 "smallint" => typeof(smallint), 3734 "smallmoney" => typeof(smallmoney), 3735 "sql_variant" => typeof(sql_variant), 3736 "text" => typeof(text), 3737 "time" => typeof(time), 3738 "tinyint" => typeof(tinyint), 3739 "uniqueidentifier" => typeof(uniqueidentifier), 3740 "varbinary" => typeof(varbinary), 3741 "varchar" => typeof(varchar), 3742 "xml" => typeof(xml), 3743 _ => throw new InvalidOperationException($"{sqlTypeName} is not supported."), 3744 }; 3745 #endregion 3746 3747 #region Instance Functions 3748 #endregion 3749 3750 #region Operators 3751 #endregion 3752 3753 #region Types 3754 #endregion 3755 } 3756 #endregion 3757 3758 #region Namespaces 3759 #endregion 3760 } 3761 #endregion