Преобразования типов
Клиент стремится быть максимально гибким в плане поддержки типов переменных — как для вставки, так и для маршалинга ответов. В большинстве случаев для типа столбца ClickHouse существует эквивалентный тип Golang, например UInt64 и uint64. Такие логические соответствия должны поддерживаться всегда. Также могут использоваться типы переменных, которые можно вставлять в столбцы или применять для получения ответа, если предварительно выполняется преобразование переменной или полученных данных. Клиент стремится поддерживать такие преобразования прозрачно, чтобы пользователям не приходилось заранее точно приводить данные к нужному типу перед вставкой, а также чтобы обеспечить гибкий маршалинг во время выполнения запроса. При этом такие прозрачные преобразования не допускают потери точности. Например,uint32 нельзя использовать для получения данных из столбца UInt64. И наоборот, строку можно вставить в поле DateTime64, если она соответствует требованиям формата.
Поддерживаемые в настоящее время преобразования типов для примитивных типов собраны здесь.
Эта работа продолжается и может быть разделена на вставку (Append/AppendRow) и чтение (через Scan). Если вам нужна поддержка определённого преобразования, создайте issue.
Стандартный интерфейс database/sql должен поддерживать те же типы, что и API ClickHouse. Есть несколько исключений, в основном для сложных типов; они описаны в разделах ниже. Как и API ClickHouse, клиент стремится быть максимально гибким в плане поддержки типов переменных — как для вставки, так и для маршалинга ответов.
Сложные типы
Date/DateTime
Клиент Go для ClickHouse поддерживает типы даты и даты/времениDate, Date32, DateTime и DateTime64. Даты можно вставлять как строки в формате 2006-01-02 или с помощью встроенных типов Go time.Time{} и sql.NullTime. Для DateTime также поддерживаются эти типы, но строки должны передаваться в формате 2006-01-02 15:04:05 с необязательным смещением часового пояса, например 2006-01-02 15:04:05 +08:00. При чтении также поддерживаются time.Time{} и sql.NullTime, а также любая реализация интерфейса sql.Scanner.
Обработка информации о часовом поясе зависит от типа ClickHouse и от того, вставляется значение или читается:
- DateTime/DateTime64
- Во время вставки значение отправляется в ClickHouse в формате Unix-временной метки. Если часовой пояс не указан, клиент будет считать, что используется локальный часовой пояс клиента.
time.Time{}илиsql.NullTimeбудут соответствующим образом преобразованы в время эпохи Unix. - Во время чтения при возврате значения
time.Timeбудет использоваться часовой пояс столбца, если он задан. В противном случае будет использоваться часовой пояс сервера.
- Во время вставки значение отправляется в ClickHouse в формате Unix-временной метки. Если часовой пояс не указан, клиент будет считать, что используется локальный часовой пояс клиента.
- Date/Date32
- Во время вставки при преобразовании даты в Unix-временную метку учитывается ее часовой пояс, то есть перед сохранением как даты к ней применяется соответствующее смещение часового пояса, поскольку типы
Dateв ClickHouse не содержат информации о локали. Если в строковом значении он не указан, будет использоваться локальный часовой пояс. - Во время чтения даты, считанные в экземпляры
time.Time{}илиsql.NullTime{}, возвращаются без информации о часовом поясе.
- Во время вставки при преобразовании даты в Unix-временную метку учитывается ее часовой пояс, то есть перед сохранением как даты к ней применяется соответствующее смещение часового пояса, поскольку типы
Типы Time/Time64
Типы столбцовTime и Time64 хранят значения времени суток без компонента даты. Оба сопоставляются с типом Go time.Duration.
Timeхранит время с точностью до секунды.Time64(precision)поддерживает точность меньше секунды (какDateTime64), гдеprecisionпринимает значения от 0 до 9.
Array
Значения типа Array следует вставлять как срезы. Правила для типов элементов такие же, как и для примитивного типа, то есть по возможности элементы будут преобразованы. При вызове Scan следует передавать указатель на срез.Map
Значения типа Map следует вставлять в виде map в Go, при этом ключи и значения должны соответствовать правилам преобразования типов, определённым ранее.При использовании API database/sql для значений Map требуется строгая типизация —
interface{} нельзя использовать в качестве типа значения. Например, для поля Map(String,String) нельзя передать map[string]interface{}; вместо него нужно использовать map[string]string. Переменная типа interface{} при этом всегда будет совместимой и может использоваться для более сложных структур.Полный примерTuple
Тип Tuple представляет собой группу столбцов произвольной длины. Столбцы могут быть либо явно именованными, либо можно указать только их тип, например.Nested
Поле Nested эквивалентно Array из именованных Tuple. Использование зависит от того, установил ли пользователь для flatten_nested значение 1 или 0. Если установить flatten_nested в 0, столбцы Nested останутся единым массивом Tuple. Это позволяет использовать срезы map для вставки и извлечения, а также произвольные уровни вложенности. Ключ map должен совпадать с именем столбца, как показано в примере ниже. Примечание: поскольку map представляют Tuple, они должны иметь типmap[string]interface{}. В настоящее время значения не имеют строгой типизации.
flatten_tested=0
Если для flatten_nested используется значение по умолчанию, равное 1, вложенные столбцы преобразуются в отдельные массивы. Для вставки и чтения данных в этом случае нужно использовать вложенные срезы. Хотя произвольные уровни вложенности могут работать, официально это не поддерживается.
flatten_nested=1
Примечание: столбцы Nested должны иметь одинаковую размерность. Например, в приведённом выше примере Col_2_2 и Col_2_1 должны содержать одинаковое количество элементов.
Благодаря более простому интерфейсу и официальной поддержке вложенных структур мы рекомендуем flatten_nested=0.
Гео-типы
Клиент поддерживает гео-типы Point, Ring, LineString, Polygon, MultiPolygon и MultiLineString. В Go эти типы представлены с помощью пакета github.com/paulmach/orb.UUID
Тип UUID поддерживается пакетом github.com/google/uuid. Вы также можете отправлять и сериализовать UUID как строку или как любой тип, реализующийsql.Scanner или Stringify.
Decimal
Поскольку в Go нет встроенного типа Decimal, мы рекомендуем использовать сторонний пакет github.com/shopspring/decimal, чтобы работать с типами Decimal нативно, не изменяя исходные запросы.Может возникнуть соблазн использовать вместо него Float, чтобы избежать сторонних зависимостей. Однако учтите, что типы Float в ClickHouse не рекомендуется использовать, когда требуется точность значений.Если вы всё же решите использовать на стороне клиента встроенный тип Float в Go, необходимо явно преобразовать Decimal в Float с помощью функции toFloat64() или её вариантов в запросах ClickHouse. Имейте в виду, что такое преобразование может привести к потере точности.
Nullable
Значение Nil в Go соответствует NULL в ClickHouse. Его можно использовать, если поле объявлено как Nullable. Во время вставки Nil можно передавать как для обычного столбца, так и для столбца типа Nullable. В первом случае будет сохранено значение по умолчанию для этого типа, например пустая строка для string. Для Nullable-варианта в ClickHouse будет сохранено значение NULL. При сканировании пользователь должен передать указатель на тип, который поддерживает nil, например *string, чтобы представить значение nil для поля Nullable. В примере ниже col1, имеющий тип Nullable(String), получает **string. Это позволяет представить nil.sql.Null*, например sql.NullInt64. Они совместимы с соответствующими типами ClickHouse.
Большие целые числа
Числовые типы размером более 64 бит представлены с помощью встроенного в Go пакета big.BFloat16
BFloat16 — это 16-битный тип чисел с плавающей запятой brain float, используемый в рабочих нагрузках машинного обучения. В Go значения BFloat16 записываются и считываются как float32. Для вариантов Nullable используется sql.NullFloat64.
QBit
QBit — это экспериментальный тип столбца для хранения векторных эмбеддингов в бит-срезовом формате, оптимизированный для поиска по сходству векторов. Для его использования должна быть включена настройка allow_experimental_qbit_type.
В Go столбец QBit(Float32, N) вставляется и считывается как []float32, где N — размерность вектора.