1
0
mirror of https://github.com/Bayselonarrend/OpenIntegrations.git synced 2026-05-06 21:04:14 +02:00

Начало ClickHouse

This commit is contained in:
Anton Titovets
2026-01-07 20:08:27 +03:00
parent b2cf5f4e29
commit 9325ea4aac
5 changed files with 438 additions and 12 deletions
@@ -0,0 +1,349 @@
// OneScript: ./OInt/core/Modules/OPI_ClickHouse.os
// Lib: ClickHouse
// CLI: clickhouse
// Keywords: clickhouse
// MIT License
// Copyright (c) 2023-2025 Anton Tsitavets
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// https://github.com/Bayselonarrend/OpenIntegrations
// BSLLS:Typo-off
// BSLLS:LatinAndCyrillicSymbolInWord-off
// BSLLS:IncorrectLineBreak-off
// BSLLS:NumberOfOptionalParams-off
// BSLLS:UsingServiceTag-off
// BSLLS:LineLength-off
// BSLLS:UsingSynchronousCalls-off
//@skip-check module-structure-top-region
//@skip-check module-structure-method-in-regions
//@skip-check wrong-string-literal-content
//@skip-check method-too-many-params
//@skip-check constructor-function-return-section
//@skip-check doc-comment-collection-item-type
#Область ПрограммныйИнтерфейс
#Область ОсновныеМетоды
// Выполнить запрос
// Выполняет запрос с указанными параметрами
//
// Параметры:
// Соединение - Произвольный - Настройки или объект соединения. См. ПолучитьНастройкиСоединения - conn
// Запрос - Структура Из КлючИЗначение - Данные запроса. См. ПолучитьНастройкиЗапроса - req
// Сессия - Структура Из КлючИЗначение - Настройки сессии. См. ПолучитьНастройкиСессии - session
//
// Возвращаемое значение:
// Соответствие Из КлючИЗначение - Результат выполнения
Функция ВыполнитьЗапрос(Знач Соединение, Знач Запрос, Знач Сессия = Неопределено) Экспорт
OPI_ПреобразованиеТипов.ПолучитьКоллекциюКлючИЗначение(Запрос, "Некорректная структура запроса");
Если Сессия <> Неопределено Тогда
OPI_ПреобразованиеТипов.ПолучитьКоллекциюКлючИЗначение(Сессия, "Некорректная структура сессии");
КонецЕсли;
ПереданКоннекторGrpc = OPI_GRPC.ЭтоКоннектор(Соединение);
ТранспортПоУмолчанию = ?(ПереданКоннекторGrpc, "grpc", "http");
Если Не ПереданКоннекторGrpc Тогда
OPI_ПреобразованиеТипов.ПолучитьКоллекциюКлючИЗначение(Соединение, "Некорректная структура соединения");
ОтсутствующиеПоля = OPI_Инструменты.НайтиОтсутствующиеПоляКоллекции(Соединение, "address");
Если ЗначениеЗаполнено(ОтсутствующиеПоля) Тогда
ПоляСтрокой = СтрСоединить(ОтсутствующиеПоля, ", ");
ТекстОшибки = СтрШаблон("Отсутствуют обязательные поля настроек соединения: %1", ПоляСтрокой);
ВызватьИсключение ТекстОшибки;
КонецЕсли;
КонецЕсли;
Транспорт = OPI_Инструменты.ПолучитьИли(Соединение, "transport", ТранспортПоУмолчанию);
Если Транспорт = "http" Тогда
Результат = ВыполнитьЗапросЧерезHttp(Соединение, Запрос, Сессия);
ИначеЕсли Транспорт = "grpc" Тогда
Результат = ВыполнитьЗапросЧерезGrpc(Соединение, Запрос, Сессия);
КонецЕсли;
Возврат Результат;
КонецФункции
// Получить настройки соединения
// Получает структуру настроек соединения
//
// Параметры:
// Адрес - Строка - Адрес подключения с протоколом и портом - addr
// Авторизация - Строка, Структура Из КлючИЗначение - Авторизация: строка для JWT, структура для basic - auth
// Транспорт - Строка - Вид транспорта для подключения: http, grpc - trns
//
// Возвращаемое значение:
// Структура Из КлючИЗначение - Структура настроек соединения
Функция ПолучитьНастройкиСоединения(Знач Адрес
, Знач Авторизация = Неопределено
, Знач Транспорт = "http") Экспорт
НастройкиСоединения = Новый Структура;
OPI_Инструменты.ДобавитьПоле("address", Адрес , "Строка", НастройкиСоединения);
OPI_ПреобразованиеТипов.ПолучитьСтроку(Транспорт);
Транспорт = нРег(Транспорт);
Если Транспорт <> "http" И Транспорт <> "grpc" Тогда
ВызватьИсключение "Некорректный вид транспорта. Доступные виды: http, grpc";
КонецЕсли;
OPI_Инструменты.ДобавитьПоле("transport", Транспорт, "Строка", НастройкиСоединения);
Если Авторизация = Неопределено Тогда
Возврат НастройкиСоединения;
КонецЕсли;
OPI_ПреобразованиеТипов.ПолучитьКоллекцию(Авторизация);
Если ТипЗнч(Авторизация) = Тип("Массив") Тогда
JWT = Авторизация[0];
OPI_Инструменты.ДобавитьПоле("auth_type" , "jwt", "Строка", НастройкиСоединения);
OPI_Инструменты.ДобавитьПоле("token" , JWT , "Строка", НастройкиСоединения);
Иначе
OPI_Инструменты.ДобавитьПоле("auth_type" , "basic", "Строка", НастройкиСоединения);
Для Каждого КлючЗначение Из Авторизация Цикл
OPI_Инструменты.ДобавитьПоле("user" , КлючЗначение.Ключ , "Строка", НастройкиСоединения);
OPI_Инструменты.ДобавитьПоле("password" , КлючЗначение.Значение, "Строка", НастройкиСоединения);
Прервать;
КонецЦикла;
КонецЕсли;
Возврат НастройкиСоединения;
КонецФункции
// Получить настройки запроса
// Формирует структуру описания запроса
//
// При использовании транспорта `http` нельзя одновременно использовать Данные и Внешние таблицы
//
// Параметры:
// Текст - Строка - Текст запроса - query
// БазаДанных - Строка - База данных - db
// IDЗапроса - Строка - Уникальный ID запроса. Будет сгенерирован, если не указан - id
// Данные - Произвольный - Строка, файл или двоичные данные запроса - data
// ФорматДанных - Строка - Формат данных: CVS, TVS, JSON и др. - format
// ВнешниеТаблицы - Массив Из Структура - Информация о внешних таблицах. См. ПолучитьСтруктуруВнешнихТаблиц - ext
// Настройки - Соответствие Из КлючИЗначение - Дополнительные настройки запроса - settings
//
// Возвращаемое значение:
// Структура Из КлючИЗначение - Структура запроса
Функция ПолучитьНастройкиЗапроса(Знач Текст
, Знач БазаДанных = Неопределено
, Знач IDЗапроса = Неопределено
, Знач Данные = Неопределено
, Знач ФорматДанных = Неопределено
, Знач ВнешниеТаблицы = Неопределено
, Знач Настройки = Неопределено) Экспорт
Если IDЗапроса = Неопределено Тогда
IDЗапроса = Строка(Новый УникальныйИдентификатор());
КонецЕсли;
ПараметрыЗапроса = Новый Структура;
OPI_Инструменты.ДобавитьПоле("query" , Текст , "Строка" , ПараметрыЗапроса);
OPI_Инструменты.ДобавитьПоле("database" , БазаДанных , "Строка" , ПараметрыЗапроса);
OPI_Инструменты.ДобавитьПоле("id" , IDЗапроса , "Строка" , ПараметрыЗапроса);
OPI_Инструменты.ДобавитьПоле("data" , Данные , "Текущий" , ПараметрыЗапроса);
OPI_Инструменты.ДобавитьПоле("format" , ФорматДанных , "Строка" , ПараметрыЗапроса);
OPI_Инструменты.ДобавитьПоле("external_tables", ВнешниеТаблицы, "Массив" , ПараметрыЗапроса);
OPI_Инструменты.ДобавитьПоле("settings" , Настройки , "КлючИЗначение", ПараметрыЗапроса);
Возврат ПараметрыЗапроса;
КонецФункции
// Получить структуру внешней таблицы
// Получает структуру описания внешней таблицы запроса
//
// Параметры:
// Имя - Строка - Имя таблицы - name
// СтруктураКолонок - Структура Из КлючИЗначение - Структура колонок таблицы: Ключ > имя, Значение > тип данных - cols
// Данные - Произвольный - Строка, файл или двоичные данные таблицы - data
// ФорматДанных - Строка - Формат данных: CVS, TVS, JSON и др. - format
//
// Возвращаемое значение:
// Структура Из КлючИЗначение - Структура описания внешней таблицы
Функция ПолучитьСтруктуруВнешнейТаблицы(Знач Имя
, Знач СтруктураКолонок
, Знач Данные = Неопределено
, Знач ФорматДанных = Неопределено) Экспорт
ПараметрыТаблицы = Новый Структура;
OPI_Инструменты.ДобавитьПоле("name" , Имя , "Строка" , ПараметрыТаблицы);
OPI_Инструменты.ДобавитьПоле("cols" , СтруктураКолонок, "КлючИЗначение", ПараметрыТаблицы);
OPI_Инструменты.ДобавитьПоле("data" , Данные , "Текущий" , ПараметрыТаблицы);
OPI_Инструменты.ДобавитьПоле("format", ФорматДанных , "Строка" , ПараметрыТаблицы);
Возврат ПараметрыТаблицы;
КонецФункции
// Получить настройки сессии
// Формирует структуру настроек сессии выполнения запроса
//
// Параметры:
// IDСессии - Строка - ID сессии. Новый уникальный идентификатор, если не указано (будет создана новая сессия) - id
// ПроверятьСессию - Булево - Проверять существование сессии. Истина, когда ID указан и ложь, когда нет, если не указано иное - check
// Таймаут - Число - Время жизни сессии в секундах - timeout
//
// Возвращаемое значение:
// Структура Из КлючИЗначение - Структура настроек сессии
Функция ПолучитьНастройкиСессии(Знач IDСессии = Неопределено
, Знач ПроверятьСессию = Неопределено
, Знач Таймаут = 60) Экспорт
НастройкиСессии = Новый Структура;
ЕстьИдентификаторСессии = IDСессии <> Неопределено;
Если ЕстьИдентификаторСессии Тогда
IDСессии = Строка(Новый УникальныйИдентификатор());
КонецЕсли;
Если ПроверятьСессию = Неопределено Тогда
ПроверятьСессию = ЕстьИдентификаторСессии;
КонецЕсли;
OPI_Инструменты.ДобавитьПоле("id" , IDСессии , "Строка", НастройкиСессии);
OPI_Инструменты.ДобавитьПоле("check" , ПроверятьСессию, "Булево", НастройкиСессии);
OPI_Инструменты.ДобавитьПоле("timeout", Таймаут , "Число" , НастройкиСессии);
Возврат НастройкиСессии;
КонецФункции
#КонецОбласти
#КонецОбласти
#Область СлужебныеПроцедурыИФункции
Функция ВыполнитьЗапросЧерезHttp(Знач Соединение, Знач Запрос, Знач Сессия)
Данные = OPI_Инструменты.ПолучитьИли(Запрос, "data" , Неопределено);
ВнешниеТаблицы = OPI_Инструменты.ПолучитьИли(Запрос, "external_tables", Неопределено);
Если Данные <> Неопределено И ВнешниеТаблицы <> Неопределено Тогда
ВызватьИсключение "Нельзя использовать Данные и Внешние таблицы одновременно при передаче через http";
КонецЕсли;
URL = Соединение["address"];
ТекстЗапроса = OPI_Инструменты.ПолучитьИли(Запрос, "query" , Неопределено);
ФорматДанных = OPI_Инструменты.ПолучитьИли(Запрос, "format" , Неопределено);
Авторизация = OPI_Инструменты.ПолучитьИли(Запрос, "auth_type", "none");
HTTPКлиент = OPI_ЗапросыHTTP
.НовыйЗапрос()
.Инициализировать(URL)
.ДобавитьПараметрURL("query" , ТекстЗапроса, Истина)
.ДобавитьПараметрURL("format", ФорматДанных, Истина)
.УстановитьДвоичноеТело(Данные);
Если Авторизация = "jwt" Тогда
JWT = OPI_Инструменты.ПолучитьИли(Запрос, "token", "");
HTTPКлиент.ДобавитьBearerАвторизацию(JWT);
ИначеЕсли Авторизация = "basic" Тогда
Логин = OPI_Инструменты.ПолучитьИли(Запрос, "user", Неопределено);
Пароль = OPI_Инструменты.ПолучитьИли(Запрос, "password", Неопределено);
HTTPКлиент
.ДобавитьЗаголовок("X-ClickHouse-User", Логин , Истина)
.ДобавитьЗаголовок("X-ClickHouse-Key" , Пароль, Истина);
КонецЕсли;
Если ЗначениеЗаполнено(ВнешниеТаблицы) Тогда
ТипДанныхФайла = "application/octet-stream";
HTTPКлиент.НачатьЗаписьТелаMultipart(Ложь);
OPI_ПреобразованиеТипов.ПолучитьМассив(ВнешниеТаблицы);
Для Каждого Таблица Из ВнешниеТаблицы Цикл
ТекстОшибки = СтрШаблон("Передана некорректная структура внешней таблицы", ИмяТаблицы);
OPI_ПреобразованиеТипов.ПолучитьКоллекциюКлючИЗначение(Таблица, ТекстОшибки);
ИмяТаблицы = OPI_Инструменты.ПолучитьИли(Таблица, "name" , "");
ФорматТаблицы = OPI_Инструменты.ПолучитьИли(Таблица, "format", Неопределено);
ДанныеТаблицы = OPI_Инструменты.ПолучитьИли(Таблица, "data" , Неопределено);
КолонкиТаблицы = OPI_Инструменты.ПолучитьИли(Таблица, "cols" , Неопределено);
ТекстОшибки = СтрШаблон("Некорректная структура колонок для таблицы ""%1""", ИмяТаблицы);
OPI_ПреобразованиеТипов.ПолучитьКоллекциюКлючИЗначение(КолонкиТаблицы, ТекстОшибки);
МассивКолонок = Новый Массив;
Для Каждого КолонкаТаблицы Из КолонкиТаблицы Цикл
КолонкаСтрокой = СтрШаблон("%1 %2", КолонкаТаблицы.Ключ, КолонкаТаблицы.Значение);
МассивКолонок.Добавить(КолонкаСтрокой);
КонецЦикла;
КолонкиТаблицыСтрокой = СтрСоединить(МассивКолонок, ", ");
ИмяПараметраФормат = СтрШаблон("%1_%2" , ИмяТаблицы, "format");
ИмяПараметраСтруктура = СтрШаблон("%1_%2" , ИмяТаблицы, "structure");
ИмяФайла = СтрШаблон("%1.bin", ИмяТаблицы);
HTTPКлиент
.ДобавитьПараметрURL(ИмяПараметраФормат , ФорматТаблицы , Истина)
.ДобавитьПараметрURL(ИмяПараметраСтруктура, КолонкиТаблицыСтрокой, Истина)
.ДобавитьФайлMultipartFormData(ИмяТаблицы, ИмяФайла, ДанныеТаблицы, ТипДанныхФайла, Истина);
КонецЦикла;
КонецЕсли;
Ответ = HTTPКлиент.ВернутьОтветКакJSONКоллекцию();
Возврат Ответ;
КонецФункции
Функция ВыполнитьЗапросЧерезGrpc(Знач Соединение, Знач Запрос, Знач Сессия)
КонецФункции
#КонецОбласти
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<mdclass:CommonModule xmlns:mdclass="http://g5.1c.ru/v8/dt/metadata/mdclass" uuid="34f3181c-fd79-4d34-9978-5ebb19c8d974">
<name>OPI_ClickHouse</name>
<synonym>
<key>ru</key>
<value>ClickHouse</value>
</synonym>
<server>true</server>
<externalConnection>true</externalConnection>
<clientOrdinaryApplication>true</clientOrdinaryApplication>
</mdclass:CommonModule>
@@ -428,7 +428,7 @@
Если ПопыткаB64 Тогда
Значение = Base64Значение(Значение);
Иначе
ВызватьИсключение "значение не является путем к файлу или Base64 строкой";
ВызватьИсключение "Значение не является путем к файлу или Base64 строкой";
КонецЕсли;
КонецЕсли;
@@ -58,6 +58,7 @@
<commonModules>CommonModule.OPI_Airtable</commonModules>
<commonModules>CommonModule.OPI_Bitrix24</commonModules>
<commonModules>CommonModule.OPI_CDEK</commonModules>
<commonModules>CommonModule.OPI_ClickHouse</commonModules>
<commonModules>CommonModule.OPI_Dropbox</commonModules>
<commonModules>CommonModule.OPI_FTP</commonModules>
<commonModules>CommonModule.OPI_GoogleCalendar</commonModules>
@@ -206,7 +206,7 @@
КонецФункции
// Установить параметры URL !NOCLI
// Устанавливает коллекцию параметров URL
// Устанавливает коллекцию query параметров URL
//
// Параметры:
// Значение - Произвольный - Стрктура или соответствие параметров URL - params
@@ -235,6 +235,51 @@
КонецФункции
// Добавить параметр URL !NOCLI
// Добавляет query параметр URL в запрос
//
// Примечание:
// Значение параметро будет преобразовано в строку
//
// Параметры:
// Имя - Строка - Ключ параметра запроса - key
// Значение - Произвольный - Значение параметра запроса - value
// ИгнорироватьПустой - Булево - Не добавлять параметр, если передано пустое значение - skipempty
//
// Возвращаемое значение:
// ОбработкаОбъект.OPI_HTTPКлиент - Этот же объект обработки
Функция ДобавитьПараметрURL(Знач Имя, Знач Значение, Знач ИгнорироватьПустой = Ложь) Экспорт
Попытка
Если ОстановитьРаботу() Тогда Возврат ЭтотОбъект; КонецЕсли;
Если Не ЗначениеЗаполнено(Имя) Тогда
ДобавитьЛог("ДобавитьПараметрURL: Передан пустой ключ - пропуск");
Возврат ЭтотОбъект;
КонецЕсли;
OPI_ПреобразованиеТипов.ПолучитьБулево(ИгнорироватьПустой);
Если ИгнорироватьПустой И Не ЗначениеЗаполнено(Строка(Значение)) Тогда
ДобавитьЛог(СтрШаблон("ДобавитьПараметрURL: пустой параметр проигнорирован, ключ - %1", Имя));
Возврат ЭтотОбъект;
КонецЕсли;
OPI_ПреобразованиеТипов.ПолучитьСтроку(Имя);
ДобавитьЛог(СтрШаблон("ДобавитьПараметрURL: добавление параметра, ключ - %1", Имя));
ЗапросПараметрыURL.Вставить(Имя, Значение);
Возврат ЭтотОбъект;
Исключение
Возврат Ошибка(ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
КонецПопытки;
КонецФункции
// Установить файл ответа !NOCLI
// Устанавливает путь к файлу для сохранения результата запроса
//
@@ -947,21 +992,33 @@
// Запись Multipart предварительно должна быть инициализирована при помощи функции `НачатьЗаписьТелаMultipart`
//
// Параметры:
// ИмяПоля - Строка - Имя поля формы - field
// ИмяФайла - Строка - Имя файла с расширением - filename
// Данные - ДвоичныеДанные, Строка - Данные файла для записи - data
// ТипДанных - Строка - Mime тип записываемых данных - mime
// ИмяПоля - Строка - Имя поля формы - field
// ИмяФайла - Строка - Имя файла с расширением - filename
// Данные - ДвоичныеДанные, Строка - Данные файла для записи - data
// ТипДанных - Строка - Mime тип записываемых данных - mime
// ИгнорироватьПустой - Булево - Не добавлять файл, если переданы пустые данные - skipempty
//
// Возвращаемое значение:
// ОбработкаОбъект.OPI_HTTPКлиент - Этот же объект обработки
Функция ДобавитьФайлMultipartFormData(Знач ИмяПоля, Знач ИмяФайла, Знач Данные, Знач ТипДанных = "") Экспорт
Функция ДобавитьФайлMultipartFormData(Знач ИмяПоля
, Знач ИмяФайла
, Знач Данные
, Знач ТипДанных = ""
, Знач ИгнорироватьПустой = Ложь) Экспорт
Попытка
Если ОстановитьРаботу() Тогда Возврат ЭтотОбъект; КонецЕсли;
Если Не Multipart Тогда Возврат Ошибка("ДобавитьФайлMultipart: не инициализирована запись Multipart"); КонецЕсли;
OPI_ПреобразованиеТипов.ПолучитьДвоичныеДанные(Данные);
OPI_ПреобразованиеТипов.ПолучитьДвоичныеДанные(Данные, Истина, Истина);
OPI_ПреобразованиеТипов.ПолучитьБулево(ИгнорироватьПустой);
Если ИгнорироватьПустой И Данные.Размер = 0 Тогда
ДобавитьЛог(СтрШаблон("ДобавитьФайлMultipart: пустой файл проигнорирован, поле %1", ИмяПоля));
Возврат ЭтотОбъект;
КонецЕсли;
OPI_ПреобразованиеТипов.ПолучитьСтроку(ИмяПоля);
OPI_ПреобразованиеТипов.ПолучитьСтроку(ИмяФайла);
@@ -1148,12 +1205,13 @@
// Добавляет заголовок в набор заголовков запроса
//
// Параметры:
// Имя - Строка - Ключ заголовка - header
// Значение - Строка - Значение заголовка - value
// Имя - Строка - Ключ заголовка - header
// Значение - Строка - Значение заголовка - value
// ИгнорироватьПустой - Булево - Не добавлять заголовок, если передано пустое значение - skipempty
//
// Возвращаемое значение:
// ОбработкаОбъект.OPI_HTTPКлиент - Этот же объект обработки
Функция ДобавитьЗаголовок(Знач Имя, Знач Значение) Экспорт
Функция ДобавитьЗаголовок(Знач Имя, Знач Значение, Знач ИгнорироватьПустой = Ложь) Экспорт
Попытка
@@ -1162,6 +1220,12 @@
OPI_ПреобразованиеТипов.ПолучитьСтроку(Имя);
OPI_ПреобразованиеТипов.ПолучитьСтроку(Значение);
OPI_ПреобразованиеТипов.ПолучитьБулево(ИгнорироватьПустой);
Если ИгнорироватьПустой И Значение = "" Тогда
ДобавитьЛог(СтрШаблон("ДобавитьЗаголовок: пустой заголовок проигнорирован, ключ %1", Имя));
Возврат ЭтотОбъект;
КонецЕсли;
ДобавитьЛог("ДобавитьЗаголовок: установка заголовков запроса");
@@ -1172,6 +1236,7 @@
Исключение
Возврат Ошибка(ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
КонецПопытки;
КонецФункции
#КонецОбласти