SQL PLUS LUA -- Добавление Tarantool/NoSQL к Tarantool/SQL
Руководство «Добавление Tarantool/NoSQL к Tarantool/SQL» содержит описания объектов базы данных NoSQL, доступных из SQL, объектов базы данных SQL, доступных из NoSQL, а также способов вызова SQL из Lua и Lua из SQL.
Heading | Summary |
|---|---|
Some Lua requests that are especially useful for SQL, such as requests to grant privileges | |
Looking at Lua sysview spaces such as space | |
Tarantool's implementation of SQL stored procedures | |
The LUA(...) function | |
Million-row insert, etc. | |
Making equivalents to standard-SQL information_schema tables |
Множество функций не является специфической частью 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: memtxbefore_replace: 'function: 0x40bb4608'on_replace: 'function: 0x40bb45e0'ck_constraint:field_count: 2temporary: falseindex:0: &0unique: trueparts:- type: integeris_nullable: falsefieldno: 1id: 0space_id: 520type: TREEname: pk_unnamed_THINGS_1pk_unnamed_THINGS_1: *0is_local: falseenabled: truename: THINGSid: 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 для создания представлений метаданных.
Из 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).viewview = flags.viewif view == nil then return false endreturn viewend]],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" xWHERE x."id" = y."owner") AS owner,"field_count" AS field_countFROM "_vspace" y;]])
Напомним, что эти функции 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 dofor j = 1, #space[i][7] dosqlstring = "INSERT INTO information_schema_columns VALUES (".. "'" .. space[i][3] .. "'".. ",".. "'" .. space[i][7][j].name .. "'".. ",".. j.. ",".. "'" .. space[i][7][j].type .. "'".. ");"box.execute(sqlstring)endendreturnend
Если теперь вызвать функцию, выполнив команду:
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_numberlocal random_stringrandom_string = ""for x = 1,10,1 dorandom_number = math.random(65, 90)random_string = random_string .. string.char(random_number)endreturn random_stringendfunction main_function()local string_value, t, sql_statementfor i = 1,1000000, 1 dostring_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).
В 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.
Пример:
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).viewview = flags.viewif view == nil then return false endreturn viewend]],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 SELECTCAST(NULL AS STRING) AS table_catalog,CAST(NULL AS STRING) AS table_schema,"name" AS table_name,CASEWHEN _tables_is_view("flags") = TRUE THEN 'VIEW'ELSE 'BASE TABLE' ENDAS 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')
Этот пример также показывает, как с помощью
рекурсивных представлений создавать временные таблицы с
несколькими строками для каждого кортежа в исходном спейсе "_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 dolocal format = vspace[i]["format"]for j = 1, #format dolocal is_nullable = 'YES'if format[j].is_nullable == false thenis_nullable = 'NO'endtable.insert(_G.box.FORMATS,{vspace[i].name, format[j].name, j,is_nullable, format[j].type, vspace[i].id})endendreturn ''endif row_number_ > #_G.box.FORMATS then_G.box.FORMATS = {}return ''endreturn _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 idFROM r_columnsWHERE 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,idFROM r_columnsWHERE data_type <> '';]])box.schema.role.grant('public', 'read', 'space', '_COLUMNS')
Пример:
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).sqlreturn flags.sqlend]],-- 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 SELECTCAST(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_countFROM "_vspace" yWHERE _tables_is_view("flags") = TRUE;]])box.schema.role.grant('public', 'read', 'space', '_VIEWS')
Функция TABLES_IS_VIEW() была описана ранее, см. Представление _TABLES.
Пример:
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).sqlreturn opts.sqlend]],-- 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 SELECTCAST(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_idFROM "_trigger";]])box.schema.role.grant('public', 'read', 'space', '_TRIGGERS')
Пользователям, выполняющим выборку из этого представления, требуется привилегия 'read' для спейса trigger.
Пример:
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 SELECTCAST(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_idFROM "_fk_constraint" y;]])box.schema.role.grant('public', 'read', 'space', '_REFERENTIAL_CONSTRAINTS')
В этом примере child_cols или parent_cols не берутся из спейса fk_constraint, поскольку в стандартном SQL они находятся в отдельной таблице.
Пользователям, выполняющим выборку из этого представления, требуется привилегия 'read' для спейса fk_constraint.
Пример:
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 SELECTCAST(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_idFROM "_ck_constraint" y;]])box.schema.role.grant('public', 'read', 'space', '_CHECK_CONSTRAINTS')
Пользователям, выполняющим выборку из этого представления, требуется привилегия 'read' на спейс ck_constraint.
Содержит только ограничения (первичный ключ и уникальный ключ), которые можно найти, просмотрев спейс _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')