Learning Platform
Глоссарий Troubleshooting
Урок 04.01 · 15 мин
Средний
SQL Enginesqlparser-rsSQL ParsingLogicalPlanDDLData TypesDataFusion SQL

SQL-движок DataFusion

DataFusion принимает стандартный SQL и превращает его в план выполнения. В этом уроке разберём, как устроен SQL-движок: от текста запроса до LogicalPlan, какие SQL-конструкции поддерживаются, и чем диалект DataFusion отличается от PostgreSQL.

Pipeline: SQL-текст до LogicalPlan

Каждый SQL-запрос проходит три трансформации до того, как начнётся оптимизация:

SQL → LogicalPlan Pipeline
SQL TextВходной SQL-запрос от пользователя — текстовая строка
sqlparser-rs (парсинг)
AST (Statement)Абстрактное синтаксическое дерево — структурное представление SQL без проверки схемы
SqlToRel (семантический анализ)
Unoptimized LogicalPlanЛогический план до оптимизации — привязан к каталогу таблиц и типов
Optimizer (цепочка правил)
Optimized LogicalPlanОптимизированный план — pushdown фильтров, elimination колонок, constant folding

Парсинг — чисто синтаксический. Парсер не знает о таблицах, колонках или типах данных. Его задача — превратить текст в дерево синтаксиса (AST).

Семантический анализ (SqlToRel) — привязка AST к реальным данным: проверка существования таблиц в каталоге, разрешение колонок, проверка типов, разрешение имён функций.

Оптимизация — трансформация LogicalPlan без изменения семантики: pushdown фильтров, elimination неиспользуемых колонок, constant folding.

Парсинг: sqlparser-rs

DataFusion использует библиотеку sqlparser-rs — Rust-парсер SQL, поддерживающий множество диалектов. Парсер возвращает Vec<Statement> — набор AST-узлов.

use datafusion::sql::sqlparser::parser::Parser;
use datafusion::sql::sqlparser::dialect::GenericDialect;

let sql = "SELECT region, SUM(amount) FROM orders GROUP BY region";
let dialect = GenericDialect {};
let statements = Parser::parse_sql(&dialect, sql)?;
// statements[0]: Statement::Query { body: Select { ... } }

AST содержит структуру запроса — какие выражения в SELECT, какая таблица в FROM, какие условия в WHERE — но не проверяет, существуют ли эти объекты.

NOTE

sqlparser-rs поддерживает диалекты: PostgreSQL, MySQL, Hive, Snowflake, BigQuery, Redshift и другие. DataFusion по умолчанию использует GenericDialect. Переключить диалект можно через SessionConfig.

Семантический анализ: SqlToRel

Компонент SqlToRel берёт AST и превращает его в LogicalPlan. На этом этапе выполняются проверки:

  • Таблица существует в каталоге (CatalogProvider)
  • Колонки есть в схеме таблицы
  • Типы данных совместимы (нельзя сравнить INT с STRUCT)
  • Функции зарегистрированы (SUM, AVG, пользовательские UDF)
  • GROUP BY корректен (все не-агрегатные выражения в SELECT есть в GROUP BY)

Если любая проверка не пройдена — возвращается ошибка с описанием проблемы, а не тихий сбой на этапе выполнения.

Поддерживаемые SQL-конструкции

DDL (Data Definition Language)

-- Внешняя таблица: DataFusion читает данные из файлов
CREATE EXTERNAL TABLE logs
STORED AS PARQUET
LOCATION 's3://bucket/logs/';

-- С указанием схемы и опций формата
CREATE EXTERNAL TABLE events (
    event_id   VARCHAR NOT NULL,
    timestamp  TIMESTAMP,
    payload    VARCHAR
)
STORED AS CSV
LOCATION '/data/events/'
OPTIONS (
    'has_header' 'true',
    'delimiter'  ','
);

-- CREATE TABLE AS — материализация запроса в Parquet
CREATE TABLE top_regions
AS SELECT region, SUM(amount) AS total
   FROM orders
   GROUP BY region
   ORDER BY total DESC
   LIMIT 100;

-- VIEW — логическое представление (не материализуется)
CREATE VIEW active_orders AS
SELECT * FROM orders WHERE status = 'active';

-- Удаление
DROP TABLE top_regions;
DROP VIEW active_orders;
WARNING

CREATE EXTERNAL TABLE не копирует данные — DataFusion читает файлы напрямую. Если файлы изменятся или исчезнут, запросы к таблице вернут ошибку. Для устойчивого хранения используйте CREATE TABLE AS, которая записывает данные в управляемый формат.

DML (Data Manipulation Language)

DataFusion поддерживает INSERT для записи результатов запросов:

-- Вставка из запроса
INSERT INTO archive_orders
SELECT * FROM orders WHERE created_at < '2024-01-01';

-- COPY — экспорт в файл
COPY (SELECT * FROM orders WHERE region = 'EU')
TO '/data/export/eu_orders.parquet';

Типы данных

DataFusion использует систему типов Apache Arrow. Основные типы:

SQL-типArrow-типПримечание
BOOLEANBooleantrue / false
INT / INTEGERInt3232-bit целое
BIGINTInt6464-bit целое
FLOAT / REALFloat3232-bit вещественное
DOUBLEFloat6464-bit вещественное
VARCHAR / TEXTUtf8Строка UTF-8
TIMESTAMPTimestampНаносекунды, с/без timezone
DATEDate32Дни с эпохи
DECIMAL(p, s)Decimal128Точная арифметика
BINARY / BYTEABinaryБайтовый массив

Отличия от PostgreSQL

DataFusion реализует стандартный SQL, но не является полным клоном PostgreSQL. Основные различия:

АспектPostgreSQLDataFusion
ХранениеВстроенный storage engineВнешние файлы (Parquet, CSV, JSON)
ТранзакцииACID, MVCCНет транзакций — read-only аналитика
UPDATE / DELETEПолная поддержкаОграниченная (зависит от TableProvider)
ИндексыB-tree, Hash, GIN, GiSTНет индексов — predicate pushdown
PL/pgSQLХранимые процедурыНет — UDF на Rust или Python
JSONjsonb с индексамиБазовый JSON через Arrow
РасширенияPostgreSQL extensionsTableProvider, OptimizerRule, UDF
TIP

DataFusion не конкурирует с PostgreSQL как OLTP-база. Его ниша — встраиваемый аналитический движок: Parquet-аналитика, data lake querying, построение собственных query engines.

Регистрация таблиц через API

SQL DDL — не единственный путь. Таблицы можно регистрировать программно:

use datafusion::prelude::*;

let ctx = SessionContext::new();

// Parquet
ctx.register_parquet("orders", "data/orders.parquet", ParquetReadOptions::default()).await?;

// CSV
ctx.register_csv("events", "data/events.csv", CsvReadOptions::new().has_header(true)).await?;

// JSON
ctx.register_json("logs", "data/logs.json", NdJsonReadOptions::default()).await?;

// In-memory RecordBatch
let batch = RecordBatch::try_from_iter(vec![
    ("id", Arc::new(Int32Array::from(vec![1, 2, 3])) as ArrayRef),
    ("name", Arc::new(StringArray::from(vec!["a", "b", "c"]))),
])?;
ctx.register_batch("test", batch)?;

После регистрации таблицы доступны в SQL-запросах и через DataFrame API одинаково.

Итоги

  • SQL-запрос проходит парсинг (sqlparser-rs), семантический анализ (SqlToRel) и оптимизацию
  • Парсинг синтаксический — не проверяет существование таблиц и колонок
  • SqlToRel привязывает AST к каталогу: таблицы, типы, функции
  • DDL: CREATE EXTERNAL TABLE, CREATE TABLE AS, CREATE VIEW, DROP
  • Типы данных основаны на Apache Arrow — BOOLEAN, INT, BIGINT, VARCHAR, TIMESTAMP, DECIMAL
  • DataFusion — аналитический движок, не OLTP-база: нет транзакций, индексов и PL/pgSQL

Проверьте понимание

Результат: 0 из 0
Концептуальный
Вопрос 1 из 5. Какую роль выполняет sqlparser-rs в SQL-pipeline DataFusion?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 7