Example #1
0
            public IClickHouseTableColumn EndRead(ClickHouseColumnSettings?settings)
            {
                var column     = _underlyingReader.EndRead(settings);
                var dispatcher = new MapColumnDispatcher(column);

                return(TypeDispatcher.Dispatch(_fieldType, dispatcher));
            }
        internal ITypeDispatcher?GetColumnTypeDispatcher()
        {
            if (ColumnType == null)
            {
                return(null);
            }

            return(_columnTypeDispatcher ??= TypeDispatcher.Create(ColumnType));
        }
        private async ValueTask WriteRow(IReadOnlyCollection <object?> values, bool async, CancellationToken cancellationToken)
        {
            if (values == null)
            {
                throw new ArgumentNullException(nameof(values));
            }

            if (values.Count != _columns.Count)
            {
                throw new ArgumentException("The number of values must be equal to the number of columns.");
            }

            var columnWriters = new List <IClickHouseColumnWriter>(_columns.Count);

            foreach (var value in values)
            {
                int i = columnWriters.Count;

                var columnInfo = _columns[i];
                SingleRowColumnWriterDispatcher dispatcher;
                Type valueType;
                if (value != null && !(value is DBNull))
                {
                    dispatcher = new SingleRowColumnWriterDispatcher(value, columnInfo, _columnSettings?[i]);
                    valueType  = value.GetType();
                }
                else if (columnInfo.TypeInfo.TypeName != "Nullable")
                {
                    throw new ClickHouseException(ClickHouseErrorCodes.ColumnMismatch, $"The column \"{columnInfo.Name}\" at the position {i} doesn't support nulls.");
                }
                else
                {
                    dispatcher = new SingleRowColumnWriterDispatcher(null, columnInfo, _columnSettings?[i]);
                    valueType  = columnInfo.TypeInfo.GetFieldType();
                }

                IClickHouseColumnWriter columnWriter;
                try
                {
                    columnWriter = TypeDispatcher.Dispatch(valueType, dispatcher);
                }
                catch (ClickHouseException ex)
                {
                    throw new ClickHouseException(ex.ErrorCode, $"Column \"{columnInfo.Name}\" (position {i}): {ex.Message}", ex);
                }

                columnWriters.Add(columnWriter);
            }

            var table = new ClickHouseTableWriter(string.Empty, 1, columnWriters);

            await SendTable(table, async, cancellationToken);
        }
Example #4
0
        static ClickHouseParameterTests()
        {
            var properties = new Dictionary <string, Action <ClickHouseParameter, ClickHouseParameter> >();

            foreach (var property in typeof(ClickHouseParameter).GetProperties(BindingFlags.Public | BindingFlags.Instance))
            {
                if (property.GetMethod == null || property.SetMethod == null || !property.GetMethod.IsPublic || !property.SetMethod.IsPublic)
                {
                    continue;
                }

                var comparer = TypeDispatcher.Dispatch(property.PropertyType, new PropertyComparerDispatcher(property));
                properties.Add(property.Name, comparer);
            }

            ParameterPublicProperties = new ReadOnlyDictionary <string, Action <ClickHouseParameter, ClickHouseParameter> >(properties);
        }
Example #5
0
        public IClickHouseColumnWriter CreateColumnWriter <T>(string columnName, IReadOnlyList <T> rows, ClickHouseColumnSettings?columnSettings)
        {
            if (_elementTypeInfo == null)
            {
                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $"The type \"{ComplexTypeName}\" is not fully specified.");
            }

            Type?elementType = null;

            foreach (var genericItf in typeof(T).GetInterfaces().Where(itf => itf.IsGenericType))
            {
                if (genericItf.GetGenericTypeDefinition() != typeof(IReadOnlyList <>))
                {
                    continue;
                }

                if (elementType == null)
                {
                    elementType = genericItf.GetGenericArguments()[0];
                }
                else
                {
                    var elementTypeCandidate = genericItf.GetGenericArguments()[0];

                    if (elementType.IsAssignableFrom(elementTypeCandidate))
                    {
                        elementType = elementTypeCandidate;
                    }
                    else if (!elementTypeCandidate.IsAssignableFrom(elementType))
                    {
                        throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $"Can't detect a type of the array's element. Candidates are: \"{elementType}\" and \"{elementTypeCandidate}\".");
                    }
                }
            }

            if (elementType == null)
            {
                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $"Can't detect a type of the array's element. The type \"{typeof(T)}\" doesn't implement \"{typeof(IReadOnlyList<>)}\".");
            }

            var dispatcher = new ArrayColumnWriterDispatcher(columnName, ComplexTypeName, rows, columnSettings, _elementTypeInfo);

            return(TypeDispatcher.Dispatch(elementType, dispatcher));
        }
Example #6
0
            public IClickHouseTableColumn EndRead(ClickHouseColumnSettings?settings)
            {
                var elementColumnReader = _elementColumnReader ?? _elementType.CreateColumnReader(0);
                var column = elementColumnReader.EndRead(settings);
                var ranges = _position == _ranges.Count ? _ranges : _ranges.Take(_position).ToList();

                var  columnType            = column.GetType();
                Type?recognizedElementType = null;

                foreach (var itf in columnType.GetInterfaces().Where(i => i.IsGenericType))
                {
                    var typeDef = itf.GetGenericTypeDefinition();
                    if (typeDef != typeof(IClickHouseTableColumn <>))
                    {
                        continue;
                    }

                    if (recognizedElementType == null)
                    {
                        recognizedElementType = itf.GenericTypeArguments[0];
                    }
                    else
                    {
                        recognizedElementType = null;
                        break;
                    }
                }

                if (recognizedElementType != null)
                {
                    var reinterpretedColumn = TypeDispatcher.Dispatch(recognizedElementType, new ArrayTableColumnTypeDispatcher(column, ranges));
                    if (reinterpretedColumn != null)
                    {
                        return(reinterpretedColumn);
                    }
                }

                return(new ArrayTableColumn(column, ranges));
            }
Example #7
0
        public IClickHouseColumnWriter CreateColumnWriter <T>(string columnName, IReadOnlyList <T> rows, ClickHouseColumnSettings?columnSettings)
        {
            if (_elementTypeInfo == null)
            {
                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $"The type \"{ComplexTypeName}\" is not fully specified.");
            }

            var  rowType     = typeof(T);
            Type?elementType = null;

            if (rowType.IsArray)
            {
                var rank = rowType.GetArrayRank();
                if (rank > 1)
                {
                    elementType = rowType.GetElementType() !;
                    Debug.Assert(elementType != null);

                    var listAdapterInfo = MultiDimensionalArrayReadOnlyListAdapter.Dispatch(elementType, rowType.GetArrayRank());
                    var mdaDispatcher   = new MultiDimensionalArrayColumnWriterDispatcher(
                        columnName,
                        (IReadOnlyList <Array>)rows,
                        columnSettings,
                        _elementTypeInfo,
                        listAdapterInfo.createList);

                    return(TypeDispatcher.Dispatch(listAdapterInfo.listElementType, mdaDispatcher));
                }
            }

            foreach (var genericItf in rowType.GetInterfaces().Where(itf => itf.IsGenericType))
            {
                if (genericItf.GetGenericTypeDefinition() != typeof(IReadOnlyList <>))
                {
                    continue;
                }

                if (elementType == null)
                {
                    elementType = genericItf.GetGenericArguments()[0];
                }
                else
                {
                    var elementTypeCandidate = genericItf.GetGenericArguments()[0];

                    if (elementType.IsAssignableFrom(elementTypeCandidate))
                    {
                        elementType = elementTypeCandidate;
                    }
                    else if (!elementTypeCandidate.IsAssignableFrom(elementType))
                    {
                        throw new ClickHouseException(
                                  ClickHouseErrorCodes.TypeNotSupported,
                                  $"Can't detect a type of the array's element. Candidates are: \"{elementType}\" and \"{elementTypeCandidate}\".");
                    }
                }
            }

            ArrayColumnWriterDispatcherBase dispatcher;

            if (elementType == null)
            {
                var rowGenericTypeDef = rowType.GetGenericTypeDefinition();
                if (rowGenericTypeDef == typeof(ReadOnlyMemory <>))
                {
                    elementType = rowType.GetGenericArguments()[0] !;
                    dispatcher  = new ReadOnlyColumnWriterDispatcher(columnName, rows, columnSettings, _elementTypeInfo);
                }
                else if (rowGenericTypeDef == typeof(Memory <>))
                {
                    elementType = rowType.GetGenericArguments()[0] !;
                    dispatcher  = new MemoryColumnWriterDispatcher(columnName, rows, columnSettings, _elementTypeInfo);
                }
                else
                {
                    throw new ClickHouseException(
                              ClickHouseErrorCodes.TypeNotSupported,
                              $"Can't detect a type of the array's element. The type \"{typeof(T)}\" doesn't implement \"{typeof(IReadOnlyList<>)}\".");
                }
            }
            else
            {
                dispatcher = new ArrayColumnWriterDispatcher(columnName, rows, columnSettings, _elementTypeInfo);
            }

            try
            {
                return(TypeDispatcher.Dispatch(elementType, dispatcher));
            }
            catch (ClickHouseException ex) when(ex.ErrorCode == ClickHouseErrorCodes.TypeNotSupported)
            {
                throw new ClickHouseException(ex.ErrorCode, $"The type \"{rowType}\" can't be converted to the ClickHouse type \"{ComplexTypeName}\". See the inner exception for details.", ex);
            }
        }
        private async ValueTask WriteTable(IReadOnlyList <object?> columns, int rowCount, bool async, CancellationToken cancellationToken)
        {
            if (columns == null)
            {
                throw new ArgumentNullException(nameof(columns));
            }
            if (columns.Count != _columns.Count)
            {
                throw new ArgumentException("The number of columns for writing must be equal to the number of columns in the table.", nameof(columns));
            }
            if (rowCount < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(rowCount));
            }
            if (rowCount == 0)
            {
                throw new ArgumentException("The number of rows must be grater than zero.", nameof(rowCount));
            }

            if (IsClosed)
            {
                throw new ClickHouseException(ClickHouseErrorCodes.InvalidConnectionState, "The writer is closed.");
            }

            var writers = new List <IClickHouseColumnWriter>(_columns.Count);

            for (int i = 0; i < _columns.Count; i++)
            {
                var column     = columns[i];
                var columnInfo = _columns[i];

                if (column == null)
                {
                    if (!columnInfo.TypeInfo.TypeName.StartsWith("Nullable"))
                    {
                        throw new ClickHouseException(ClickHouseErrorCodes.ColumnMismatch, $"The column \"{columnInfo.Name}\" at the position {i} doesn't support nulls.");
                    }

                    var constColumn = TypeDispatcher.Dispatch(columnInfo.TypeInfo.GetFieldType(), new NullColumnWriterDispatcher(columnInfo, _columnSettings?[i], rowCount));
                    writers.Add(constColumn);
                    continue;
                }

                var columnType = column.GetType();

                bool isEnumerable = false;
                Type?enumerable = null, asyncEnumerable = null, readOnlyList = null, list = null;
                foreach (var ifs in columnType.GetInterfaces())
                {
                    if (ifs == typeof(IEnumerable))
                    {
                        isEnumerable = true;
                    }
                    else if (ifs.IsGenericType)
                    {
                        var ifsDefinition = ifs.GetGenericTypeDefinition();
                        if (ifsDefinition == typeof(IEnumerable <>))
                        {
                            enumerable ??= ifs;
                        }
                        else if (ifsDefinition == typeof(IAsyncEnumerable <>))
                        {
                            asyncEnumerable ??= ifs;
                        }
                        else if (ifsDefinition == typeof(IReadOnlyList <>))
                        {
                            readOnlyList ??= ifs;
                        }
                        else if (ifsDefinition == typeof(IList <>))
                        {
                            list ??= ifs;
                        }
                    }
                }

                Type dispatchedElementType;
                if (readOnlyList != null)
                {
                    dispatchedElementType = readOnlyList.GetGenericArguments()[0];
                }
                else if (list != null)
                {
                    dispatchedElementType = list.GetGenericArguments()[0];
                }
                else
                {
                    if (asyncEnumerable != null)
                    {
                        if (async)
                        {
                            var genericArg      = asyncEnumerable.GetGenericArguments()[0];
                            var asyncDispatcher = new AsyncColumnWriterDispatcher(column, columnInfo, _columnSettings?[i], rowCount, i, cancellationToken);
                            var asyncColumn     = await TypeDispatcher.Dispatch(genericArg, asyncDispatcher);

                            writers.Add(asyncColumn);
                            continue;
                        }

                        if (!isEnumerable && enumerable == null)
                        {
                            throw new ClickHouseException(
                                      ClickHouseErrorCodes.ColumnMismatch,
                                      $"The column \"{columnInfo.Name}\" at the position {i} implements interface \"{asyncEnumerable}\". Call async method \"{nameof(WriteTableAsync)}\".");
                        }
                    }

                    if (enumerable != null)
                    {
                        dispatchedElementType = enumerable.GetGenericArguments()[0];
                    }
                    else if (isEnumerable)
                    {
                        dispatchedElementType = columnInfo.TypeInfo.GetFieldType();
                    }
                    else
                    {
                        throw new ClickHouseException(ClickHouseErrorCodes.ColumnMismatch, $"The column \"{columnInfo.Name}\" at the position {i} is not a collection.");
                    }
                }

                var dispatcher   = new ColumnWriterDispatcher(column, columnInfo, _columnSettings?[i], rowCount, i);
                var columnWriter = TypeDispatcher.Dispatch(dispatchedElementType, dispatcher);
                writers.Add(columnWriter);
            }

            var table = new ClickHouseTableWriter(string.Empty, rowCount, writers);

            await SendTable(table, async, cancellationToken);
        }