Tarantool CE/EE Documentation portal logo
Помощь
Обновлена 15 сентября 2026 г. в 08:55

SQL PLUS LUA -- Добавление Tarantool/NoSQL к Tarantool/SQL

Руководство «Добавление Tarantool/NoSQL к Tarantool/SQL» содержит описания объектов базы данных NoSQL, доступных из SQL, объектов базы данных SQL, доступных из NoSQL, а также способов вызова SQL из Lua и Lua из SQL.

Heading

Summary

Lua requests

Some Lua requests that are especially useful for SQL, such as requests to grant privileges

System tables

Looking at Lua sysview spaces such as space

Calling Lua routines from SQL

Tarantool's implementation of SQL stored procedures

Executing Lua chunks

The LUA(...) function

Example sessions

Million-row insert, etc.

Lua functions to make views of metadata

Making equivalents to standard-SQL information_schema tables

Lua-запросы

Множество функций не является специфической частью SQL-возможностей Tarantool, а относится к серверу приложений Tarantool Lua и СУБД. Ниже приведены несколько примеров, чтобы было понятно, где искать информацию в других разделах документации Tarantool.

К NoSQL «спейсам» можно обращаться как к SQL «таблицам», и наоборот. Например, предположим, что таблица создана с помощью CREATE TABLE things (id INTEGER PRIMARY KEY, remark SCALAR);

В NoSQL-возможностях Tarantool это отображается как memtx-спейс с именем THINGS с TREE-индексом по первичному ключу ...

tarantool> box.space.THINGS---- engine: memtx  before_replace: 'function: 0x40bb4608'  on_replace: 'function: 0x40bb45e0'  ck_constraint:   field_count: 2  temporary: false  index:    0: &0      unique: true      parts:     - type: integer        is_nullable: false        fieldno: 1      id: 0      space_id: 520      type: TREE      name: pk_unnamed_THINGS_1    pk_unnamed_THINGS_1: *0  is_local: false  enabled: true  name: THINGS  id: 520

Базовые запросы на работу с данными NoSQL basic data operation requests — select, insert, replace, upsert, update, delete — также работают. Особый интерес представляют запросы, доступные только через NoSQL.

Чтобы создать индекс для things (remark) с нестандартной опцией, например, с определенным идентификатором, выполните: box.space.THINGS:create_index('idx_100_things_2', {id=100, parts={2, 'scalar'}})

(Если имя типа данных SQL — SCALAR, то типом NoSQL будет 'scalar', как описано ранее. См. таблицу в разделе Операнды.)

Чтобы предоставить пользователю 'guest' права доступа к базе данных, выполните
box.schema.user.grant('guest', 'execute', 'universe') Чтобы предоставить пользователю 'guest' права SELECT на таблицу things, выполните box.schema.user.grant('guest', 'read', 'space', 'THINGS')
Чтобы предоставить пользователю 'guest' права UPDATE на таблицу things, выполните: box.schema.user.grant('guest', 'read,write', 'space', 'THINGS') Чтобы предоставить пользователю 'guest' права DELETE или INSERT на таблицу things, если чтение не требуется, выполните: box.schema.user.grant('guest', 'write', 'space', 'THINGS')
Чтобы предоставить пользователю 'guest' права DELETE или INSERT на таблицу things, если требуется чтение, выполните: box.schema.user.grant('guest', 'read,write', 'space', 'THINGS') Чтобы предоставить пользователю 'guest' право CREATE TABLE, выполните
box.schema.user.grant('guest', 'read,write', 'space', '_schema') box.schema.user.grant('guest', 'read,write', 'space', '_space') box.schema.user.grant('guest', 'read,write', 'space', '_index') box.schema.user.grant('guest', 'create', 'space')
Чтобы предоставить пользователю 'guest' право CREATE TRIGGER, выполните box.schema.user.grant('guest', 'read', 'space', '_space') box.schema.user.grant('guest', 'read,write', 'space', '_trigger') Чтобы предоставить пользователю 'guest' право CREATE INDEX, выполните
box.schema.user.grant('guest', 'read,write', 'space', '_index') box.schema.user.grant('guest', 'create', 'space')
Чтобы предоставить пользователю 'guest' право CREATE TABLE ... INTEGER PRIMARY KEY AUTOINCREMENT, выполните box.schema.user.grant('guest', 'read,write', 'space', '_schema') box.schema.user.grant('guest', 'read,write', 'space', '_space') box.schema.user.grant('guest', 'read,write', 'space', '_index') box.schema.user.grant('guest', 'create', 'space') box.schema.user.grant('guest', 'read,write', 'space', '_space_sequence')
box.schema.user.grant('guest', 'read,write', 'space', '_sequence')
box.schema.user.grant('guest', 'create', 'sequence') Чтобы написать хранимую процедуру, которая добавляет 5 строк в things, выполните function f() for i = 3, 7 do box.space.THINGS:insert{i, i} end end Описание функций клиентского API см. в разделе "Коннекторы".

Чтобы создать спейсы с именами полей, понятными для SQL, используйте space_object:format(). (Исключение: в Tarantool/NoSQL кортежи могут содержать больше полей, чем описано в предложении format, но в Tarantool/SQL такие поля будут игнорироваться.)

Подробнее о репликации и шардировании данных SQL см. в разделе Шардирование.

О том, как повысить производительность SQL-запросов с помощью предварительной подготовки, см. в разделе box.prepare().

О том, как вызывать SQL из Lua, см. в разделе box.execute([[...]]).

Ограничения: (Issue#2368)
* после выполнения box.schema.user.grant('guest','read,write,execute','universe') пользователь 'guest' сможет создавать таблицы. Но это очень широкий набор привилегий.

Ограничения: (Issue#4659, Issue#4757, Issue#4758) Использование SELECT с * или ORDER BY, или GROUP BY для спейсов, содержащих поля типа map или array, может привести к ошибкам. Любой доступ к спейсам с хеш-индексами может привести к критическим ошибкам в Tarantool версии 2.3 и более ранних.

Системные таблицы

Получить информацию об объектах базы данных, например, названия всех таблиц и их индексов, можно с помощью операторов SELECT. Это делается путем обращения к специальным таблицам только для чтения, которые Tarantool обновляет автоматически при создании или удалении объектов. См. обзорный раздел подмодуля box.space. Имена системных таблиц указаны в нижнем регистре, поэтому всегда заключайте их в "кавычки".

Например, системная таблица _space содержит следующие поля, которые в SQL отображаются как колонки:
NBSP id = числовой идентификатор
NBSP owner = например, 1, если объект создан пользователем 'admin'
NBSP name = имя, использованное в CREATE TABLE
NBSP engine = обычно 'memtx' (можно использовать движок 'vinyl', но он не является движком по умолчанию)
NBSP field_count = иногда 0, но обычно это количество колонок в таблице
NBSP flags = обычно пусто
NBSP format = результат выполнения функции format() в Lua или оператора CREATE в SQL
Пример запроса:
NBSP SELECT "id", "name" FROM "_space";

См. также: Функции Lua для создания представлений метаданных.

Вызов процедур Lua из SQL

Из SQL-запросов можно вызывать функции, написанные на Lua. Это аналог функции «хранимых процедур», которая есть в других SQL-СУБД. Серверные хранимые процедуры в Tarantool пишутся на Lua, а не на диалекте SQL/PSM.

Функции можно вызывать везде, где синтаксис SQL допускает использование литерала или имени колонки для чтения. Параметры функции могут включать любое количество значений SQL. Если результирующий набор оператора SELECT содержит миллион строк, а в списке выборки вызывается недетерминированная функция, то эта функция будет вызвана миллион раз.

Чтобы создать функцию на Lua, которую можно вызывать из SQL, используйте box.schema.func.create(func-name, {options-with-body}) со следующими дополнительными параметрами:

exports = {'LUA', 'SQL'} — указывает, на каких языках можно вызывать функцию. Значение по умолчанию — 'LUA'. Укажите оба значения: 'LUA', 'SQL'.

param_list = {list} — список параметров. Укажите имена типов Lua для каждого параметра функции. Напомним, что имя типа Lua совпадает с именем типа данных SQL, но записывается в нижнем регистре. Тип Lua не должен быть массивом.

Также по возможности рекомендуется указывать {deterministic = true}, так как это позволяет Tarantool генерировать более эффективный байт-код SQL.

В качестве полезного примера приведем общую функцию для декодирования отдельного поля типа 'map' в Lua:

box.schema.func.create('_DECODE',   {language = 'LUA',    returns = 'string',    body = [[function (field, key)             -- If Tarantool version < 2.10.1, replace next line with             -- return require('msgpack').decode(field)[key]             return field[key]             end]],    is_sandboxed = false,    -- If Tarantool version < 2.10.1, replace next line with    -- param_list = {'string', 'string'},    param_list = {'map', 'string'},    exports = {'LUA', 'SQL'},    is_deterministic = true})

Проверим ее работу, например, со спейсом trigger. В этом спейсе есть поле типа 'map' с именем opts, в котором есть ключ sql. Выполнив выборку из спейса и передав поле и имя ключа в DECODE, можно получить список всех тел триггеров.

box.execute([[SELECT _decode("opts", 'sql') FROM "_trigger";]])

Напомним, что SQL преобразует обычные идентификаторы в верхний регистр, поэтому данный пример работает с функцией DECODE. Если бы функция называлась decode, то оператор SELECT должен был бы выглядеть так: box.execute([[SELECT "_decode"("opts", 'sql') FROM "_trigger";]])

Вот еще один пример, который иллюстрирует создание в Tarantool представления, содержащего колонки table_name и table_type, аналогично тому, как они представлены в представлении information_schema.tables в стандарте SQL. Сложность заключается в том, чтобы определить, должно ли значение table_type быть 'BASE TABLE' или 'VIEW', для чего необходимо знать значение поля "flags" в спейсе Tarantool/NoSQL "_space" или "_vspace". Тип поля "flags""map", который SQL плохо понимает. Если бы функций Lua не было, пришлось бы рассматривать это поле как VARBINARY и искать POSITION(X'A476696577C3',"flags") > 0 (A4 — это сигнал MsgPack о том, что далее следует строка из 4 байт, 76696577 — это кодировка UTF8 для 'view', C3 — код MsgPack, означающий true). В любом случае, начиная с версии Tarantool 2.10, функция POSITION() не работает с операндами VARBINARY. Но есть более изящный способ — создать функцию, которая возвращает true, если "flags".view равно true. В данном случае создание функции выглядит так:

box.schema.func.create('TABLES_IS_VIEW',     {language = 'LUA',      returns = 'boolean',      body = [[function (flags)          local view          -- If Tarantool version < 2.10.1, replace next line with          -- view = require('msgpack').decode(flags).view          view = flags.view          if view == nil then return false end          return view          end]],     is_sandboxed = false,     -- If Tarantool version < 2.10.1, replace next line with     -- param_list = {'string'},     param_list = {'map'},     exports = {'LUA', 'SQL'},     is_deterministic = true})

А так создается представление:

box.execute([[CREATE VIEW vtables AS SELECT"name" AS table_name,CASE WHEN tables_is_view("flags") == TRUE THEN 'VIEW'     ELSE 'BASE TABLE' END AS table_type,"id" AS id,"engine" AS engine,(SELECT "name" FROM "_vuser" x WHERE x."id" = y."owner") AS owner,"field_count" AS field_countFROM "_vspace" y;]])

Напомним, что эти функции Lua являются постоянными, поэтому при перезапуске сервера их не нужно объявлять заново.

Выполнение фрагментов кода Lua

Чтобы выполнить код Lua без создания функции, используйте: LUA({Lua-code-string})
где Lua-code-string — это любой объем кода Lua. Строка должна начинаться с 'return '.

Например, так можно вывести количество секунд с начала эпохи: box.execute([[SELECT lua('return os.time()');]])
Например, так можно вывести параметр конфигурации базы данных: box.execute([[SELECT lua('return box.cfg.memtx_memory');]]) Например, так будет возвращено FALSE, так как Lua nil и box.NULL — это то же самое, что SQL NULL: box.execute([[SELECT lua('return box.NULL') IS NOT NULL;]])

Предупреждение: SQL-запрос не должен вызывать функцию Lua или выполнять фрагмент кода Lua, который обращается к спейсу, лежащему в основе любой SQL-таблицы, к которой обращается этот SQL-запрос. Например, если функция f() содержит запрос "box.space.TEST:insert{0}", то SQL-запрос "SELECT f() FROM test;" попытается обратиться к одному и тому же спейсу двумя способами. Результатом такого конфликта может стать зависание или бесконечный цикл.

Примеры сеансов

Пример сеанса — создание, вставка, выборка

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

То есть способ наполнения таблицы выглядит так: CREATE TABLE t1 (c1 INTEGER PRIMARY KEY, c2 STRING); CREATE TABLE t2 (c1 INTEGER PRIMARY KEY, x2 STRING); INSERT INTO t1 VALUES (1, 'A'), (2, 'B'), (3, 'C'); INSERT INTO t1 VALUES (4, 'D'), (5, 'E'), (6, 'F'); INSERT INTO t2 VALUES (1, 'C'), (4, 'A'), (6, NULL); CREATE VIEW v AS SELECT * FROM t1 NATURAL JOIN t2; SELECT * FROM v WHERE c2 IS NOT NULL ORDER BY c1;

Таким образом, сеанс выглядит следующим образом:
box.cfg{} box.execute([[CREATE TABLE t1 (c1 INTEGER PRIMARY KEY, c2 STRING);]])

box.execute([[CREATE TABLE t2 (c1 INTEGER PRIMARY KEY, x2 STRING);]])

box.execute([[INSERT INTO t1 VALUES (1, 'A'), (2, 'B'), (3, 'C');]])

box.execute([[INSERT INTO t1 VALUES (4, 'D'), (5, 'E'), (6, 'F');]])

box.execute([[INSERT INTO t2 VALUES (1, 'C'), (4, 'A'), (6, NULL);]])
box.execute([[CREATE VIEW v AS SELECT * FROM t1 NATURAL JOIN t2;]])
box.execute([[SELECT * FROM v WHERE c2 IS NOT NULL ORDER BY c1;]])

Если выполнить указанные выше запросы с использованием Tarantool в качестве клиента (при условии, что объекты базы данных еще не существуют), выполнение завершится успешно, и в результате будет выведено:

tarantool> box.execute([[SELECT * FROM v WHERE c2 IS NOT NULL ORDER BY c1;]])---- - [1, 'A', 'C']- [4, 'D', 'A']- [6, 'F', null]

Пример сеанса — получение списка колонок

Ниже приведена функция, которая создает таблицу со списком всех колонок и их типов Lua для всех таблиц. Эта функция не является обязательной, так как вместо нее можно создать представление _COLUMNS. Она лишь показывает на примере простого кода на Lua, как создать базовую таблицу вместо представления.

function create_information_schema_columns()  box.execute([[DROP TABLE IF EXISTS information_schema_columns;]])  box.execute([[CREATE TABLE information_schema_columns (                    table_name STRING,                    column_name STRING,                    ordinal_position INTEGER,                    data_type STRING,                    PRIMARY KEY (table_name, column_name));]]);  local space = box.space._vspace:select()  local sqlstring = ''  for i = 1, #space do      for j = 1, #space[i][7] do          sqlstring = "INSERT INTO information_schema_columns VALUES ("                  .. "'" .. space[i][3] .. "'"                  .. ","                  .. "'" .. space[i][7][j].name .. "'"                  .. ","                  .. j                  .. ","                  .. "'" .. space[i][7][j].type .. "'"                  .. ");"          box.execute(sqlstring)      end  end  returnend

Если теперь вызвать функцию, выполнив команду: create_information_schema_columns()
можно увидеть, что существует таблица с именем information_schema_columns, содержащая table_name, column_name, ordinal_position и data_type для всех доступных объектов.

Пример сеанса — вставка миллиона строк

Это вариация руководства по Lua "Вставка одного миллиона кортежей с помощью хранимой процедуры Lua". Отличия заключаются в следующем: создание таблицы выполняется с помощью SQL-оператора CREATE TABLE, а вставка — с помощью SQL-оператора INSERT. В остальном всё так же. Это возможно благодаря совместимости Lua и SQL, так же как Lua совместим с NoSQL.

box.execute([[CREATE TABLE tester (s1 INTEGER PRIMARY KEY, s2 STRING);]])function string_function()  local random_number  local random_string  random_string = ""  for x = 1,10,1 do    random_number = math.random(65, 90)    random_string = random_string .. string.char(random_number)  end  return random_stringendfunction main_function()    local string_value, t, sql_statement    for i = 1,1000000, 1 do    string_value = string_function()    sql_statement = "INSERT INTO tester VALUES (" .. i .. ",'" .. string_value .. "');"    box.execute(sql_statement)    endendstart_time = os.clock()main_function()end_time = os.clock()'insert done in ' .. end_time - start_time .. ' seconds'

Ограничения: Функция выполняется дольше, чем исходная (Tarantool/NoSQL).

Функции Lua для создания представлений метаданных

В Tarantool входят не все представления стандартного SQL information_schema, предназначенные для просмотра метаданных, то есть "данных о данных". Ниже приведен код на Lua и SQL для создания аналогов: _TABLES — почти эквивалент INFORMATION_SCHEMA.TABLES
_COLUMNS — почти эквивалент INFORMATION_SCHEMA.COLUMNS
_VIEWS — почти эквивалент INFORMATION_SCHEMA.VIEWS _TRIGGERS — почти эквивалент INFORMATION_SCHEMA.TRIGGERS _REFERENTIAL_CONSTRAINTS — почти эквивалент INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS _CHECK_CONSTRAINTS — почти эквивалент INFORMATION_SCHEMA.CHECK_CONSTRAINTS _TABLE_CONSTRAINTS — почти эквивалент INFORMATION_SCHEMA.TABLE_CONSTRAINTS.
Для каждого представления будет приведен пример SELECT из него и сам код. Пользователи, которым нужны метаданные, могут просто скопировать этот код. Используйте этот код только с Tarantool версии 2.3.0 или более поздней. Для более ранних версий Tarantool может быть полезен оператор PRAGMA.

Представление TABLES

Пример:

tarantool>SELECT * FROM _tables WHERE id > 340 LIMIT 5;OK 5 rows selected (0.0 seconds)<table class="tableblock frame-all grid-all stretch"><colgroup><col style="width: 20%;"><col style="width: 13.3333%;"><col style="width: 20%;"><col style="width: 13.3333%;"><col style="width: 6.6666%;"><col style="width: 6.6666%;"><col style="width: 6.6666%;"><col style="width: 13.3336%;"></colgroup><tbody><tr><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IFRBQkxFX0NBVEFMT0c=)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IFRBQkxFX1NDSEVNQQ==)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IFRBQkxFX05BTUU=)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IFRBQkxFX1RZUEU=)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IElE)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IEVOR0lORQ==)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IE9XTkVS)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IEZJRUxEX0NPVU5U)}</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IE5VTEwgIE5VTEwgIE5VTEwgIE5VTEwgIE5VTEw=)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IE5VTEwgIE5VTEwgIE5VTEwgIE5VTEwgIE5VTEw=)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IF9ma19jb25zdHJhaW50ICBfY2tfY29uc3RyYWludCAgX2Z1bmNfaW5kZXggIF9DT0xVTU5TICBfVklFV1M=)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IEJBU0UgVEFCTEUgIEJBU0UgVEFCTEUgIEJBU0UgVEFCTEUgIFZJRVcgIFZJRVc=)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IDM1NiAgMzY0ICAzNzIgIDUxMyAgNTE0)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IG1lbXR4ICBtZW10eCAgbWVtdHggIG1lbXR4ICBtZW10eA==)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IGFkbWluICBhZG1pbiAgYWRtaW4gIGFkbWluICBhZG1pbg==)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IDAgICAgICAgICAwICAgICAgICAgMCAgICAgICAgIDggICAgICAgICA3)}</p></td></tr></tbody></table>

Определение функции и оператора CREATE VIEW:

box.schema.func.drop('_TABLES_IS_VIEW',{if_exists = true})box.schema.func.create('_TABLES_IS_VIEW',     {language = 'LUA',      returns = 'boolean',      body = [[function (flags)          local view          -- If Tarantool version < 2.10.1, replace next line with          -- view = require('msgpack').decode(flags).view          view = flags.view          if view == nil then return false end          return view          end]],     is_sandboxed = false,     -- If Tarantool version < 2.10.1, replace next line with     -- param_list = {'string'},     param_list = {'map'},     exports = {'LUA', 'SQL'},     is_deterministic = true})box.schema.role.grant('public', 'execute', 'function', '_TABLES_IS_VIEW')pcall(function ()    box.schema.role.revoke('public', 'read', 'space', '_TABLES', {if_exists = true})    end)box.execute([[DROP VIEW IF EXISTS _tables;]])box.execute([[CREATE VIEW _tables AS SELECT    CAST(NULL AS STRING) AS table_catalog,    CAST(NULL AS STRING) AS table_schema,    "name" AS table_name,    CASE        WHEN _tables_is_view("flags") = TRUE THEN 'VIEW'        ELSE 'BASE TABLE' END        AS table_type,    "id" AS id,    "engine" AS engine,    (SELECT "name" FROM "_vuser" x WHERE x."id" = y."owner") AS owner,    "field_count" AS field_countFROM "_vspace" y;]])box.schema.role.grant('public', 'read', 'space', '_TABLES')

Представление COLUMNS

Этот пример также показывает, как с помощью рекурсивных представлений создавать временные таблицы с несколькими строками для каждого кортежа в исходном спейсе "_vspace". Для этого потребуется глобальная переменная _G.box.FORMATS в качестве временной статической переменной.

Предупреждение: используйте этот код только с Tarantool версии 2.3.2 или новее. Использование с более ранними версиями приведет к срабатыванию assertion. Подробнее см. Issue#4504.

Пример:

tarantool>SELECT * FROM _columns WHERE ordinal_position = 9;OK 6 rows selected (0.0 seconds)<table class="tableblock frame-all grid-all stretch"><colgroup><col style="width: 10.5263%;"><col style="width: 10.5263%;"><col style="width: 26.3157%;"><col style="width: 10.5263%;"><col style="width: 15.7894%;"><col style="width: 10.5263%;"><col style="width: 10.5263%;"><col style="width: 5.2634%;"></colgroup><tbody><tr><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IENBVEFMT0dfTkFNRQ==)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IFNDSEVNQV9OQU1F)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IFRBQkxFX05BTUU=)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IENPTFVNTl9OQU1F)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IE9SRElOQUxfUE9TSVRJT04=)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IElTX05VTExBQkxF)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IERBVEFfVFlQRQ==)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IElE)}</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IE5VTEwgIE5VTEwgIE5VTEwgIE5VTEwgIE5VTEw=)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IE5VTEwgIE5VTEwgIE5VTEwgIE5VTEwgIE5VTEw=)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IF9zZXF1ZW5jZSAgX3ZzZXF1ZW5jZSAgX2Z1bmMgIF9ma19jb25zdHJhaW50ICBfUkVGRVJFTlRJQUxfQ09OU1RSQUlOVFM=)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IGN5Y2xlICBjeWNsZSAgcmV0dXJucyAgcGFyZW50X2NvbHMgIE1BVENIX09QVElPTg==)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IDkgICAgICAgICAgICAgICAgIDkgICAgICAgICAgICAgICAgIDkgICAgICAgICAgICAgICAgIDkgICAgICAgICAgICAgICAgIDk=)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IFlFUyAgWUVTICBZRVMgIFlFUyAgWUVT)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IGJvb2xlYW4gIGJvb2xlYW4gIHN0cmluZyAgYXJyYXkgIHN0cmluZw==)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IDI4NCAgMjg2ICAyOTYgIDM1NiAgNTE4)}</p></td></tr></tbody></table>

Определение функции и оператора CREATE VIEW:

box.schema.func.drop('_COLUMNS_FORMATS', {if_exists = true})box.schema.func.create('_COLUMNS_FORMATS',    {language = 'LUA',     returns = 'scalar',     body = [[     function (row_number_, ordinal_position)         if row_number_ == 0 then             _G.box.FORMATS = {}             local vspace = box.space._vspace:select()             for i = 1, #vspace do                 local format = vspace[i]["format"]                 for j = 1, #format do                     local is_nullable = 'YES'                     if format[j].is_nullable == false then                         is_nullable = 'NO'                     end                     table.insert(_G.box.FORMATS,                                  {vspace[i].name, format[j].name, j,                                   is_nullable, format[j].type, vspace[i].id})                 end             end             return ''         end         if row_number_ > #_G.box.FORMATS then             _G.box.FORMATS = {}             return ''         end         return _G.box.FORMATS[row_number_][ordinal_position]     end     ]],    param_list = {'integer', 'integer'},    exports = {'LUA', 'SQL'},    is_sandboxed = false,    setuid = false,    is_deterministic = false})box.schema.role.grant('public', 'execute', 'function', '_COLUMNS_FORMATS')pcall(function ()    box.schema.role.revoke('public', 'read', 'space', '_COLUMNS', {if_exists = true})    end)box.execute([[DROP VIEW IF EXISTS _columns;]])box.execute([[CREATE VIEW _columns ASWITH RECURSIVE r_columns AS(SELECT 0 AS row_number_,      '' AS table_name,      '' AS column_name,      0 AS ordinal_position,      '' AS is_nullable,      '' AS data_type,      0 AS idUNION ALLSELECT row_number_ + 1 AS row_number_,       _columns_formats(row_number_, 1) AS table_name,       _columns_formats(row_number_, 2) AS column_name,       _columns_formats(row_number_, 3) AS ordinal_position,       _columns_formats(row_number_, 4) AS is_nullable,       _columns_formats(row_number_, 5) AS data_type,       _columns_formats(row_number_, 6) AS id    FROM r_columns    WHERE row_number_ == 0 OR row_number_ <= lua('return #_G.box.FORMATS + 1'))SELECT CAST(NULL AS STRING) AS catalog_name,       CAST(NULL AS STRING) AS schema_name,       table_name,       column_name,       ordinal_position,       is_nullable,       data_type,       id    FROM r_columns    WHERE data_type <> '';]])box.schema.role.grant('public', 'read', 'space', '_COLUMNS')

Представление VIEWS

Пример:

tarantool>SELECT table_name, substr(view_definition,1,20), id, owner, field_count FROM _views LIMIT 5;OK 5 rows selected (0.0 seconds)<table class="tableblock frame-all grid-all stretch"><colgroup><col style="width: 33.3333%;"><col style="width: 40%;"><col style="width: 6.6666%;"><col style="width: 6.6666%;"><col style="width: 13.3335%;"></colgroup><tbody><tr><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IFRBQkxFX05BTUU=)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IFNVQlNUUihWSUVXX0RFRklOSVRJT04sMSwyMCk=)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IElE)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IE9XTkVS)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IEZJRUxEX0NPVU5U)}</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IF9DT0xVTU5TICBfVFJJR0dFUlMgIF9DSEVDS19DT05TVFJBSU5UUyAgX1JFRkVSRU5USUFMX0NPTlNUUkFJTlRTICBfVEFCTEVfQ09OU1RSQUlOVFM=)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IENSRUFURSBWSUVXIF9jb2x1bW5zICBDUkVBVEUgVklFVyBfdHJpZ2dlciAgQ1JFQVRFIFZJRVcgX2NoZWNrX2MgIENSRUFURSBWSUVXIF9yZWZlcmVuICBDUkVBVEUgVklFVyBfdGFibGVfYw==)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IDUxMyAgNTE1ICA1MTcgIDUxOCAgNTE5)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IGFkbWluICBhZG1pbiAgYWRtaW4gIGFkbWluICBhZG1pbg==)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IDggICAgICAgICAgICA0ICAgICAgICAgICAgOCAgICAgICAgICAgMTIgICAgICAgICAgIDEx)}</p></td></tr></tbody></table>

Определение функции и оператора CREATE VIEW:

box.schema.func.drop('_VIEWS_DEFINITION',{if_exists = true})box.schema.func.create('_VIEWS_DEFINITION',    {language = 'LUA',     returns = 'string',     body = [[function (flags)              -- If Tarantool version < 2.10.1, replace next line with              -- return require('msgpack').decode(flags).sql              return flags.sql              end]],     -- If Tarantool version < 2.10.1, replace next line with     -- param_list = {'string'},     param_list = {'map'},     exports = {'LUA', 'SQL'},     is_sandboxed = false,     setuid = false,     is_deterministic = false})box.schema.role.grant('public', 'execute', 'function', '_VIEWS_DEFINITION')pcall(function ()    box.schema.role.revoke('public', 'read', 'space', '_VIEWS', {if_exists = true})    end)box.execute([[DROP VIEW IF EXISTS _views;]])box.execute([[CREATE VIEW _views AS SELECT    CAST(NULL AS STRING) AS table_catalog,    CAST(NULL AS STRING) AS table_schema,    "name" AS table_name,    CAST(_views_definition("flags") AS STRING) AS VIEW_DEFINITION,    "id" AS id,    (SELECT "name" FROM "_vuser" x WHERE x."id" = y."owner") AS owner,    "field_count" AS field_count    FROM "_vspace" y    WHERE _tables_is_view("flags") = TRUE;]])box.schema.role.grant('public', 'read', 'space', '_VIEWS')

Функция TABLES_IS_VIEW() была описана ранее, см. Представление _TABLES.

Представление TRIGGERS

Пример:

tarantool>SELECT trigger_name, opts_sql FROM _triggers;OK 2 rows selected (0.0 seconds)<table class="tableblock frame-all grid-all stretch"><colgroup><col style="width: 14.2857%;"><col style="width: 85.7143%;"></colgroup><tbody><tr><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IFRSSUdHRVJfTkFNRQ==)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IE9QVFNfU1FM)}</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IFRISU5HUzFfQUQgIFRISU5HUzFfQkk=)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IENSRUFURSBUUklHR0VSIHRoaW5nczFfYWQgQUZURVIgREVMRVRFIE9OIHRoaW5nczEgRk9SIEVBQ0ggUk9XIEJFR0lOIERFTEVURSBGUk9NIHRoaW5nczI7IEVORDsgIENSRUFURSBUUklHR0VSIHRoaW5nczFfYmkgQkVGT1JFIElOU0VSVCBPTiB0aGluZ3MxIEZPUiBFQUNIIFJPVyBCRUdJTiBERUxFVEUgRlJPTSB0aGluZ3MyOyBFTkQ7)}</p></td></tr></tbody></table>

Определение функции и оператор CREATE VIEW:

box.schema.func.drop('_TRIGGERS_OPTS_SQL',{if_exists = true})box.schema.func.create('_TRIGGERS_OPTS_SQL',    {language = 'LUA',     returns = 'string',     body = [[function (opts)              -- If Tarantool version < 2.10.1, replace next line with              -- return require('msgpack').decode(opts).sql              return opts.sql              end]],     -- If Tarantool version < 2.10.1, replace next line with     -- param_list = {'string'},     param_list = {'map'},     exports = {'LUA', 'SQL'},     is_sandboxed = false,     setuid = false,     is_deterministic = false})box.schema.role.grant('public', 'execute', 'function', '_TRIGGERS_OPTS_SQL')pcall(function ()    box.schema.role.revoke('public', 'read', 'space', '_TRIGGERS', {if_exists = true})    end)box.execute([[DROP VIEW IF EXISTS _triggers;]])box.execute([[CREATE VIEW _triggers AS SELECT    CAST(NULL AS STRING) AS trigger_catalog,    CAST(NULL AS STRING) AS trigger_schema,    "name" AS trigger_name,    CAST(_triggers_opts_sql("opts") AS STRING) AS opts_sql,    "space_id" AS space_id    FROM "_trigger";]])box.schema.role.grant('public', 'read', 'space', '_TRIGGERS')

Пользователям, выполняющим выборку из этого представления, требуется привилегия 'read' для спейса trigger.

Представление REFERENTIAL_CONSTRAINTS

Пример:

tarantool>SELECT constraint_name, update_rule, delete_rule, match_option,referencing, referencedFROM _referential_constraints;OK 2 rows selected (0.0 seconds)<table class="tableblock frame-all grid-all stretch"><colgroup><col style="width: 16.6666%;"><col style="width: 16.6666%;"><col style="width: 16.6666%;"><col style="width: 16.6666%;"><col style="width: 16.6666%;"><col style="width: 16.667%;"></colgroup><tbody><tr><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IENPTlNUUkFJTlRfTkFNRQ==)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IFVQREFURV9SVUxF)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IERFTEVURV9SVUxF)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IE1BVENIX09QVElPTg==)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IFJFRkVSRU5DSU5H)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IFJFRkVSRU5DRUQ=)}</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IGZrX3VubmFtZWRfVEhJTkdTMl8xICBma191bm5hbWVkX1RISU5HUzNfMQ==)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IG5vX2FjdGlvbiAgbm9fYWN0aW9u)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IG5vX2FjdGlvbiAgbm9fYWN0aW9u)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IHNpbXBsZSAgc2ltcGxl)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IFRISU5HUzIgIFRISU5HUzM=)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IFRISU5HUzEgIFRISU5HUzE=)}</p></td></tr></tbody></table>

Определение оператора CREATE VIEW:

pcall(function ()    box.schema.role.revoke('public', 'read', 'space', '_REFERENTIAL_CONSTRAINTS', {if_exists = true})    end)box.execute([[DROP VIEW IF EXISTS _referential_constraints;]])box.execute([[CREATE VIEW _referential_constraints AS SELECT    CAST(NULL AS STRING) AS constraint_catalog,    CAST(NULL AS STRING) AS constraint_schema,    "name" AS constraint_name,    CAST(NULL AS STRING) AS unique_constraint_catalog,    CAST(NULL AS STRING) AS unique_constraint_schema,    '' AS unique_constraint_name,    "on_update" AS update_rule,    "on_delete" AS delete_rule,    "match" AS match_option,    (SELECT "name" FROM "_vspace" x WHERE x."id" = y."child_id") AS referencing,    (SELECT "name" FROM "_vspace" x WHERE x."id" = y."parent_id") AS referenced,    "is_deferred" AS is_deferred,    "child_id" AS child_id,    "parent_id" AS parent_id    FROM "_fk_constraint" y;]])box.schema.role.grant('public', 'read', 'space', '_REFERENTIAL_CONSTRAINTS')

В этом примере child_cols или parent_cols не берутся из спейса fk_constraint, поскольку в стандартном SQL они находятся в отдельной таблице.

Пользователям, выполняющим выборку из этого представления, требуется привилегия 'read' для спейса fk_constraint.

Представление CHECK_CONSTRAINTS

Пример:

tarantool>SELECT constraint_name, check_clause, space_name, languageFROM _check_constraints;OK 3 rows selected (0.0 seconds)<table class="tableblock frame-all grid-all stretch"><colgroup><col style="width: 33.3333%;"><col style="width: 33.3333%;"><col style="width: 16.6666%;"><col style="width: 16.6668%;"></colgroup><tbody><tr><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IENPTlNUUkFJTlRfTkFNRQ==)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IENIRUNLX0NMQVVTRQ==)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IFNQQUNFX05BTUU=)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IExBTkdVQUdF)}</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IGNrX3VubmFtZWRfRW1wbG95ZWVzXzEgIGNrX3VubmFtZWRfQ3JpdGljc18xICBja191bm5hbWVkX0FDVE9SU18x)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IGZpcnN0X25hbWUgTElLRSAn0JLQu9Cw0LQlJyAgZmlyc3RfbmFtZSBMSUtFICdWbGFkJScgIHNhbGFyeSA+IDA=)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IEVtcGxveWVlcyAgQ3JpdGljcyAgQUNUT1JT)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IFNRTCAgU1FMICBTUUw=)}</p></td></tr></tbody></table>

Определение оператора CREATE VIEW:

pcall(function ()    box.schema.role.revoke('public', 'read', 'space', '_CHECK_CONSTRAINTS', {if_exists = true})    end)box.execute([[DROP VIEW IF EXISTS _check_constraints;]])box.execute([[CREATE VIEW _check_constraints AS SELECT    CAST(NULL AS STRING) AS constraint_catalog,    CAST(NULL AS STRING) AS constraint_schema,    "name" AS constraint_name,    "code" AS check_clause,    (SELECT "name" FROM "_vspace" x WHERE x."id" = y."space_id") AS space_name,    "language" AS language,    "is_deferred" AS is_deferred,    "space_id" AS space_id    FROM "_ck_constraint" y;]])box.schema.role.grant('public', 'read', 'space', '_CHECK_CONSTRAINTS')

Пользователям, выполняющим выборку из этого представления, требуется привилегия 'read' на спейс ck_constraint.

Представление TABLE_CONSTRAINTS

Содержит только ограничения (первичный ключ и уникальный ключ), которые можно найти, просмотрев спейс _index. Это не список индексов, то есть не эквивалент INFORMATION_SCHEMA.STATISTICS. Колонки индекса не включены, так как в стандартном SQL они находились бы в отдельной таблице.

Пример:

tarantool>SELECT constraint_name, constraint_type, table_name, id, iid, index_typeFROM _table_constraintsLIMIT 5;OK 5 rows selected (0.0 seconds)<table class="tableblock frame-all grid-all stretch"><colgroup><col style="width: 25%;"><col style="width: 25%;"><col style="width: 16.6666%;"><col style="width: 8.3333%;"><col style="width: 8.3333%;"><col style="width: 16.6668%;"></colgroup><tbody><tr><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IENPTlNUUkFJTlRfTkFNRQ==)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IENPTlNUUkFJTlRfVFlQRQ==)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IFRBQkxFX05BTUU=)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IElE)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IElJRA==)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IElOREVYX1RZUEU=)}</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IHByaW1hcnkgIHByaW1hcnkgIG5hbWUgIHByaW1hcnkgIG5hbWU=)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IFBSSU1BUlkgIFBSSU1BUlkgIFVOSVFVRSAgUFJJTUFSWSAgVU5JUVVF)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IF9zY2hlbWEgIF9jb2xsYXRpb24gIF9jb2xsYXRpb24gIF92Y29sbGF0aW9uICBfdmNvbGxhdGlvbg==)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IDI3MiAgMjc2ICAyNzYgIDI3NyAgMjc3)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IDAgICAgMCAgICAxICAgIDAgICAgMQ==)}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">{decodeBase64(IHRyZWUgIHRyZWUgIHRyZWUgIHRyZWUgIHRyZWU=)}</p></td></tr></tbody></table>

Определение функции и оператора CREATE VIEW:

box.schema.func.drop('_TABLE_CONSTRAINTS_OPTS_UNIQUE',{if_exists = true})function _TABLE_CONSTRAINTS_OPTS_UNIQUE (opts) return require('msgpack').decode(opts).unique endbox.schema.func.create('_TABLE_CONSTRAINTS_OPTS_UNIQUE',    {language = 'LUA',     returns = 'boolean',     body = [[function (opts) return require('msgpack').decode(opts).unique end]],     param_list = {'string'},     exports = {'LUA', 'SQL'},     is_sandboxed = false,     setuid = false,     is_deterministic = false})box.schema.role.grant('public', 'execute', 'function', '_TABLE_CONSTRAINTS_OPTS_UNIQUE')pcall(function ()box.schema.role.revoke('public', 'read', 'space', '_TABLE_CONSTRAINTS', {if_exists = true})end)box.execute([[DROP VIEW IF EXISTS _table_constraints;]])box.execute([[CREATE VIEW _table_constraints AS SELECTCAST(NULL AS STRING) AS constraint_catalog,CAST(NULL AS STRING) AS constraint_schema,"name" AS constraint_name,(SELECT "name" FROM "_vspace" x WHERE x."id" = y."id") AS table_name,CASE WHEN "iid" = 0 THEN 'PRIMARY' ELSE 'UNIQUE' END AS constraint_type,CAST(NULL AS STRING) AS initially_deferrable,CAST(NULL AS STRING) AS deferred,CAST(NULL AS STRING) AS enforced,"id" AS id,"iid" AS iid,"type" AS index_typeFROM "_vindex" yWHERE _table_constraints_opts_unique("opts") = TRUE;]])box.schema.role.grant('public', 'read', 'space', '_TABLE_CONSTRAINTS')