public IClickHouseColumnTypeInfo GetDetailedTypeInfo(List <ReadOnlyMemory <char> > options, IClickHouseTypeInfoProvider typeInfoProvider) { if (_elementTypes != null) { throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, "The type is already fully specified."); } var complexTypeNameBuilder = new StringBuilder(TypeName).Append('('); var elementTypes = new List <IClickHouseColumnTypeInfo>(options.Count); List <string>?elementNames = null; foreach (var option in options) { if (elementTypes.Count > 0) { complexTypeNameBuilder.Append(", "); } var identifierLen = ClickHouseSyntaxHelper.GetIdentifierLiteralLength(option.Span); if (identifierLen == option.Span.Length) { identifierLen = -1; } if (identifierLen < 0) { if (elementNames != null) { throw new ClickHouseException(ClickHouseErrorCodes.InvalidTypeName, "A tuple can be either named or not. Mixing of named and unnamed arguments is not allowed."); } var typeInfo = typeInfoProvider.GetTypeInfo(option); elementTypes.Add(typeInfo); } else { if (elementNames == null) { if (elementTypes.Count > 0) { throw new ClickHouseException(ClickHouseErrorCodes.InvalidTypeName, "A tuple can be either named or not. Mixing of named and unnamed arguments is not allowed."); } elementNames = new List <string>(options.Count); } var name = ClickHouseSyntaxHelper.GetIdentifier(option.Span.Slice(0, identifierLen)); var typeInfo = typeInfoProvider.GetTypeInfo(option.Slice(identifierLen + 1)); elementTypes.Add(typeInfo); elementNames.Add(name); complexTypeNameBuilder.Append(option.Slice(0, identifierLen)).Append(' '); } complexTypeNameBuilder.Append(elementTypes[^ 1].ComplexTypeName);
public IClickHouseColumnTypeInfo GetDetailedTypeInfo(List <ReadOnlyMemory <char> > options, IClickHouseTypeInfoProvider typeInfoProvider) { var parsedOptions = new List <KeyValuePair <string, TValue> >(options.Count); var complexNameBuilder = new StringBuilder(TypeName).Append('('); bool isFirst = true; foreach (var option in options) { if (isFirst) { isFirst = false; } else { complexNameBuilder.Append(", "); } var keyStrLen = ClickHouseSyntaxHelper.GetSingleQuoteStringLength(option.Span); if (keyStrLen < 0) { throw new ClickHouseException(ClickHouseErrorCodes.InvalidTypeName, $"The fragment \"{option}\" is not recognized as an item of the enum."); } var key = ClickHouseSyntaxHelper.GetSingleQuoteString(option.Slice(0, keyStrLen).Span); var valuePart = option.Slice(keyStrLen); var eqSignIdx = valuePart.Span.IndexOf('='); if (eqSignIdx < 0) { throw new ClickHouseException(ClickHouseErrorCodes.InvalidTypeName, $"The fragment \"{option}\" is not recognized as an item of the enum."); } valuePart = valuePart.Slice(eqSignIdx + 1).Trim(); if (!TryParse(valuePart.Span, out var value)) { throw new ClickHouseException(ClickHouseErrorCodes.InvalidTypeName, $"The value {valuePart} is not a valid value of {TypeName}."); } complexNameBuilder.Append(option); parsedOptions.Add(new KeyValuePair <string, TValue>(key, value)); } var complexName = complexNameBuilder.Append(')').ToString(); return(CreateDetailedTypeInfo(complexName, parsedOptions)); }
private static (ReadOnlyMemory <char> baseTypeName, List <ReadOnlyMemory <char> >?options) ParseTypeName(ReadOnlyMemory <char> typeName) { var typeNameSpan = typeName.Span; var pOpenIdx = typeNameSpan.IndexOf('('); if (pOpenIdx == 0) { throw new ClickHouseException(ClickHouseErrorCodes.InvalidTypeName, $"The name of the type (\"{typeNameSpan.ToString()}\") can't start with \"(\"."); } ReadOnlyMemory <char> baseTypeName; List <ReadOnlyMemory <char> >?options = null; if (pOpenIdx < 0) { baseTypeName = typeName.Trim(); } else { baseTypeName = typeName.Slice(0, pOpenIdx).Trim(); int count = 1; int currentIdx = pOpenIdx; int optionStartIdx = pOpenIdx + 1; ReadOnlySpan <char> significantChars = "(,)'`"; do { if (typeNameSpan.Length - 1 == currentIdx) { break; } var pNextIdx = typeNameSpan.Slice(currentIdx + 1).IndexOfAny(significantChars); if (pNextIdx < 0) { break; } pNextIdx += currentIdx + 1; currentIdx = pNextIdx; if ("'`".Contains(typeNameSpan[currentIdx])) { var len = ClickHouseSyntaxHelper.GetQuotedTokenLength(typeNameSpan.Slice(currentIdx), typeNameSpan[currentIdx]); if (len < 0) { break; } Debug.Assert(len > 0); currentIdx += len - 1; } else if (typeNameSpan[currentIdx] == '(') { ++count; } else if (typeNameSpan[currentIdx] == ')') { --count; if (count == 0) { break; } } else if (count == 1) { var currentOption = typeName.Slice(optionStartIdx, currentIdx - optionStartIdx).Trim(); optionStartIdx = currentIdx + 1; if (options != null) { options.Add(currentOption); } else { options = new List <ReadOnlyMemory <char> >(2) { currentOption } }; } } while (true); if (count != 0) { throw new ClickHouseException(ClickHouseErrorCodes.InvalidTypeName, $"The number of open parentheses doesn't match to the number of close parentheses in the type name \"{typeNameSpan.ToString()}\"."); } if (currentIdx != typeNameSpan.Length - 1) { var unexpectedString = typeNameSpan.Slice(currentIdx + 1); if (!unexpectedString.Trim().IsEmpty) { throw new ClickHouseException( ClickHouseErrorCodes.InvalidTypeName, $"There are unexpected characters (\"{unexpectedString.ToString()}\") in the type name \"{typeNameSpan.ToString()}\" after closing parenthesis."); } } var lastOption = typeName.Slice(optionStartIdx, currentIdx - optionStartIdx).Trim(); if (options != null) { options.Add(lastOption); } else { options = new List <ReadOnlyMemory <char> >(1) { lastOption } }; } return(baseTypeName, options); }