Exemple #1
0
        private ColumnWriterDetails GetDecimalColumnWriterDetails(bool isNullable, uint columnId,
                                                                  PropertyInfo propertyInfo, Func <object, decimal?> valueGetter)
        {
            //TODO add two options to configure Precision and Scale, via an attribute on the property, and via a fluent configuration source
            var precision = _defaultDecimalPrecision;
            var scale     = _defaultDecimalScale;

            var state        = new List <decimal?>();
            var columnWriter = new DecimalWriter(isNullable, _shouldAlignNumericValues, precision, scale,
                                                 _bufferFactory, columnId);

            return(new ColumnWriterDetails
            {
                PropertyName = propertyInfo.Name,
                ColumnWriter = columnWriter,
                AddValueToState = classInstance =>
                {
                    var value = valueGetter(classInstance);
                    state.Add(value);
                },
                WriteValuesFromState = () =>
                {
                    columnWriter.AddBlock(state);
                    state.Clear();
                },
                ColumnType = new ColumnType
                {
                    Kind = ColumnTypeKind.Decimal,
                    Precision = (uint)precision,
                    Scale = (uint)scale
                }
            });
        }
        ColumnWriterDetails GetDecimalColumnWriterDetails(bool isNullable, uint columnId, PropertyInfo propertyInfo, Func <object, decimal?> valueGetter, SerializationPropertyConfiguration propertyConfiguration)
        {
            var precision = propertyConfiguration?.DecimalPrecision ?? _defaultDecimalPrecision;
            var scale     = propertyConfiguration?.DecimalScale ?? _defaultDecimalScale;

            var state        = new List <decimal?>();
            var columnWriter = new DecimalWriter(isNullable, _shouldAlignNumericValues, precision, scale, _bufferFactory, columnId);

            return(new ColumnWriterDetails
            {
                PropertyName = propertyInfo.Name,
                ColumnWriter = columnWriter,
                AddValueToState = classInstance =>
                {
                    var value = valueGetter(classInstance);
                    state.Add(value);
                },
                WriteValuesFromState = () =>
                {
                    columnWriter.AddBlock(state);
                    state.Clear();
                },
                ColumnType = new Protocol.ColumnType
                {
                    Kind = Protocol.ColumnTypeKind.Decimal,
                    Precision = (uint)precision,
                    Scale = (uint)scale
                }
            });
        }
        public void FormatValue(StringBuilder queryStringBuilder, object?value)
        {
            if (_precision == null && _scale == null)
            {
                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $"Both scale and precision are required for the type \"{TypeName}\".");
            }

            if (_scale == null)
            {
                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $"Scale is required for the type \"{TypeName}\".");
            }

            if (_precision == null)
            {
                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $"Precision is required for the type \"{TypeName}\".");
            }

            if (value == null || value is DBNull)
            {
                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $"The ClickHouse type \"{ComplexTypeName}\" does not allow null values");
            }

            decimal outputValue = value switch
            {
                decimal theValue => theValue,
                long theValue => theValue,
                ulong theValue => theValue,
                int theValue => theValue,
                uint theValue => theValue,
                short theValue => theValue,
                ushort theValue => theValue,
                sbyte theValue => theValue,
                byte theValue => theValue,
                    _ => throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $"The type \"{value.GetType()}\" can't be converted to the ClickHouse type \"{ComplexTypeName}\"."),
            };

            var         writer      = new DecimalWriter("Value", ComplexTypeName, _precision.Value, _scale.Value, new[] { outputValue });
            var         elementSize = GetElementSize(_precision.Value);
            Span <byte> buffer      = stackalloc byte[elementSize];
            var         writeSize   = writer.WriteNext(buffer);

            if (writeSize.Bytes != elementSize)
            {
                throw new ClickHouseException(ClickHouseErrorCodes.InternalError, "Internal error. DecimalWrite did not write expected amount of bytes.");
            }

            // Not all decimal values can be parsed:
            // > Real value ranges that can be stored in memory are a bit larger than specified above, which are checked only on conversion from a string.
            // But reinterpret_cast lets obtain any Decimal value
            queryStringBuilder.Append("reinterpret('");
            foreach (var byteValue in buffer)
            {
                queryStringBuilder.Append("\\x");
                queryStringBuilder.Append(HexDigits[byteValue >> 4]);
                queryStringBuilder.Append(HexDigits[byteValue & 0xF]);
            }
            queryStringBuilder.Append("', 'Decimal");
            switch (elementSize)
            {
            case 4:
                queryStringBuilder.Append("32");
                break;

            case 8:
                queryStringBuilder.Append("64");
                break;

            case 16:
                queryStringBuilder.Append("128");
                break;

            default:
                throw new ClickHouseException(ClickHouseErrorCodes.InternalError, "Internal error. Invalid element size.");
            }
            queryStringBuilder.Append("(");
            queryStringBuilder.Append(_scale.Value.ToString(CultureInfo.InvariantCulture));
            queryStringBuilder.Append(")')");
        }