Вы когда-нибудь задумывались, почему в 2026 году технические статьи про базы данных всё еще выглядят ��ак же, как в 2005? Я вижу тонны статического текста, скриншоты терминалов и блоки кода, которые читатель должен копировать и запускать где-то у себя.

В этой статье я расскажу о технической реализации SQLize Embed — легковесного JS-компонента, который превращает любую страницу в живую лабораторию SQL. Я разберу, как это работает под капотом: от фронтенда на Ace Editor до изоляции выполнения запросов на бэкенде.

Проблема: «Смерть» примера кода

Типичный путь читателя туториала:

  1. Видит интересный запрос SELECT ... OVER (PARTITION BY ...).

  2. Думает: «Круто, надо попробовать на своих данных».

  3. Вспоминает, что у него стоит PostgreSQL 14, а пример для 17-й версии.

  4. Открывает Docker, ищет образ, ждет загрузки...

  5. В 50% случаев на этом этапе интерес пропадает.

Я решил сократить это расстояние до одного клика (кнопки "Run SQL").

Архитектура решения

Система состоит из трех уровней:

  1. Frontend (Embed SDK): JS-библиотека, которая инициализирует редакторы и управляет состоянием UI.

  2. Execution API: Прослойка, отвечающая за квоты, кэширование сессий и безопасность.

  3. Backend-кластер: Ферма изолированных контейнеров с различными версиями СУБД (MySQL, PostgreSQL, Oracle, MS SQL, и т.д.).

Фронтенд: Ace Editor и MutationObserver

Я выбрал Ace Editor за его производительность и гибкость. Скрипт sqlize-embed.js весит совсем немного, так как подгружает тяжелые части редактора с CDN только при наличии на странице элементов [data-sqlize-editor].

Интересный момент — работа с динамическим контентом. Если ваш сайт подгружает статьи через AJAX или использует бесконечный скролл, обычные методы инициализации не сработают. Я применил MutationObserver:

new MutationObserver((mutations) => {
    let shouldInit = false;
    mutations.forEach(m => {
        m.addedNodes.forEach(node => {
            if (node.nodeType === 1 && (node.hasAttribute('data-sqlize-editor'))) {
                shouldInit = true;
            }
        });
    });
    if (shouldInit) initSQLize();
}).observe(document.body, { childList: true, subtree: true });

Это позволяет «подхватывать» новые редакторы сразу после их появления в DOM.

Процесс выполнения запроса

Когда пользователь нажимает «Run», происходит двухфазный процесс:

  1. Phase 1: Session Hashing. Я отправляю код и версию СУБД на эндпоинт /hash.php. Сервер генерирует уникальный идентификатор сессии. Это позволяет мне не гонять огромные куски SQL кода в URL и обеспечивает базу для кэширования результатов.

  2. Phase 2: Execution & Streaming. Клиент обращается к /sqleval.php?sqlses={hash}. Сервер находит соответствующую задачу, отправляет её в нужный контейнер и возвращает результат в виде отформатированного HTML (или JSON, в зависимости от настроек).

Технические подробности бэкенда

Самое сложное — это поддержка огромного количества версий. На текущий момент SQLize поддерживает:

  • MySQL: 8.0, 9.3.0

  • PostgreSQL: от 14 до 18 (включая PostGIS)

  • MS SQL Server: от 2017 до новейшей 2025

  • Oracle: 19c, 21c, 23ai

  • MariaDB, SQLite, Firebird, ClickHouse и даже экзотику вроде SOQOL.

Внутри это работает на Docker-контейнерах с жесткими лимитами по CPU/Memory и read-only файловой системой для пользовательских запросов (если не подразумевается CREATE TABLE).

Безопасность и изоляция

При встраивании кода в чужие сайты встает вопрос безопасности (CORS, XSS). Я использую строгие политики CORS: выполнение запросов разрешено только для доменов, имеющих активную подписку. Это предотвращает нецелевое использование моих вычислительных мощностей.

Как внедрить?

Внедрение требует минимум усилий.

  1. Подключаем SDK:

<script src="https://sqlize.online/js/sqlize-embed.js"></script>
  1. Создаем контейнер:

<div data-sqlize-editor 
     data-sql-version="psql17" 
     code-rows="10">
-- Пример использования оконных функций в PostgreSQL 17
SELECT 
    name, 
    salary, 
    AVG(salary) OVER(ORDER BY salary ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) as moving_avg
FROM (VALUES ('Alice', 5000), ('Bob', 6000), ('Charlie', 7000)) AS employees(name, salary);
</div>

Атрибуты настройки:

  • data-sql-version: идентификатор движка (например, mysql93, mssql2025).

  • data-read-only: если true, пользователь увидит код, сможет его запустить, но не сможет изменить.

  • code-rows / result-rows: фиксированная высота блоков (в строках).

Цепочки запросов (Chaining): наследование контекста

Одной из самых интересных функций стала возможность связывать несколько редакторов в цепочку. Представьте, что в начале статьи вы создаете схему базы данных, а в последующих разделах постепенно наполняете её данными и выполняете сложные запросы.

Чтобы не заставлять читателя видеть (и копировать) один и тот же DDL-код в каждом блоке, я реализовал поддержку атрибутов data-sqlize-id и data-sqlize-parent. Это работает как цепочка наследования: при нажатии кнопки «Run» скрипт рекурсивно обхо��ит дерево родителей и собирает весь код воедино перед отправкой на сервер.

Это позволяет сохранять логический контекст обучения на протяжении всей лонгрид-статьи, не перегружая визуальное пространство лишним кодом.

Что дальше?

Я активно работаю над поддержкой векторных типов данных (уже доступны в MariaDB 11.8 через мой SDK) и интеграцией с LLM для автоматического исправления ошибок в запросах прямо внутри встраиваемого редактора.

Если вы пишете про базы данных — попробуйте сделать свои статьи интерактивными. Это кардинально меняет вовлеченность аудитории.

Буду рад ответить на вопросы в комментариях! Какие СУБД вам было бы интересно увидеть в песочнице?