/// <summary> /// Gets the column information based on the SQL Source, but overwritten with the definitions /// </summary> /// <param name="generalFormat"> /// general value format for not explicitly specified columns format /// </param> /// <param name="columnDefinitions"></param> /// <param name="sourceSchemaDataReader">The reader for the source.</param> /// <returns></returns> /// <exception cref="ArgumentNullException">reader</exception> public static IEnumerable <IColumn> GetColumnInformation(IValueFormat generalFormat, IReadOnlyCollection <IColumn> columnDefinitions, DataTable schemaTable) { if (schemaTable == null) { throw new ArgumentNullException(nameof(schemaTable)); } var result = new List <WriterColumn>(); var colName = new BiDirectionalDictionary <int, string>(); // Make names unique and fill the dictionary foreach (DataRow schemaRow in schemaTable.Rows) { var colNo = (int)schemaRow[SchemaTableColumn.ColumnOrdinal]; var newName = StringUtils.MakeUniqueInCollection(colName.Values, schemaRow[SchemaTableColumn.ColumnName].ToString()); colName.Add(colNo, newName); } foreach (DataRow schemaRow in schemaTable.Rows) { var colNo = (int)schemaRow[SchemaTableColumn.ColumnOrdinal]; var column = columnDefinitions.FirstOrDefault(x => x.Name.Equals(colName[colNo], StringComparison.OrdinalIgnoreCase)); if (column != null && column.Ignore) { continue; } // Based on the data Type in the reader defined and the general format create the value format var valueFormat = column?.ValueFormat ?? new ImmutableValueFormat( ((Type)schemaRow[SchemaTableColumn.DataType]).GetDataType(), generalFormat.DateFormat, generalFormat.DateSeparator, generalFormat.TimeSeparator, generalFormat.NumberFormat, generalFormat.GroupSeparator, generalFormat.DecimalSeparator, generalFormat.True, generalFormat.False, generalFormat.DisplayNullAs); var fieldLength = Math.Max((int)schemaRow[SchemaTableColumn.ColumnSize], 0); switch (valueFormat.DataType) { case DataType.Integer: fieldLength = 10; break; case DataType.Boolean: { var lenTrue = valueFormat.True.Length; var lenFalse = valueFormat.False.Length; fieldLength = lenTrue > lenFalse ? lenTrue : lenFalse; break; } case DataType.Double: case DataType.Numeric: fieldLength = 28; break; case DataType.DateTime: fieldLength = valueFormat.DateFormat.Length; break; case DataType.Guid: fieldLength = 36; break; case DataType.String: case DataType.TextToHtml: case DataType.TextToHtmlFull: case DataType.TextPart: break; default: throw new ArgumentOutOfRangeException(); } var constantTimeZone = string.Empty; var columnOrdinalTimeZoneReader = -1; // the timezone information if (column != null) { var tz = column.TimeZonePart; if (!string.IsNullOrEmpty(tz)) { var tzInfo = tz.GetPossiblyConstant(); if (tzInfo.Item2) { constantTimeZone = tzInfo.Item1; } else { if (colName.TryGetByValue(tzInfo.Item1, out var ordinal)) { columnOrdinalTimeZoneReader = ordinal; } } } } var ci = new WriterColumn(colName[colNo], colNo, valueFormat, fieldLength, constantTimeZone, columnOrdinalTimeZoneReader); result.Add(ci); // add an extra column for the time, reading columns they get combined, writing them they // get separated again if (column == null || string.IsNullOrEmpty(column.TimePart) || colName.ContainsValue(column.TimePart)) { continue; } if (ci.ValueFormat.DateFormat.IndexOfAny(new[] { 'h', 'H', 'm', 's' }) != -1) { Logger.Warning( $"'{ci.Name}' will create a separate time column '{column.TimePart}' but seems to write time itself '{ci.ValueFormat.DateFormat}'"); } // In case we have a split column, add the second column (unless the column is also present result.Add(new WriterColumn(column.TimePart, colNo, new ImmutableValueFormat(DataType.DateTime, column.TimePartFormat, timeSeparator: column.ValueFormat.TimeSeparator), column.TimePartFormat.Length, constantTimeZone, columnOrdinalTimeZoneReader)); } return(result); }