• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

HicServices / RDMP / 6245535001

20 Sep 2023 07:44AM UTC coverage: 57.013%. First build
6245535001

push

github

web-flow
8.1.0 Release (#1628)

* Bump Newtonsoft.Json from 13.0.1 to 13.0.2

Bumps [Newtonsoft.Json](https://github.com/JamesNK/Newtonsoft.Json) from 13.0.1 to 13.0.2.
- [Release notes](https://github.com/JamesNK/Newtonsoft.Json/releases)
- [Commits](https://github.com/JamesNK/Newtonsoft.Json/compare/13.0.1...13.0.2)

---
updated-dependencies:
- dependency-name: Newtonsoft.Json
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump NLog from 5.0.5 to 5.1.0

Bumps [NLog](https://github.com/NLog/NLog) from 5.0.5 to 5.1.0.
- [Release notes](https://github.com/NLog/NLog/releases)
- [Changelog](https://github.com/NLog/NLog/blob/dev/CHANGELOG.md)
- [Commits](https://github.com/NLog/NLog/compare/v5.0.5...v5.1.0)

---
updated-dependencies:
- dependency-name: NLog
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump NLog from 5.0.5 to 5.1.0

* Fix -r flag - should have been --results-directory all along

* Bump Newtonsoft.Json from 13.0.1 to 13.0.2

* Bump YamlDotNet from 12.0.2 to 12.1.0

Bumps [YamlDotNet](https://github.com/aaubry/YamlDotNet) from 12.0.2 to 12.1.0.
- [Release notes](https://github.com/aaubry/YamlDotNet/releases)
- [Commits](https://github.com/aaubry/YamlDotNet/compare/v12.0.2...v12.1.0)

---
updated-dependencies:
- dependency-name: YamlDotNet
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump Moq from 4.18.2 to 4.18.3

Bumps [Moq](https://github.com/moq/moq4) from 4.18.2 to 4.18.3.
- [Release notes](https://github.com/moq/moq4/releases)
- [Changelog](https://github.com/moq/moq4/blob/main/CHANGELOG.md)
- [Commits](https://github.com/moq/moq4/compare/v4.18.2...v4.18.3)

---
updated-dependencies:
- dependency-name: Moq
... (continued)

10732 of 20257 branches covered (0.0%)

Branch coverage included in aggregate %.

48141 of 48141 new or added lines in 1086 files covered. (100.0%)

30685 of 52388 relevant lines covered (58.57%)

7387.88 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

77.98
/Rdmp.Core/DataLoad/Engine/Pipeline/Destinations/DataTableUploadDestination.cs
1
// Copyright (c) The University of Dundee 2018-2019
2
// This file is part of the Research Data Management Platform (RDMP).
3
// RDMP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
4
// RDMP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
5
// You should have received a copy of the GNU General Public License along with RDMP. If not, see <https://www.gnu.org/licenses/>.
6

7
using System;
8
using System.Collections.Generic;
9
using System.Data;
10
using System.Diagnostics;
11
using System.Globalization;
12
using System.Linq;
13
using System.Threading.Tasks;
14
using FAnsi.Connections;
15
using FAnsi.Discovery;
16
using FAnsi.Discovery.TableCreation;
17
using Rdmp.Core.Curation.Data;
18
using Rdmp.Core.DataFlowPipeline;
19
using Rdmp.Core.DataFlowPipeline.Requirements;
20
using Rdmp.Core.Logging;
21
using Rdmp.Core.Logging.Listeners;
22
using Rdmp.Core.Repositories.Construction;
23
using Rdmp.Core.ReusableLibraryCode;
24
using Rdmp.Core.ReusableLibraryCode.Checks;
25
using Rdmp.Core.ReusableLibraryCode.DataAccess;
26
using Rdmp.Core.ReusableLibraryCode.Progress;
27
using TypeGuesser;
28

29
namespace Rdmp.Core.DataLoad.Engine.Pipeline.Destinations;
30

31
/// <summary>
32
/// Pipeline component (destination) which commits the DataTable(s) (in batches) to the DiscoveredDatabase (PreInitialize argument).  Supports cross platform
33
/// targets (MySql , Sql Server etc).  Normally the SQL Data Types and column names will be computed from the DataTable and a table will be created with the
34
/// name of the DataTable being processed.  If a matching table already exists you can choose to load it anyway in which case a basic bulk insert will take
35
/// place.
36
/// </summary>
37
public class DataTableUploadDestination : IPluginDataFlowComponent<DataTable>, IDataFlowDestination<DataTable>,
38
    IPipelineRequirement<DiscoveredDatabase>
39
{
40
    public const string LoggingServer_Description =
41
        "The logging server to log the upload to (leave blank to not bother auditing)";
42

43
    public const string AllowResizingColumnsAtUploadTime_Description =
44
        "If the target table being loaded has columns that are too small the destination will attempt to resize them";
45

46
    public const string AllowLoadingPopulatedTables_Description =
47
        "Normally when DataTableUploadDestination encounters a table that already contains records it will abandon the insertion attempt.  Set this to true to instead continue with the load.";
48

49
    public const string AlterTimeout_Description =
50
        "Timeout to perform all ALTER TABLE operations (column resize and PK creation)";
51

52
    [DemandsInitialization(LoggingServer_Description)]
53
    public ExternalDatabaseServer LoggingServer { get; set; }
288✔
54

55
    [DemandsInitialization(AllowResizingColumnsAtUploadTime_Description, DefaultValue = true)]
56
    public bool AllowResizingColumnsAtUploadTime { get; set; }
514✔
57

58
    [DemandsInitialization(AllowLoadingPopulatedTables_Description, DefaultValue = false)]
59
    public bool AllowLoadingPopulatedTables { get; set; }
55✔
60

61
    [DemandsInitialization(AlterTimeout_Description, DefaultValue = 300)]
62
    public int AlterTimeout { get; set; }
82✔
63

64
    [DemandsInitialization("Optional - Change system behaviour when a new table is being created by the component",
65
        TypeOf = typeof(IDatabaseColumnRequestAdjuster))]
66
    public Type Adjuster { get; set; }
257✔
67

68
    private CultureInfo _culture;
69

70
    [DemandsInitialization("The culture to use for uploading (determines date format etc)")]
71
    public CultureInfo Culture
72
    {
73
        get => _culture ?? CultureInfo.CurrentCulture;
191✔
74
        set => _culture = value;
2✔
75
    }
76

77
    public string TargetTableName { get; private set; }
1,708✔
78

79
    /// <summary>
80
    /// True if a new table was created or re-created by the execution of this destination.  False if
81
    /// the table already existed e.g. data was simply added
82
    /// </summary>
83
    public bool CreatedTable { get; private set; }
517✔
84

85
    private IBulkCopy _bulkcopy;
86
    private int _affectedRows;
87

88
    private Stopwatch swTimeSpentWriting = new();
195✔
89
    private Stopwatch swMeasuringStrings = new();
195✔
90

91
    private DiscoveredServer _loggingDatabaseSettings;
92

93
    private DiscoveredServer _server;
94
    private DiscoveredDatabase _database;
95

96
    private DataLoadInfo _dataLoadInfo;
97

98
    private IManagedConnection _managedConnection;
99
    private ToLoggingDatabaseDataLoadEventListener _loggingDatabaseListener;
100

101
    public List<DatabaseColumnRequest> ExplicitTypes { get; set; }
596✔
102

103
    private bool _firstTime = true;
195✔
104
    private HashSet<string> _primaryKey = new(StringComparer.CurrentCultureIgnoreCase);
195✔
105
    private DiscoveredTable _discoveredTable;
106

107
    //All column values sent to server so far
108
    private Dictionary<string, Guesser> _dataTypeDictionary;
109

110
    /// <summary>
111
    /// Optional function called when a name is needed for the table being uploaded (this overrides
112
    /// upstream components naming of tables - e.g. from file names).
113
    /// </summary>
114
    public Func<string> TableNamerDelegate { get; set; }
195✔
115

116
    public DataTableUploadDestination()
195✔
117
    {
118
        ExplicitTypes = new List<DatabaseColumnRequest>();
195✔
119
    }
195✔
120

121
    public DataTable ProcessPipelineData(DataTable toProcess, IDataLoadEventListener listener,
122
        GracefulCancellationToken cancellationToken)
123
    {
124
        if (toProcess == null)
247!
125
            return null;
×
126

127
        RemoveInvalidCharactersInSchema(toProcess);
247✔
128

129
        IDatabaseColumnRequestAdjuster adjuster = null;
247✔
130
        if (Adjuster != null) adjuster = (IDatabaseColumnRequestAdjuster)ObjectConstructor.Construct(Adjuster);
251✔
131

132
        //work out the table name for the table we are going to create
133
        if (TargetTableName == null)
247✔
134
        {
135
            if (TableNamerDelegate != null)
195!
136
            {
137
                TargetTableName = TableNamerDelegate();
×
138
                if (string.IsNullOrWhiteSpace(TargetTableName))
×
139
                    throw new Exception("No table name specified (TableNamerDelegate returned null)");
×
140
            }
141
            else if (string.IsNullOrWhiteSpace(toProcess.TableName))
195!
142
            {
143
                throw new Exception(
×
144
                    "Chunk did not have a TableName, did not know what to call the newly created table");
×
145
            }
146
            else
147
            {
148
                TargetTableName = QuerySyntaxHelper.MakeHeaderNameSensible(toProcess.TableName);
195✔
149
            }
150
        }
151

152
        ClearPrimaryKeyFromDataTableAndExplicitWriteTypes(toProcess);
247✔
153

154
        StartAuditIfExists(TargetTableName);
247✔
155

156
        if (_loggingDatabaseListener != null)
247!
157
            listener = new ForkDataLoadEventListener(listener, _loggingDatabaseListener);
×
158

159
        EnsureTableHasDataInIt(toProcess);
247✔
160

161
        CreatedTable = false;
243✔
162

163
        if (_firstTime)
243✔
164
        {
165
            var tableAlreadyExistsButEmpty = false;
191✔
166

167
            if (!_database.Exists())
191!
168
                throw new Exception($"Database {_database} does not exist");
×
169

170
            _discoveredTable = _database.ExpectTable(TargetTableName);
191✔
171

172
            //table already exists
173
            if (_discoveredTable.Exists())
191✔
174
            {
175
                tableAlreadyExistsButEmpty = true;
47✔
176

177
                if (!AllowLoadingPopulatedTables)
47✔
178
                    if (_discoveredTable.IsEmpty())
41!
179
                        listener.OnNotify(this, new NotifyEventArgs(ProgressEventType.Warning,
41✔
180
                            $"Found table {TargetTableName} already, normally this would forbid you from loading it (data duplication / no primary key etc) but it is empty so we are happy to load it, it will not be created"));
41✔
181
                    else
182
                        throw new Exception(
×
183
                            $"There is already a table called {TargetTableName} at the destination {_database}");
×
184

185
                if (AllowResizingColumnsAtUploadTime)
47✔
186
                    _dataTypeDictionary = _discoveredTable.DiscoverColumns().ToDictionary(k => k.GetRuntimeName(),
44✔
187
                        v => v.GetGuesser(), StringComparer.CurrentCultureIgnoreCase);
44✔
188
            }
189
            else
190
            {
191
                listener.OnNotify(this, new NotifyEventArgs(ProgressEventType.Information,
144✔
192
                    $"Determined that the table name {TargetTableName} is unique at destination {_database}"));
144✔
193
            }
194

195
            //create connection to destination
196
            if (!tableAlreadyExistsButEmpty)
191✔
197
            {
198
                CreatedTable = true;
144✔
199

200
                if (AllowResizingColumnsAtUploadTime)
144✔
201
                    _database.CreateTable(out _dataTypeDictionary, TargetTableName, toProcess, ExplicitTypes.ToArray(),
70✔
202
                        true, adjuster);
70✔
203
                else
204
                    _database.CreateTable(TargetTableName, toProcess, ExplicitTypes.ToArray(), true, adjuster);
74✔
205

206
                listener.OnNotify(this, new NotifyEventArgs(ProgressEventType.Information,
144✔
207
                    $"Created table {TargetTableName} successfully."));
144✔
208
            }
209

210
            _managedConnection = _server.BeginNewTransactedConnection();
191✔
211
            _bulkcopy = _discoveredTable.BeginBulkInsert(Culture, _managedConnection.ManagedTransaction);
191✔
212

213
            _firstTime = false;
191✔
214
        }
215

216
        try
217
        {
218
            if (AllowResizingColumnsAtUploadTime && !CreatedTable)
243✔
219
                ResizeColumnsIfRequired(toProcess, listener);
60✔
220

221
            //push the data
222
            swTimeSpentWriting.Start();
243✔
223

224
            _affectedRows += _bulkcopy.Upload(toProcess);
243✔
225

226
            swTimeSpentWriting.Stop();
204✔
227
            listener.OnProgress(this,
204✔
228
                new ProgressEventArgs($"Uploading to {TargetTableName}",
204✔
229
                    new ProgressMeasurement(_affectedRows, ProgressType.Records), swTimeSpentWriting.Elapsed));
204✔
230
        }
204✔
231
        catch (Exception e)
39✔
232
        {
233
            _managedConnection.ManagedTransaction.AbandonAndCloseConnection();
39✔
234

235
            if (LoggingServer != null)
39!
236
                _dataLoadInfo.LogFatalError(GetType().Name, ExceptionHelper.ExceptionToListOfInnerMessages(e, true));
×
237

238
            throw new Exception($"Failed to write rows (in transaction) to table {TargetTableName}", e);
39✔
239
        }
240

241

242
        _dataLoadInfo?.CloseAndMarkComplete();
204!
243
        return null;
204✔
244
    }
245

246
    private static void RemoveInvalidCharactersInSchema(DataTable toProcess)
247
    {
248
        var invalidSymbols = new[] { '.' };
247✔
249

250
        if (!string.IsNullOrWhiteSpace(toProcess.TableName) && invalidSymbols.Any(c => toProcess.TableName.Contains(c)))
488!
251
            foreach (var symbol in invalidSymbols)
×
252
                toProcess.TableName = toProcess.TableName.Replace(symbol.ToString(), "");
×
253

254
        foreach (DataColumn col in toProcess.Columns)
1,492✔
255
            if (!string.IsNullOrWhiteSpace(col.ColumnName) && invalidSymbols.Any(c => col.ColumnName.Contains(c)))
998✔
256
                foreach (var symbol in invalidSymbols)
16✔
257
                    col.ColumnName = col.ColumnName.Replace(symbol.ToString(), "");
4✔
258
    }
247✔
259

260

261
    /// <summary>
262
    /// Clears the primary key status of the DataTable / <see cref="ExplicitTypes"/>.  These are recorded in <see cref="_primaryKey"/> and applied at Dispose time
263
    /// in order that primary key in the destination database table does not interfere with ALTER statements (see <see cref="ResizeColumnsIfRequired"/>)
264
    /// </summary>
265
    /// <param name="toProcess"></param>
266
    private void ClearPrimaryKeyFromDataTableAndExplicitWriteTypes(DataTable toProcess)
267
    {
268
        //handle primary keyness by removing it until Dispose step
269
        foreach (var pkCol in toProcess.PrimaryKey.Select(dc => dc.ColumnName))
554✔
270
            _primaryKey.Add(pkCol);
20✔
271

272
        toProcess.PrimaryKey = Array.Empty<DataColumn>();
247✔
273

274
        //also get rid of any ExplicitTypes primary keys
275
        foreach (var dcr in ExplicitTypes.Where(dcr => dcr.IsPrimaryKey))
508!
276
        {
277
            dcr.IsPrimaryKey = false;
×
278
            _primaryKey.Add(dcr.ColumnName);
×
279
        }
280
    }
247✔
281

282
    private static void EnsureTableHasDataInIt(DataTable toProcess)
283
    {
284
        if (toProcess.Columns.Count == 0)
247✔
285
            throw new Exception($"DataTable '{toProcess}' had no Columns!");
2✔
286

287
        if (toProcess.Rows.Count == 0)
245✔
288
            throw new Exception($"DataTable '{toProcess}' had no Rows!");
2✔
289
    }
243✔
290

291
    private void ResizeColumnsIfRequired(DataTable toProcess, IDataLoadEventListener listener)
292
    {
293
        swMeasuringStrings.Start();
60✔
294

295
        var tbl = _database.ExpectTable(TargetTableName);
60✔
296
        var typeTranslater = tbl.GetQuerySyntaxHelper().TypeTranslater;
60✔
297

298
        //Get the current estimates from the datatype computer
299
        var oldTypes = _dataTypeDictionary.ToDictionary(k => k.Key,
172✔
300
            v => typeTranslater.GetSQLDBTypeForCSharpType(v.Value.Guess), StringComparer.CurrentCultureIgnoreCase);
172✔
301

302
        //columns in
303
        var sharedColumns = new List<string>();
60✔
304

305
        //for each destination column
306
        foreach (var col in _dataTypeDictionary.Keys)
344✔
307
            //if it appears in the toProcess DataTable
308
            if (toProcess.Columns.Contains(col))
112✔
309
                sharedColumns.Add(col); //it is a shared column
108✔
310

311
        //for each shared column adjust the corresponding computer for all rows
312
        Parallel.ForEach(sharedColumns, col =>
60✔
313
        {
60✔
314
            var guesser = _dataTypeDictionary[col];
108✔
315
            foreach (DataRow row in toProcess.Rows)
496✔
316
                guesser.AdjustToCompensateForValue(row[col]);
140✔
317
        });
168✔
318

319
        //see if any have changed
320
        foreach (DataColumn column in toProcess.Columns)
336✔
321
        {
322
            //get what is required for the current batch and the current type that is configured in the live table
323
            var oldSqlType = oldTypes[column.ColumnName];
108✔
324
            var newSqlType = typeTranslater.GetSQLDBTypeForCSharpType(_dataTypeDictionary[column.ColumnName].Guess);
108✔
325

326
            var changesMade = false;
108✔
327

328
            //if the SQL data type has degraded e.g. varchar(10) to varchar(50) or datetime to varchar(20)
329
            if (oldSqlType != newSqlType)
108✔
330
            {
331
                var col = tbl.DiscoverColumn(column.ColumnName, _managedConnection.ManagedTransaction);
64✔
332

333

334
                if (AbandonAlter(col.DataType.SQLType, newSqlType, out var reason))
64✔
335
                {
336
                    listener.OnNotify(this,
2✔
337
                        new NotifyEventArgs(ProgressEventType.Warning,
2✔
338
                            $"Considered resizing column '{column}' from '{col.DataType.SQLType}' to '{newSqlType}' but decided not to because:{reason}"));
2✔
339
                    continue;
2✔
340
                }
341

342
                listener.OnNotify(this, new NotifyEventArgs(ProgressEventType.Warning,
62✔
343
                    $"Resizing column '{column}' from '{col.DataType.SQLType}' to '{newSqlType}'"));
62✔
344

345
                //try changing the Type to the legit type
346
                col.DataType.AlterTypeTo(newSqlType, _managedConnection.ManagedTransaction, AlterTimeout);
62✔
347

348
                changesMade = true;
62✔
349
            }
350

351
            if (changesMade)
106✔
352
                _bulkcopy.InvalidateTableSchema();
62✔
353
        }
354

355
        swMeasuringStrings.Stop();
60✔
356
        listener.OnProgress(this,
60✔
357
            new ProgressEventArgs("Measuring DataType Sizes",
60✔
358
                new ProgressMeasurement(_affectedRows + toProcess.Rows.Count, ProgressType.Records),
60✔
359
                swMeasuringStrings.Elapsed));
60✔
360
    }
60✔
361

362
    /// <summary>
363
    /// Returns true if we should not be trying to do this alter after all
364
    /// </summary>
365
    /// <param name="oldSqlType">The database proprietary type you are considering altering from</param>
366
    /// <param name="newSqlType">The ANSI SQL type you are considering altering to</param>
367
    /// <param name="reason">Null or the reason we are returning true</param>
368
    /// <returns>True if the proposed alter is a bad idea and shouldn't be attempted</returns>
369
    protected virtual bool AbandonAlter(string oldSqlType, string newSqlType, out string reason)
370
    {
371
        var basicallyDecimalAlready = new List<string> { "real", "double", "float", "single" };
64✔
372

373
        var first = basicallyDecimalAlready.FirstOrDefault(c =>
64✔
374
            oldSqlType.Contains(c, StringComparison.InvariantCultureIgnoreCase));
314✔
375

376
        if (first != null && newSqlType.Contains("decimal", StringComparison.InvariantCultureIgnoreCase))
64✔
377
        {
378
            reason = $"Resizing from {first} to decimal is a bad idea and likely to fail";
2✔
379
            return true;
2✔
380
        }
381

382
        reason = null;
62✔
383
        return false;
62✔
384
    }
385

386
    public void Abort(IDataLoadEventListener listener)
387
    {
388
        _managedConnection.ManagedTransaction.AbandonAndCloseConnection();
×
389
    }
×
390

391
    public void Dispose(IDataLoadEventListener listener, Exception pipelineFailureExceptionIfAny)
392
    {
393
        try
394
        {
395
            if (_managedConnection != null)
195✔
396
            {
397
                //if there was an error
398
                if (pipelineFailureExceptionIfAny != null)
191✔
399
                {
400
                    _managedConnection.ManagedTransaction.AbandonAndCloseConnection();
39✔
401

402
                    listener.OnNotify(this,
39✔
403
                        new NotifyEventArgs(ProgressEventType.Information, "Transaction rolled back successfully"));
39✔
404

405
                    _bulkcopy?.Dispose();
39!
406
                }
407
                else
408
                {
409
                    _managedConnection.ManagedTransaction.CommitAndCloseConnection();
152✔
410

411
                    _bulkcopy?.Dispose();
152!
412

413
                    listener.OnNotify(this,
152✔
414
                        new NotifyEventArgs(ProgressEventType.Information, "Transaction committed successfully"));
152✔
415
                }
416
            }
417
        }
195✔
418
        catch (Exception e)
×
419
        {
420
            listener.OnNotify(this,
×
421
                new NotifyEventArgs(ProgressEventType.Error,
×
422
                    "Commit failed on transaction (probably there was a previous error?)", e));
×
423
        }
×
424

425
        //if we have a primary key to create
426
        if (pipelineFailureExceptionIfAny == null && _primaryKey?.Any() == true && _discoveredTable?.Exists() == true)
195!
427
        {
428
            //Find the columns in the destination
429
            var allColumns = _discoveredTable.DiscoverColumns();
16✔
430

431
            //if there are not yet any primary keys
432
            if (allColumns.All(c => !c.IsPrimaryKey))
32✔
433
            {
434
                //find the columns the user decorated in his DataTable
435
                var pkColumnsToCreate = allColumns.Where(c =>
16✔
436
                        _primaryKey.Any(pk => pk.Equals(c.GetRuntimeName(), StringComparison.CurrentCultureIgnoreCase)))
32✔
437
                    .ToArray();
16✔
438

439
                //make sure we found all of them
440
                if (pkColumnsToCreate.Length != _primaryKey.Count)
16!
441
                    throw new Exception(
×
442
                        $"Could not find primary key column(s) {string.Join(",", _primaryKey)} in table {_discoveredTable}");
×
443

444
                //create the primary key to match user provided columns
445
                _discoveredTable.CreatePrimaryKey(AlterTimeout, pkColumnsToCreate);
16✔
446
            }
447
        }
448

449
        EndAuditIfExists();
195✔
450
    }
195✔
451

452
    private void EndAuditIfExists()
453
    {
454
        //user is auditing
455
        _loggingDatabaseListener?.FinalizeTableLoadInfos();
195!
456
    }
×
457

458
    public void Check(ICheckNotifier notifier)
459
    {
460
        if (LoggingServer != null)
×
461
            new LoggingDatabaseChecker(LoggingServer).Check(notifier);
×
462
        else
463
            notifier.OnCheckPerformed(
×
464
                new CheckEventArgs(
×
465
                    "There is no logging server so there will be no audit of this destinations activities",
×
466
                    CheckResult.Success));
×
467
    }
×
468

469
    private void StartAuditIfExists(string tableName)
470
    {
471
        if (LoggingServer != null && _dataLoadInfo == null)
247!
472
        {
473
            _loggingDatabaseSettings = DataAccessPortal.ExpectServer(LoggingServer, DataAccessContext.Logging);
×
474
            var logManager = new LogManager(_loggingDatabaseSettings);
×
475
            logManager.CreateNewLoggingTaskIfNotExists("Internal");
×
476

477
            _dataLoadInfo = (DataLoadInfo)logManager.CreateDataLoadInfo("Internal", GetType().Name,
×
478
                $"Loading table {tableName}", "", false);
×
479
            _loggingDatabaseListener = new ToLoggingDatabaseDataLoadEventListener(logManager, _dataLoadInfo);
×
480
        }
481
    }
247✔
482

483
    public void PreInitialize(DiscoveredDatabase value, IDataLoadEventListener listener)
484
    {
485
        _database = value;
197✔
486
        _server = value.Server;
197✔
487
    }
197✔
488

489
    /// <summary>
490
    /// Declare that the column of name columnName (which might or might not appear in DataTables being uploaded) should always have the associated database type (e.g. varchar(59))
491
    /// The columnName is Case insensitive.  Note that if AllowResizingColumnsAtUploadTime is true then these datatypes are only the starting types and might get changed later to
492
    /// accomodate new data.
493
    /// </summary>
494
    /// <param name="columnName"></param>
495
    /// <param name="explicitType"></param>
496
    /// <param name="columnFlags"></param>
497
    /// <returns>The Column Request that has been added to the array</returns>
498
    public DatabaseColumnRequest AddExplicitWriteType(string columnName, string explicitType,
499
        ISupplementalColumnInformation columnFlags = null)
500
    {
501
        DatabaseColumnRequest columnRequest;
502

503
        if (columnFlags == null)
10!
504
        {
505
            columnRequest = new DatabaseColumnRequest(columnName, explicitType, true);
10✔
506
            ExplicitTypes.Add(columnRequest);
10✔
507
            return columnRequest;
10✔
508
        }
509

510
        columnRequest = new DatabaseColumnRequest(columnName, explicitType,
×
511
            !columnFlags.IsPrimaryKey && !columnFlags.IsAutoIncrement)
×
512
        {
×
513
            IsPrimaryKey = columnFlags.IsPrimaryKey,
×
514
            IsAutoIncrement = columnFlags.IsAutoIncrement,
×
515
            Collation = columnFlags.Collation
×
516
        };
×
517

518
        ExplicitTypes.Add(columnRequest);
×
519
        return columnRequest;
×
520
    }
521
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc