Как создать собственный плагин Trino: практический пример

Trino курсы примеры обучение, Trino для разработчика, Trino примеры курсы обучение дата-инженеров, Школа Больших Данных Учебный Центр Коммерсант

Пишем собственный плагин Trino для работы с пользовательским типом данных: практический пример создания и регистрации своих классов и pom-файла.

Пример реализации своего плагина Trino

О том, что гибкость Trino обеспечивается благодаря его плагинной архитектуре, я недавно писала здесь. Сегодня рассмотрим пример создания своего плагина, который реализует возможность работы с пользовательским типом данных.

Предположим, необходимо использовать пользовательский сложный тип данных my_custom_type в SQL-запросах Trino, например:

CREATE TABLE example (
    id INT,
    data my_custom_type
);

Таким образом, надо добавить в Trino пользовательский тип данных my_custom_type. По умолчанию Trino поставляется с рядом встроенных типов, таких как VarcharType и BigintType. Для реализации типа на языке SQL в Trino используется интерфейс Type. Плагин может предоставлять новые объекты Type, возвращая их из результата метода getTypes(). Интерпретация значения в его собственной форме типа контейнера определяется его типом Type. Для некоторых типов данных, таких как BigintType, он соответствует интерпретации Java собственного типа контейнера. Для других типов, таких как TimestampWithTimeZoneType, который также использует long для своего собственного типа контейнера, значение представляет собой 8-байт, объединяя часовой пояс и миллисекунды с эпохи unix. Поэтому очень важно знать нативную кодировку, т.е. как именно представляется и хранится значение конкретного типа данных на уровне контейнера типа. Сигнатура типа определяет его идентичность, а также кодирует некоторую общую информацию о типе, такую ​​как его параметры, если они есть, например, VARCHAR(10).

Поскольку Trino написан на Java, плагины тоже должны быть написаны на этом языке программирования. Подключаемые модули должны реализовывать интерфейсы и переопределять методы, определенные интерфейсом поставщика услуг (SPI, Service Provider Interface). Плагин включает реализацию интерфейса Plugin, который содержит методы доступа для извлечения классов плагина.

Все зависимости и параметры плагина указываются в файле pom.xml проекта Maven для автоматизированной сборки и управления зависимостями при разработке на Java. Поэтому сперва создадим Maven-проект, предварительно установив систему сборки Maven и JDK 11 или выше. Файл pom.xml со всеми зависимостями и параметрами нашего плагина может выглядеть так:

<project xmlns="http://maven.apache.org/POM/4.0.0" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
                             http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example.trino</groupId>
    <artifactId>trino-custom-type-plugin</artifactId>
    <version>1.0-SNAPSHOT</version>
    <properties>
        <trino.version>470</trino.version>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>io.trino</groupId>
            <artifactId>trino-plugin-base</artifactId>
            <version>${trino.version}</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>11</source>
                    <target>11</target>
                </configuration>
            </plugin>
        </plugins>
    </build> 
</project>

Этот pom.xml файл настраивает проект Maven для разработки плагина Trino с пользовательскими типами данных. Он определяет необходимые зависимости, указывает версии Java для компиляции и использует стандартный плагин компилятора Maven.

Затем создадим файл MyCustomType.java, где напишем класс пользовательского типа данных MY_CUSTOM_TYPE, импортировав необходимые библиотеки и сохранив этот класс в пакет com.example.trino.type:

package com.example.trino.type;
import io.airlift.slice.Slice;
import io.trino.spi.block.BlockBuilder;
import io.trino.spi.type.AbstractType;
import io.trino.spi.type.TypeSignature;
import io.trino.spi.type.TypeParameter;
import io.trino.spi.type.Type;
import java.util.List;

public class MyCustomType extends AbstractType {
    public static final MyCustomType MY_CUSTOM_TYPE = new MyCustomType();

    public MyCustomType() {
        super(new TypeSignature("my_custom_type"), Object.class);
    }

    @Override
    public Object getObjectValue(ConnectorSession session, Block block, int position) {
        // Логика преобразования блока в объект
        return null;
    }

    @Override
    public boolean equals(Object a, Object b) {
        // реализация сравнения
        return false;
    }

    @Override
    public int hash(Block block, int position) {
        // реализация хеширования
        return 0;
    }

    @Override
    public void appendTo(Block block, int position, BlockBuilder blockBuilder) {
        // реализация добавления в блок
    }

    @Override
    public String getDisplayName() {
        return "my_custom_type";
    }
}

В этом коде публичный класс MyCustomType наследуется от AbstractType, что позволяет создавать пользовательские типы данных для Trino. Применяя паттерн Singleton с помощью модификатора final, создадим единственный экземпляр этого типа, чтобы использовать его в различных местах без создания множества объектов. Внутри конструктора класса public MyCustomType() объявим вызов конструктора родительского класса AbstractType с параметром TypeSignature, который задаёт имя типа my_custom_type и класс Java, с которым он ассоциируется (Object.class).

Метод getObjectValue() отвечает за преобразование данных из блоков Trino в объекты Java. Здесь нужно реализовать логику извлечения и преобразования данных из позиции position блока block в понятный объект. В качестве параметра метода также передается информация о текущей сессии коннектора. Метод equals() нужен для сравнения двух значений данного типа, а метод hash() для вычисления хеш-кода значения в заданной позиции блока, что полезно при создании хеш-таблиц.

Метод appendTo() отвечает за добавление значения из указанной позиции блока block в другой блок blockBuilder. Это важно для операций, где данные копируются или перемещаются внутри Trino. Метод getDisplayName() возвращает строковое представление имени типа данных, которое будет отображаться в метаданных и используется для идентификации типа, т.е. my_custom_type, что соответствует имени пользовательского типа данных, заданному в конструкторе класса MyCustomType.

Далее надо создать файл CustomTypePlugin.java с описанием класса плагина, который регистрирует ранее определенный пользовательский тип данных:

package com.example.trino.plugin;

import com.example.trino.type.MyCustomType;
import io.trino.spi.Plugin;
import io.trino.spi.type.Type;
import io.trino.spi.type.TypeRegistry;

import java.util.ArrayList;
import java.util.List;

public class CustomTypePlugin implements Plugin {
    @Override
    public Iterable<Type> getTypes() {
        List<Type> types = new ArrayList<>();
        types.add(MyCustomType.MY_CUSTOM_TYPE);
        return types;
    }
}

Класс CustomTypePlugin реализует интерфейс Plugin, что делает его плагином для Trino. Метод getTypes() переопределяется из интерфейса Plugin и возвращает коллекцию типов данных, которые добавляет плагин. В итоге создаётся новый список типов types, в него добавляется пользовательский тип MyCustomType.MY_CUSTOM_TYPE и возвращается список типов.

Визуально это можно представить следующей UML-диаграммой классов.

UML-диаграмма классов для плагина Trino
UML-диаграмма классов для плагина Trino

После этого в корне Maven-проекта создадим файл plugin.properties со следующим содержимым:

description=Custom Type Plugin for Trino
version=1.0
main-class=com.example.trino.plugin.CustomTypePlugin

В итоге структура Maven-проекта может выглядеть так:

Структура Maven-проекта
Структура Maven-проекта

Такая структура поможет организовывать файлы логически, отражая их функциональность и иерархию. Тесты в отдельной директории src/test/java позволят Maven автоматически их обнаруживать и выполнять. А ресурсы, определенные в plugin.properties, находятся в src/main/resources, что гарантирует их включение в финальный артефакт при сборке.

Наконец, соберем проект с помощью Maven:

mvn clean package

И поместим полученный JAR-файл в каталог плагинов Trino, т.е. директорию plugin. Затем следует перезапустить сервер Trino, чтобы загрузить новый плагин и использовать его для назначения пользовательского типа данных столбцу таблицы.

Освоить работу с Trino вы сможете на специализированных курсах в нашем лицензированном учебном центре обучения и повышения квалификации для разработчиков, менеджеров, архитекторов, инженеров, администраторов, Data Scientist’ов и аналитиков Big Data в Москве:

Источники

  1. https://trino.io/docs/current/installation/plugins.html
  2. https://trino.io/docs/current/develop/spi-overview.html
  3. https://trino.io/docs/current/develop/types.html
Я даю свое согласие на обработку персональных данных и соглашаюсь с политикой конфиденциальности.