Перейти к основному содержимому

Свои базы данных: Qubix.db

Qubix.db — команда, через которую пользовательский скрипт и обработчик страницы сайта работают с вашей собственной базой данных PostgreSQL, MySQL или MariaDB: читают строки и записывают изменения. Посредник по HTTP для этого не нужен — скрипт сам подключается к базе по строке соединения.

Это не база Qubix. Статистику, которую записывает Qubix, читает команда sql — только на чтение и в пределах прав вашей роли (см. Таблицы для запросов sql). Qubix.db работает с вашей базой с правами того пользователя базы, который назван в строке соединения, — и читает, и пишет.

Где доступна команда:

  • в скриптах раздела Скрипты — и по расписанию, и по кнопке Запустить;
  • в обработчиках страниц сайта — на каждом заходе посетителя и по кнопке Запуск в панели Тестовый запуск (см. Бэкенд сайта).

В правилах Britva Qubix.db нет.

Сначала — разрешённый узел

Подключиться можно только к узлу, который администратор вписал в перечень Узлы баз данных для скриптов (Настройки Qubix → JavaScript). Пока перечень пуст, подключения к базам выключены. Подробнее — в разделе Куда можно подключаться.

Как это выглядит​

db-quick-start.jsJavaScript
function main() {
const db = Qubix.db.connect('postgres://report:[email protected]:5432/crm')

const rows = db.query('SELECT id, email FROM customers WHERE country = $1 LIMIT 10', ['DE'])
for (const row of rows) console.log(row.id, row.email)

const changed = db.exec('UPDATE customers SET checked_at = now() WHERE country = $1', ['DE'])
console.log('изменено строк:', changed)

db.close()
}

Все вызовы синхронные: результат приходит сразу, как у sql и ctx.fetch, поэтому await не нужен. С MySQL всё так же, только строка начинается с mysql://, а на месте значения в тексте запроса стоит ?.

Строка соединения​

Qubix.db.connect(...) принимает строку такого вида:

postgres://user:password@host:5432/database?sslmode=require
mysql://user:password@host:3306/database?tls=true
ЧастьЧто значит
схемаpostgres:// или postgresql:// — PostgreSQL; mysql:// или mariadb:// — MySQL и MariaDB
user:password@пользователь базы и его пароль. Что запросам можно, решают права этого пользователя в вашей базе
hostодин узел — имя или адрес. Несколько узлов через запятую не принимаются, путь к файлу сокета вместо узла тоже: подключение идёт только по сети
:portнеобязательно; без порта — 5432 у PostgreSQL и 3306 у MySQL
/databaseимя базы
?…параметры — только из таблицы ниже
Служебные знаки в пароле

Строка соединения читается по правилам адреса, поэтому знаки вроде #, ?, /, % или пробела в имени пользователя и в пароле записывайте в процентной записи. Проще всего собрать строку через encodeURIComponent:

JavaScript
const url = 'postgres://report:' + encodeURIComponent(password) + '@db.example.com:5432/crm'

Параметры строки:

ПараметрБазаЗначенияЕсли не указан
sslmodePostgreSQLdisable, allow, prefer, require, verify-ca, verify-fullprefer: шифрованный канал, если база его поддерживает, иначе открытый
channel_bindingPostgreSQLdisable, prefer, requireprefer
require_authPostgreSQLscram-sha-256 — пароль уходит только по SCRAM: сервер, который просит его иначе, получает отказкак попросит сервер
application_namePostgreSQLлюбое имя — под ним соединение видно в списке соединений базы—
tlsMySQL, MariaDBtrue — шифрование с проверкой сертификата; skip-verify — шифрование без проверки сертификата; preferred — шифрование без проверки, если сервер его поддерживает, иначе открытый канал; false — без шифрованиябез шифрования
Когда сервер ходит в интернет через Tor

Если администратор направил выход сервера в интернет через Tor (вкладка Исходящие запросы), база в интернете подключается, только если соединение проверяет сервер базы по имени: для PostgreSQL добавьте sslmode=verify-full, для MySQL и MariaDB — tls=true. Выход Tor — чужой сервер: без проверки он получил бы пароль базы. sslmode=verify-ca не хватает — без своего корневого сертификата он принимает любой общий сертификат, не сверяя имя, — как и channel_binding=require с require_auth=scram-sha-256: пароль тогда не идёт открытым, но выход, сам принявший шифрование, получает достаточно, чтобы подобрать слабый пароль вне сети. База во внутренней сети из перечня узлов подключается напрямую, и правило к ней не относится.

Другие параметры не принимаются — отказ перечисляет, какие поддерживаются. Параметры, которые заставили бы подключение читать файлы на сервере Qubix (passfile, sslkey, sslcert, sslrootcert, service, servicefile у PostgreSQL и allowAllFiles у MySQL), отвергаются всегда.

Строка без :// считается именем сохранённого подключения, а сохранённых подключений нет: такой вызов получает отказ. Передавайте строку соединения.

Запросы: query и exec​

Qubix.db.connect(...) возвращает объект соединения:

МетодЧто делаетЧто возвращает
query(text, params)выполняет запрос, который отдаёт строкимассив строк; каждая строка — объект, ключи которого — имена столбцов
exec(text, params)выполняет изменяющий оператор: INSERT, UPDATE, DELETE и другиечисло строк, которые оператор изменил
close()закрывает соединениеничего

Значения передаются отдельно от текста запроса — вторым аргументом, массивом, а в тексте на их месте стоят $1, $2… у PostgreSQL и ? у MySQL. Значение не вклеивается в текст запроса, поэтому экранировать его вручную не нужно и подменить запрос через него нельзя. Второй аргумент можно не передавать, если значений нет.

Значение в JavaScriptКак уходит в базу
строка, число, true / false, nullкак есть
Dateдата и время
ArrayBufferдвоичные данные
массив, объектне принимается — отказ. Для столбца JSON передайте JSON.stringify(значение)

Что приходит в строках ответа:

Столбец в базеЗначение в строке ответа
целоечисло. Целое за пределами точного целого JavaScript (Number.MAX_SAFE_INTEGER) приходит строкой, чтобы не потерять цифры
дробное (real, double precision, FLOAT, DOUBLE)число
десятичное (numeric, DECIMAL)строка — так не теряется точность
JSONстрока — разберите её через JSON.parse
дата (DATE)строка вида 2026-09-27
дата и времястрока в формате ISO в UTC, например 2026-09-27T10:00:00Z
двоичное (bytea, BLOB)ArrayBuffer
логическое (boolean в PostgreSQL)true / false. В MySQL BOOLEAN — это целое TINYINT, поэтому приходит число 0 или 1
NULLnull
прочеестрока

У столбцов даты и времени без часового пояса (timestamp в PostgreSQL, DATETIME в MySQL) пояса в самом значении нет: время приходит таким, каким записано, с приписанной Z.

Одинаковые имена столбцов

Столбцы с одинаковыми именами — например, id из двух таблиц — дают в строке одно свойство, и остаётся последнее. Давайте им разные имена через AS.

Закрытие соединения​

close() закрывает соединение. Повторный вызов ничего не ломает, а запрос по закрытому соединению получает отказ. Соединение, которое вы не закрыли, закроется само: у скрипта — сразу после прогона, у обработчика страницы — до ответа посетителю. Закрывайте соединение, как только оно больше не нужно, — удобнее всего в блоке finally.

Все запросы одного соединения идут в одном сеансе базы: посреди прогона соединение не подменяется другим. Закрытие не возвращает место в пределе подключений — в счёт идёт каждая попытка подключиться к узлу за прогон.

Пример: сводка из статистики Qubix в свою базу​

Скрипт считает депозиты за сутки по странам командой sql и записывает итог в вашу таблицу PostgreSQL одной вставкой.

daily-deps-to-db.jsJavaScript
function main() {
const rows = sql`
SELECT geo, countIf(event = 'dep') AS deps
FROM qubix_events
WHERE event_time > now() - INTERVAL 1 DAY
AND geo != ''
GROUP BY geo`
if (!rows.length) return

// Одна вставка на все строки: каждый вызов query и exec идёт в счёт предела запросов.
const values = []
const params = []
for (const r of rows) {
values.push('(now(), $' + (params.length + 1) + ', $' + (params.length + 2) + ')')
params.push(r.geo, r.deps)
}

const db = Qubix.db.connect('postgres://writer:[email protected]:5432/reports')
try {
const inserted = db.exec('INSERT INTO daily_deps (taken_at, country, deps) VALUES ' + values.join(', '), params)
console.log('записано строк:', inserted)
} finally {
db.close()
}
}

Для MySQL замените $1, $2… на ?. Большой объём разбивайте на несколько вставок, но не на вставку для каждой строки: упрётесь в предел запросов.

Куда можно подключаться​

Подключиться можно только к узлу из перечня Узлы баз данных для скриптов. Перечень ведёт администратор на вкладке JavaScript окна Настройки Qubix — о самой вкладке рассказано в статье Система. Перечень один на скрипты и обработчики страниц сайтов; пока он пуст, подключения к базам выключены.

Записи пишутся по одной на строку:

ЗаписьЧто открывает
имя, например db.example.comэтот узел — но только если имя ведёт на адрес в интернете
адрес, например 10.0.0.5, или сеть, например 10.0.0.0/24этот адрес или эту сеть, в том числе во внутренней сети; после этого до базы можно достучаться и по имени, которое ведёт на этот адрес
*любой адрес в интернете, но не внутреннюю сеть

Базу во внутренней сети — вашей или самого сервера Qubix — вписывайте адресом или сетью: одного имени для неё мало. Запись, которую не удалось прочитать (например, сеть с неверной маской), пропускается, и о ней пишется строка в журнал сервера. Правка перечня действует со следующего прогона, перезапуск не нужен.

Пределы​

Пределы задаёт администратор на той же вкладке JavaScript. У скриптов они стоят под перечнем узлов и считаются на один прогон. У обработчиков страниц сайтов пределы свои — в блоке Обработчики страниц сайтов: подключения и запросы там считаются на один запрос страницы, а строки, размер ответа и срок подписаны так же, как у скриптов.

ПределЧто ограничиваетЧто происходит сверх него
Базы данных: макс. подключений на запуск (у обработчиков — Базы данных: макс. подключений на запрос страницы)число подключений: в счёт идёт каждая попытка подключиться к узлу, в том числе неудачная; вызов, отвергнутый раньше — при пустом перечне узлов или перечне без единой читаемой записи, при неверной строке соединения, имени вместо неё или значении не строкой, — в счёт не идётотказ
Базы данных: макс. запросов на запуск (у обработчиков — Базы данных: макс. запросов на запрос страницы)число вызовов query и exec по всем соединениям вместеотказ
Базы данных: макс. строк на запросчисло строк в ответе одного queryлишние строки не читаются: база останавливает запрос, query возвращает первые строки, а в журнал прогона уходит предупреждение
Базы данных: макс. размер ответа, байтобъём ответа базы на один запрос. Считаются все присланные байты вместе со служебными, поэтому предел наступает раньше, чем его достигнет сумма самих значенийотказ, и соединение закрывается
Базы данных: срок запроса, мсвремя одного запроса и одного подключенияотказ. Соединение PostgreSQL после него остаётся рабочим, соединение MySQL закрывается

Кроме этих пределов, запросы останавливает общий срок: время прогона скрипта или Макс. время работы обработчика, мс у обработчика страницы. Правка пределов действует со следующего прогона.

Отказы и что с ними делать​

Отказ — обычная ошибка JavaScript. Её можно перехватить try…catch и продолжить работу. Неперехваченная ошибка останавливает прогон скрипта с ошибкой, а обработчик страницы отвечает посетителю пустым ответом 500; чтобы увидеть причину, повторите запрос в панели Тестовый запуск (см. Бэкенд сайта).

Тексты отказов — по-английски. В таблицах ниже опущено общее начало Qubix.db: , с которого начинается большинство из них. Перечень узлов и пределы названы в текстах так, как они подписаны в английском интерфейсе; вместо N в тексте стоит число.

Подключение и перечень узлов​

ОтказЧто случилосьЧто делать
connections to databases are off: the "Database hosts for scripts" list (System → JavaScript) is emptyперечень узлов пустпопросить администратора вписать узел базы
connections to databases are off: … has no entry that can be read (see the server log)в перечне нет ни одной записи, которую удалось прочитатьадминистратору — исправить записи; непрочитанные названы в журнале сервера
host "db.example.com" is not in the "Database hosts for scripts" list (System → JavaScript)узла нет в перечневписать узел: имя — для базы в интернете, адрес или сеть — для базы во внутренней сети
host "db.example.com" is on an internal network: its address or network must be in the "Database hosts for scripts" list (System → JavaScript)имя есть в перечне, но ведёт на внутренний адресвписать в перечень адрес или сеть базы
host "db.example.com" has no address, host "db.example.com" could not be resolvedу имени из перечня не нашлось адресапроверить имя узла
Qubix.db.connect("crm"): saved connections are not available yet; …передано имя, а не строка соединенияпередать строку соединения
database type "redis" is not supported: use postgres:// or mysql://незнакомая схеманачать строку с postgres://, postgresql://, mysql:// или mariadb://
one host per connection, the connection string names no host, port "…" is not a number from 1 to 65535, cannot read the connection string: …строка соединения собрана невернопроверить строку; служебные знаки в пароле записать в процентной записи
parameter "foo" is not supported (supported: …), sslmode="bogus" is not supported (supported: …), parameter "…" is given more than onceпараметр не из перечня, неверное значение или повтороставить параметры из таблицы выше, каждый по одному разу
this server reaches the internet through Tor, and a Tor exit reads a connection that does not check the database server: add …сервер ходит в интернет через Tor, а строка соединения не проверяет сервер базыдобавить sslmode=verify-full для PostgreSQL, tls=true для MySQL — см. выше
parameter "sslrootcert" is not allowed: it makes the driver read files on the serverпараметр заставил бы подключение читать файлы сервера Qubixубрать параметр
host "/var/run/postgresql" is a unix socket; only TCP connections are allowed, parameter "host" is not allowed: …вместо узла в адресе — путь к сокету или узел в параметрахуказать узел и порт в самом адресе

Пределы и срок​

У обработчика страницы вместо per run в тексте стоит per page request.

ТекстЧто случилосьЧто делать
connection cap exceeded: N per run (limit "Databases: max connections per run")исчерпан предел подключенийоткрыть одно соединение и вести через него все запросы
query cap exceeded: N per run (limit "Databases: max queries per run")исчерпан предел запросовобъединять запросы: много строк — одной вставкой, много чтений — одним запросом
the answer is larger than N bytes (limit "Databases: max response size, bytes")ответ больше предела; соединение закрытовыбирать меньше столбцов и строк; для следующих запросов открыть новое соединение
the database sent more than N bytes while connecting (limit "Databases: max response size, bytes")база прислала слишком много уже при входепроверить, что по этому адресу и порту отвечает именно база данных и что предел объёма ответа не слишком мал
query stopped after N ms (limit "Databases: query timeout, ms")запрос или подключение не уложились в срокускорить запрос или попросить администратора поднять предел
this connection was closed after an answer exceeded the response cap, this connection was closed after a query ran out of timeсоединение закрыто после одного из двух отказов вышеоткрыть новое соединение
the run is out of time; the query was stoppedкончилось время прогона скрипта или обработчикасократить работу, которую делает один прогон
result truncated to N rows (limit "Databases: max rows per query")это не отказ, а предупреждение в журнале прогона: строк больше предела, пришли первыесузить выборку: условие WHERE, LIMIT, свёртка в запросе

Значения и вызовы​

ТекстЧто случилось
this connection is closedзапрос по соединению, которое уже закрыто через close()
parameter #2 is an array; pass a string, number, boolean, null, Date or ArrayBufferзначение — массив
parameter #2 is an object; pass JSON.stringify(value) for a JSON columnзначение — объект
query(text, params): params must be an array, e.g. [42, "text"]значения переданы не массивом
Qubix.db.connect(connection): pass a connection string such as postgres://user:password@host:5432/databaseв Qubix.db.connect передана не строка

Ответы самой базы​

Всё остальное — ответ вашей базы: неверный пароль, нет такой таблицы, ошибка в тексте запроса. Он идёт после Qubix.db: вместе с пояснениями, которые добавляет подключение: например, при неверном пароле у PostgreSQL текст содержит failed SASL auth: FATAL: password authentication failed for user "report". Пароль из строки соединения в текст не попадает, а адреса, на которые ведёт узел, заменены его именем.

Скрипт и обработчик страницы сайта: в чём разница​

СкриптОбработчик страницы сайта
Когда исполняетсяпо расписанию и по кнопке Запуститьна каждом заходе посетителя и по кнопке Запуск в панели Тестовый запуск
Пределысчитаются на прогон; строки под перечнем узловсчитаются на запрос страницы; блок Обработчики страниц сайтов
Отказ по пределу… per run… per page request
Предупреждение об обрезке строкв консоли прогонатолько в консоли тестового запуска: журнал живого захода нигде не показывается
Общий сроквремя прогона скриптаМакс. время работы обработчика, мс
Незакрытые соединениязакрываются сразу после прогоназакрываются до ответа посетителю
Повторное использование соединенийнет: каждый прогон открывает свои соединениянет: каждый заход открывает свои соединения, и вход в базу повторяется при каждом показе страницы
Неперехваченный отказпрогон завершается ошибкойпосетитель получает пустой ответ 500
Посетитель ушёл, не дождавшись ответа—обработчик прерывается, и идущий запрос к базе снимается: изменение, которое он вносил, может как примениться, так и нет
Своё имя Qubix в корне кодасохранение отказывает: имя занято платформойобработчик работает, но Qubix.db ему недоступен

Если обработчик не вернулся даже после своего срока, посетитель получает ответ 503, а соединения этого прогона закрываются, когда обработчик всё-таки завершится.

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

Проверочный запуск пишет по-настоящему

И кнопка Запустить у скрипта, и кнопка Запуск у обработчика выполняют настоящие запросы к вашей базе: изменения через exec применяются сразу. Проверяйте запись на тестовой таблице или тестовой базе.

Что дальше​