main.rs (15911B)
1 //! This crate generates the code for the "bulk writers" 2 //! in the `BulkWriters128` and `BulkWriters256` C# libraries. 3 #![no_implicit_prelude] 4 #![deny( 5 unsafe_code, 6 unused, 7 warnings, 8 clippy::all, 9 clippy::cargo, 10 clippy::nursery, 11 clippy::pedantic 12 )] 13 #![allow( 14 clippy::arithmetic_side_effects, 15 clippy::implicit_return, 16 clippy::integer_arithmetic 17 )] 18 #[allow(unused_extern_crates)] 19 extern crate alloc; 20 #[allow(unused_extern_crates)] 21 extern crate core; 22 #[allow(unused_extern_crates)] 23 extern crate std; 24 use alloc::string::{String, ToString}; 25 use core::clone::Clone; 26 use core::convert::From; 27 use core::fmt::{self, Debug, Display, Formatter}; 28 use core::result::Result::{self, Ok}; 29 use std::env; 30 use std::fs::File; 31 use std::io::{Error, Write}; 32 fn main() -> Result<(), GenErr> { 33 #[allow(deprecated)] 34 let mut path0 = env::home_dir().ok_or(GenErr::HomeVarNotSet)?; 35 path0.push(".c#"); 36 let mut path1 = path0.clone(); 37 path0.push("BulkWriters128/BulkWriters.cs"); 38 let mut f = File::options().write(true).create_new(true).open(path0)?; 39 path1.push("BulkWriters256/BulkWriters.cs"); 40 let mut f2 = File::options().write(true).create_new(true).open(path1)?; 41 let mut s = String::with_capacity(32561); 42 write(&mut f, 0, &mut s)?; 43 write(&mut f2, 128, &mut s)?; 44 Ok(()) 45 } 46 /// Writes the file. 47 /// `s` is used as a buffer to write an entire `BulkWriter` into 48 /// before being used by `f` to write to the underlying file descriptor. 49 /// `v` is the starting arity of tuples: 0 or 128. 50 fn write(f: &mut File, v: u8, s: &mut String) -> Result<(), Error> { 51 s.push_str("// This code was generated via a CLI app that used the source code in BulkWriterBaseCase.cs as a template. 52 // To improve compilation performance, the code uses very little 'whitespace' beyond what is necessary and contains no documentation, warning suppressions, or comments. 53 using Microsoft.Data.SqlClient; 54 using Serde.Bin.Ser; 55 using Std; 56 using Std.Iter; 57 using Std.Maybe; 58 using Std.Num; 59 using Std.Result; 60 using System; 61 using System.Data; 62 using System.Diagnostics; 63 using static SQLServer.Helpers; 64 using System.Runtime.InteropServices; 65 #pragma warning disable CA1005, CA1045, CA1051, CA1062, CA1066, CA1815, CA2100, CA2231, IDE0032 66 namespace SQLServer {\n"); 67 // 68 for i in (if v == 0 { 1 } else { v })..=(v + 127) { 69 s.push_str("[StructLayout(LayoutKind.Sequential,CharSet=CharSet.Unicode,Pack= 0)]public struct BulkWriter<"); 70 for j in 0..=i { 71 Kind::T.write(s, j); 72 } 73 s.pop(); 74 s.push_str(">:IBulkWriter "); 75 for j in 0..=i { 76 Kind::Bound.write(s, j); 77 } 78 s.pop(); 79 s.push_str(r#"{public BulkWriter()=>throw new InvalidOperationException("Parameterless constructor is not allowed to be called!");BulkWriter(UserTable table,Maybe<ErrorTable>errTable,Prod<ulong,double>maxErrorsAllowed,string processName,string userName,bool encrypted,bool encryptedMod)=>(Destination,ErrTable,MaxErrorsAllowed,_currentErrorCount,_currentProcessedCount,_processName,_userName,_containsEncryptedColumn,_mustBeAllowEncryptedValueModifications)=(table,errTable,maxErrorsAllowed,ulong.MinValue,ulong.MinValue,processName,userName,encrypted,encryptedMod);public readonly Maybe<ErrorTable>ErrTable;public readonly UserTable Destination;public readonly Prod<ulong,double>MaxErrorsAllowed;ulong _currentErrorCount;ulong _currentProcessedCount;readonly string _processName;readonly string _userName;readonly bool _containsEncryptedColumn;readonly bool _mustBeAllowEncryptedValueModifications;readonly Maybe<ErrorTable>IBulkWriter.ErrTable=>ErrTable;readonly UserTable IBulkWriter.Destination=>Destination;readonly Prod<ulong,double>IBulkWriter.MaxErrorsAllowed=>MaxErrorsAllowed;public readonly ulong CurrentSuccessfullyProcessedCount=>_currentProcessedCount-_currentErrorCount;public readonly ulong CurrentErrorCount=>_currentErrorCount;public readonly ulong CurrentProcessedCount=>_currentProcessedCount;public readonly double CurrentErrorRatio=>(double)_currentErrorCount/_currentProcessedCount;public readonly bool IsInError=>_currentErrorCount>MaxErrorsAllowed.Item0&&CurrentErrorRatio>MaxErrorsAllowed.Item1;public static Result<BulkWriter<"#); 80 for j in 0..=i { 81 Kind::T.write(s, j); 82 } 83 s.pop(); 84 s.push_str(">,BulkWriterCreateError>New(in UserTable table,Prod<ulong,double>maxErrorsAllowed,in Maybe<ErrorTable>errTable,string processName,string userName){if(table.Schema.Database.IsReadOnly){return new(new BulkWriterCreateError(BulkWriterCreateError.Tag.DatabaseIsReadOnly,new StackTrace(1,true)));}else if(double.IsNaN(maxErrorsAllowed.Item1)||double.IsNegative(maxErrorsAllowed.Item1)||maxErrorsAllowed.Item1>1.0d){return new(new BulkWriterCreateError(BulkWriterCreateError.Tag.InvalidErrorRatio,new StackTrace(1,true)));}else if(processName.Length>128){return new(new BulkWriterCreateError(BulkWriterCreateError.Tag.ProcessNameLengthExceeds128,new StackTrace(1,true)));}else if(userName.Length>128){return new(new BulkWriterCreateError(BulkWriterCreateError.Tag.UserNameLengthExceeds128,new StackTrace(1,true)));}else{var counter=ushort.MinValue;var match=TypeMatch(Std.Iter.Functions.FromFn(()=>counter++switch{"); 85 for j in 0..=i { 86 Kind::GetType.write(s, j); 87 } 88 s.push_str("_=>Maybe<Type>.None()}),in table);return match.IsSome?match.Unwrap()?new(new BulkWriter<"); 89 for j in 0..=i { 90 Kind::T.write(s, j); 91 } 92 s.pop(); 93 s.push_str(">(table,errTable,maxErrorsAllowed,processName,userName,table.ContainsEncryptedColumn(),false)):new(new BulkWriterCreateError(BulkWriterCreateError.Tag.TypeMismatch,new StackTrace(1,true))):new(new BulkWriter<"); 94 for j in 0..=i { 95 Kind::T.write(s, j); 96 } 97 s.pop(); 98 s.push_str(">(table,errTable,maxErrorsAllowed,processName,userName,true,true));}}public override readonly bool Equals(object?_)=>false;public override readonly int GetHashCode()=>0;readonly void IBulkWriter.Sealed(){}public override readonly string ToString()=>string.Empty;public Result<Unit,WriteError>Write<TRowIter,TRow,TProd,TErr>(ref TRowIter iter,WriteOptions writeOptions,Maybe<NonZeroUshort>batchSize,Maybe<NonZeroUshort>timeout,bool enableStreaming,bool isSortedAccordingToClusteredIndex,SessionOptions options)where TErr:notnull,IBulkRowError where TProd:notnull,IBinSerializable,IProduct<"); 99 for j in 0..=i { 100 Kind::T.write(s, j); 101 } 102 s.pop(); 103 s.push_str(r#">where TRow:notnull,ISum<TProd,TErr>where TRowIter:notnull,IFusedIterator<TRow>{var allowEncryptMod=false;if((writeOptions&WriteOptions.AllowEncryptedValueModifications)==WriteOptions.AllowEncryptedValueModifications){if(_containsEncryptedColumn){allowEncryptMod=true;}else{return new(new WriteError(WriteError.Tag.TableExpectedToContainEncryptedDataButDoesNot,new StackTrace(1,true)));}}else if(_mustBeAllowEncryptedValueModifications){return new(new WriteError(WriteError.Tag.TableColumnMismatchOrWriteOptionsMustContainAllowEncryptedValueModifications,new StackTrace(1,true)));}using var con=Functions.CreateOpenedConnection(in Destination.Schema.Database,options,!allowEncryptMod&&_containsEncryptedColumn,Maybe<Uri>.None());if((writeOptions&WriteOptions.KeepIdentity)==WriteOptions.KeepIdentity&&Destination.ContainsIDENTITYColumn()){using var txn=con.BeginTransaction(IsolationLevel.Serializable);using (SqlCommand qry=new($"SET IDENTITY_INSERT [{Destination.Schema.Name.Value}].[{Destination.Name}] ON;",con,txn,SqlCommandColumnEncryptionSetting.Disabled){CommandTimeout=60,CommandType=CommandType.Text,EnableOptimizedParameterBinding=true}){_=qry.ExecuteNonQuery();}txn.Commit();}return WriteInternal<TRowIter,TRow,TProd,TErr>(ref iter,writeOptions,batchSize,timeout,enableStreaming,isSortedAccordingToClusteredIndex,con,null,(options&SessionOptions.NUMERIC_ROUNDABORT_OFF)!=SessionOptions.NUMERIC_ROUNDABORT_OFF,new StackTrace(1,true).ToString()).MapErr(_writeTxnErrToWriteErr);}public Result<Unit,WriteErrorOrTransactionError>Write<TRowIter,TRow,TProd,TErr>(ref TRowIter iter,WriteOptions writeOptions,Maybe<NonZeroUshort>batchSize,Maybe<NonZeroUshort>timeout,bool enableStreaming,bool isSortedAccordingToClusteredIndex,SqlTransaction txn)where TErr:notnull,IBulkRowError where TProd:notnull,IBinSerializable,IProduct<"#); 104 for j in 0..=i { 105 Kind::T.write(s, j); 106 } 107 s.pop(); 108 s.push_str(r#">where TRow:notnull,ISum<TProd,TErr>where TRowIter:notnull,IFusedIterator<TRow>{if(txn.Connection.State!=ConnectionState.Open){return new(new WriteErrorOrTransactionError(WriteErrorOrTransactionError.Tag.SqlConnectionIsNotOpen,new StackTrace(1,true)));}else if(!string.Equals(txn.Connection.DataSource,$"tcp:{Destination.Schema.Database.Server.IntoString()}",StringComparison.Ordinal)){return new(new WriteErrorOrTransactionError(WriteErrorOrTransactionError.Tag.SqlConnectionServerMismatch,new StackTrace(1,true)));}else{if((writeOptions&WriteOptions.AllowEncryptedValueModifications)==WriteOptions.AllowEncryptedValueModifications){if(_containsEncryptedColumn){if(!txn.Connection.ConnectionString.Contains("Column Encryption Setting=enabled",StringComparison.Ordinal)){return new(new WriteErrorOrTransactionError(WriteErrorOrTransactionError.Tag.SqlConnectionViolatesAllowEncryptedValueModifications,new StackTrace(1,true)));}}else{return new(new WriteErrorOrTransactionError(WriteErrorOrTransactionError.Tag.TableExpectedToContainEncryptedDataButDoesNot,new StackTrace(1,true)));}}else if(_mustBeAllowEncryptedValueModifications){return new(new WriteErrorOrTransactionError(WriteErrorOrTransactionError.Tag.TableColumnMismatchOrWriteOptionsMustContainAllowEncryptedValueModifications,new StackTrace(1,true)));}else if(_containsEncryptedColumn&&!txn.Connection.ConnectionString.Contains("Column Encryption Setting=enabled",StringComparison.Ordinal)){return new(new WriteErrorOrTransactionError(WriteErrorOrTransactionError.Tag.ContainsEncryptedColumnsButConnectionDoesNotHandleEncryptedColumns,new StackTrace(1,true)));}if((writeOptions&WriteOptions.KeepIdentity)==WriteOptions.KeepIdentity&&Destination.ContainsIDENTITYColumn()){bool numericRoundAbort;using (SqlCommand qry=new($@"SET IDENTITY_INSERT [{Destination.Schema.Database.Name.Value}].[{Destination.Schema.Name.Value}].[{Destination.Name}] ON;SELECT CASE (@@OPTIONS & 8192) WHEN 8192 THEN CONVERT(bit,1) ELSE CONVERT(bit,0) END AS fblnNumericRoundabort;",txn.Connection,txn,SqlCommandColumnEncryptionSetting.Disabled){CommandTimeout=timeout.MapOr(0,_nzUshortToInt),CommandType=CommandType.Text,EnableOptimizedParameterBinding=true}){numericRoundAbort=(bool)qry.ExecuteScalar();}var tableCopy=Destination;return WriteInternal<TRowIter,TRow,TProd,TErr>(ref iter,writeOptions,batchSize,timeout,enableStreaming,isSortedAccordingToClusteredIndex,txn.Connection,txn,numericRoundAbort,new StackTrace(1,true).ToString()).MapOrElse((err)=>{using SqlCommand qry=new($"SET IDENTITY_INSERT [{tableCopy.Schema.Database.Name.Value}].[{tableCopy.Schema.Name.Value}].[{tableCopy.Name}] OFF;",txn.Connection,txn,SqlCommandColumnEncryptionSetting.Disabled){CommandTimeout=timeout.MapOr(0,_nzUshortToInt),CommandType=CommandType.Text,EnableOptimizedParameterBinding=true};_=qry.ExecuteNonQuery();return new Result<Unit,WriteErrorOrTransactionError>(err);},(x)=>{using SqlCommand qry=new($"SET IDENTITY_INSERT [{tableCopy.Schema.Database.Name.Value}].[{tableCopy.Schema.Name.Value}].[{tableCopy.Name}] OFF;",txn.Connection,txn,SqlCommandColumnEncryptionSetting.Disabled){CommandTimeout=timeout.MapOr(0,_nzUshortToInt),CommandType=CommandType.Text,EnableOptimizedParameterBinding=true};var unused=qry.ExecuteNonQuery();return new(x);});}else{bool numericRoundAbort;using (SqlCommand qry=new($@"SELECT CASE (@@OPTIONS & 8192) WHEN 8192 THEN CONVERT(bit,1) ELSE CONVERT(bit,0) END AS fblnNumericRoundabort;",txn.Connection,txn,SqlCommandColumnEncryptionSetting.Disabled){CommandTimeout=timeout.MapOr(0,_nzUshortToInt),CommandType=CommandType.Text,EnableOptimizedParameterBinding=true}){numericRoundAbort=(bool)qry.ExecuteScalar();}return WriteInternal<TRowIter,TRow,TProd,TErr>(ref iter,writeOptions,batchSize,timeout,enableStreaming,isSortedAccordingToClusteredIndex,txn.Connection,txn,numericRoundAbort,new StackTrace(1,true).ToString());}}}Result<Unit,WriteErrorOrTransactionError>WriteInternal<TRowIter,TRow,TProd,TErr>(ref TRowIter iter,WriteOptions writeOptions,Maybe<NonZeroUshort>batchSize,Maybe<NonZeroUshort>timeout,bool enableStreaming,bool isSortedAccordingToClusteredIndex,SqlConnection con,SqlTransaction?txn,bool numericRoundAbort,string stackTrace)where TErr:notnull,IBulkRowError where TProd:notnull,IBinSerializable,IProduct<"#); 109 for j in 0..=i { 110 Kind::T.write(s, j); 111 } 112 s.pop(); 113 s.push_str(">where TRow:notnull,ISum<TProd,TErr>where TRowIter:notnull,IFusedIterator<TRow>{if(_currentErrorCount>MaxErrorsAllowed.Item0&&CurrentErrorRatio>MaxErrorsAllowed.Item1){return new(new WriteErrorOrTransactionError(WriteErrorOrTransactionError.Tag.MaxErrorsExceeded,new StackTrace(1,true)));}using var blk=CreateBulkCopy(in Destination,writeOptions,batchSize,timeout,enableStreaming,isSortedAccordingToClusteredIndex,con,txn);using IterDataReader<TRowIter,TRow,TProd,TErr,"); 114 for j in 0..=i { 115 Kind::T.write(s, j); 116 } 117 s.pop(); 118 s.push_str(">rdr=new(ErrTable,Destination,MaxErrorsAllowed,_currentProcessedCount,_currentErrorCount,iter,_processName,_userName,numericRoundAbort,stackTrace);blk.WriteToServer(rdr);_currentProcessedCount=rdr.CurrentProcessedCount;return (_currentErrorCount=rdr.CurrentErrorCount)>MaxErrorsAllowed.Item0&&CurrentErrorRatio>MaxErrorsAllowed.Item1?new(new WriteErrorOrTransactionError(WriteErrorOrTransactionError.Tag.MaxErrorsExceeded,new StackTrace(1,true))):new(new Unit());}}\n"); 119 f.write_all(s.as_bytes())?; 120 s.clear(); 121 } 122 f.write_all(&[b'}'])?; 123 f.flush() 124 } 125 /// The source code for a `BulkWriter` has multiple areas dependent 126 /// on the n-arity tuple that is written to the table. The code of which 127 /// is different, so we model those differences here. 128 enum Kind { 129 /// Represents the sequence of type parameters T0...Tn. 130 T, 131 /// Represents the type bounds for T0...Tn. 132 Bound, 133 /// Represents the portion of the source code based on T0...Tn 134 /// in the `New` function. 135 GetType, 136 } 137 impl Kind { 138 /// Writes the source code related to a single type parameter. 139 fn write(self, buffer: &mut String, i: u8) { 140 let s = &i.to_string(); 141 match self { 142 Self::T => { 143 buffer.push('T'); 144 buffer.push_str(s); 145 buffer.push(','); 146 } 147 Self::Bound => { 148 buffer.push_str("where "); 149 buffer.push('T'); 150 buffer.push_str(s); 151 buffer.push_str(":struct,IDataType "); 152 } 153 Self::GetType => { 154 buffer.push_str(s); 155 buffer.push_str("=>new(typeof(T"); 156 buffer.push_str(s); 157 buffer.push_str(")),"); 158 } 159 } 160 } 161 } 162 /// The errors possible. 163 enum GenErr { 164 /// Error when `$HOME` is not set. 165 HomeVarNotSet, 166 /// Error containing a general IO error. 167 Error(Error), 168 } 169 impl Display for GenErr { 170 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 171 match *self { 172 Self::HomeVarNotSet => f.write_str("$HOME is not set."), 173 Self::Error(ref e) => <Error as Display>::fmt(e, f), 174 } 175 } 176 } 177 impl Debug for GenErr { 178 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 179 <Self as Display>::fmt(self, f) 180 } 181 } 182 impl From<Error> for GenErr { 183 fn from(value: Error) -> Self { 184 Self::Error(value) 185 } 186 }