diff --git a/docs/architecture/system_tables.md b/docs/architecture/system_tables.md
index 12be29acafa4a96b85e9adae324b97585dfe95a6..5a171814951b3fd1d98db6ae28429c84c935c1ad 100644
--- a/docs/architecture/system_tables.md
+++ b/docs/architecture/system_tables.md
@@ -81,6 +81,37 @@ Picodata 23.06.0-287-ga98dc6919
 * `id` (unique), parts: `[table_id, id]`
 * `name` (unique), parts: `[table_id, name]`
 
+### _pico_routine {: #_pico_routine }
+
+Содержит информацию о пользовательских
+[хранимых процедурах](../reference/sql_queries.md#proc) Picodata.
+
+Поля:
+
+* `id`: (_unsigned_) — идентификатор (тип `u32`, первичный ключ)
+* `name`: (_string_) — имя (уникальный индекс)
+* `kind`: (_string_) — тип хранимого объекта: функция или процедура
+* `params`: (_array_) — таблица с типами параметров объекта, в виде  `[
+  {type: 'int', mode: 'in', default: 42}, {type: 'text'} ]`.
+* `returns`: (_array_) — тип возвращаемого результата. Для процедур это
+  пустой массив `[]`, для функций — массив типов в возвращаемом кортеже
+* `language`: (_string_) — язык тела процедуры (например, `SQL`)
+* `body`: (_string_) — тело основной части хранимой процедуры
+* `security`: (_string_) — режим безопасности, определяющий, от чьего
+  имени будет исполнена процедура (`invoker` — от имени вызывающего,
+  `definer` — от имени стороннего пользователя)
+* `operable`: (_boolean_) —  признак доступности процедуры (для
+  `prepare` — _false_, для `commit` — _true_)
+* `schema_version`: (_unsigned_) — версия схемы данных в Raft на момент
+  изменения хранимой процедуры
+* `owner`: (_unsigned_) — идентификатор владельца (создателя) хранимой
+  процедуры
+
+Индексы:
+
+* `id` (unique), parts: `[id]`
+* `name` (unique), parts: `[name]`
+
 ## Описание свойств кластера {: #cluster_properties }
 
 ### _pico_property {: #_pico_property }
diff --git a/docs/images/ebnf/body.svg b/docs/images/ebnf/body.svg
new file mode 100644
index 0000000000000000000000000000000000000000..132b3b9dafce002c09a665f4a8c6cc5d1b219e58
Binary files /dev/null and b/docs/images/ebnf/body.svg differ
diff --git a/docs/images/ebnf/create_procedure.svg b/docs/images/ebnf/create_procedure.svg
new file mode 100644
index 0000000000000000000000000000000000000000..bf3779bd46a7b51453381d5d572ddec1d6805030
Binary files /dev/null and b/docs/images/ebnf/create_procedure.svg differ
diff --git a/docs/images/ebnf/ddl.svg b/docs/images/ebnf/ddl.svg
index 5e9bb2ad81c492b7f1aacddf6a5470d74c5f5cc8..3069cf8472d06dedd49569d24574cdb38042adb3 100644
Binary files a/docs/images/ebnf/ddl.svg and b/docs/images/ebnf/ddl.svg differ
diff --git a/docs/images/ebnf/drop_procedure.svg b/docs/images/ebnf/drop_procedure.svg
new file mode 100644
index 0000000000000000000000000000000000000000..2e429469ec169061b123f0ebbe0dc035b17ef8c4
Binary files /dev/null and b/docs/images/ebnf/drop_procedure.svg differ
diff --git a/docs/overview/glossary.md b/docs/overview/glossary.md
index 7a43a0a169ea488f27e810d82815f96472b82368..7e84ffcbab085492fd057341e5b00d8a7de72483 100644
--- a/docs/overview/glossary.md
+++ b/docs/overview/glossary.md
@@ -384,18 +384,41 @@ sharding_key:
 
 В этом подразделе собраны определения терминов, встречающихся при работе с распределенным SQL в Picodata.
 
-**Data Definition Language (DDL)**. Язык описания схемы данных. Набор команд для управления таблицами в SQL (создание/удаление таблиц).
+### Data Definition Language (DDL) {: #ddl }
 
-**Data Modification Language (DML)**. Язык модификации данных. Набор команд для изменения данных в таблицах SQL (вставка/обновление/удаление строк).
+Язык описания схемы данных. Набор команд для управления таблицами в SQL (создание/удаление таблиц).
 
-**Data Query Language (DQL)**. Язык получения данных (команда `SELECT` для чтения данных из таблиц).
+### Data Modification Language (DML) {: #dml }
 
-**Команда SQL**. Запрос (и, опционально, значения передаваемых параметров) к базе данных Tarantool на языке SQL.
+Язык модификации данных. Набор команд для изменения данных в таблицах SQL (вставка/обновление/удаление строк).
 
-**План запроса**. Схема и последовательность шагов, требуемая для исполнения запрос. План строится на узле-маршрутизаторе и исполняется во всем кластере.
+### Data Query Language (DQL) {: #dql }
 
-**Проекция**. Фильтрация или модификация столбцов таблицы.
+Язык получения данных (команда `SELECT` для чтения данных из таблиц).
 
-**Ключ шардирования**. Столбец, по которому таблица распределена в кластере.
+### Команда SQL  {: #query }
 
-**Материализация данных**. Один из этапов исполнения распределенного SQL-запроса, при котором на отдельном узле промежуточные данные сохраняются в его памяти.
+Запрос (и, опционально, значения передаваемых параметров) к базе данных Tarantool на языке SQL.
+
+### План запроса {: #execution_plan }
+
+Схема и последовательность шагов, требуемая для исполнения запрос. План строится на узле-маршрутизаторе и исполняется во всем кластере.
+
+### Проекция {: #projection }
+
+Фильтрация или модификация столбцов таблицы.
+
+### Ключ шардирования {: #sharding_key }
+
+Столбец, по которому таблица распределена в кластере.
+
+### Материализация данных {: #data_materialization }
+
+Один из этапов исполнения распределенного SQL-запроса, при котором на отдельном узле промежуточные данные сохраняются в его памяти.
+
+### Хранимая процедура {: #stored_procedure }
+
+Представляет собой набор SQL-инструкций для
+выполнения DML-операций, т.е. изменения данных в таблице. Такой набор
+является отдельным объектом БД и выполняется как единое целое. В простом
+виде он может состоять из нужной пользователю DML-команды.
diff --git a/docs/reference/sql_queries.md b/docs/reference/sql_queries.md
index c31a8abfe311ee025e82325afcb87bca657b681d..bd4484846f54e870d54af42b43c148047038d752 100644
--- a/docs/reference/sql_queries.md
+++ b/docs/reference/sql_queries.md
@@ -9,15 +9,16 @@ Picodata при работе с распределенной СУБД.
 
 - Data Definition Language (`DDL`): команды для
   [создания](#create_table) и [удаления](#drop_table)
-  [таблиц](../overview/glossary.md#table);
+  [таблиц](../overview/glossary.md#table), а также для работы с
+  [хранимыми процедурами](../overview/glossary.md#stored_procedure);
 - Data Modification Language (`DML`): команды [`INSERT`](#query_insert) для
   добавления данных в таблицы, [`UPDATE`](#query_update) для обновления данных
   и [`DELETE`](#query_delete) для удаления данных;
 - Data Query Language (`DQL`): команда [`SELECT`](#query_select) для получения
   данных из таблиц;
 - Access Control Lists (`ACL`): команды для [управления пользователями](#users) и ролями;
-- команда [`EXPLAIN`](#query_explain) для отображения планов запросов `DML` и
-  `DQL`.
+- команда [`EXPLAIN`](#query_explain) для отображения [планов
+  запросов](../overview/glossary.md#execution_plan) `DML` и `DQL`.
 
 Схемы этих функций на верхнем уровне показаны ниже:
 
@@ -288,6 +289,89 @@ Cхема запроса для удаления таблицы показана
 DROP TABLE "characters";
 ```
 
+## Работа с хранимыми процедурами {: #proc }
+
+[Хранимые процедуры](../overview/glossary.md#stored_procedure) позволяют
+создавать шаблоны команд для более удобного выполнения DML-операций.
+
+### **CREATE PROCEDURE** {: #create_procedure }
+
+![CREATE PROCEDURE](../images/ebnf/create_procedure.svg)
+
+#### **Тело процедуры** {: #body }
+
+![body](../images/ebnf/body.svg)
+
+
+Для примера создадим процедуру по добавлению новых строк в таблицу `characters`:
+
+```sql
+CREATE PROCEDURE proc1(int, text, int)
+AS $$INSERT INTO "characters" VALUES($1, $2, $3)$$
+```
+
+где:
+
+- `proc1` — название хранимой процедуры
+- `int, text, int` — типы колонок
+- `$1, $2, $3`— порядок колонок
+
+Порядок колонок может быть произвольным, но важно, чтобы указанный тип
+совпадал с фактическим типом в целевой таблице. Так, следующая команда
+создаст ту же самую процедуру:
+
+```sql
+CREATE PROCEDURE proc1(int, int, text)
+AS $$INSERT INTO "characters" VALUES($1, $3, $2)$$
+```
+
+NOTE: **Примечание** Пользователь может создавать и затем управлять
+своими процедурами при наличии [соответствующей
+привилегии](../tutorial/access_control.md#proc_access).
+
+Созданная процедура хранится в системной таблице
+[_pico_routine](../architecture/system_tables.md#_pico_routine).
+Посмотреть ее структуру можно следующей командой:
+
+```sql
+SELECT * FROM "_pico_routine" WHERE "name" = 'PROC1';
+```
+
+### Вызов процедуры {: #call_procedure }
+
+Созданную ранее хранимую процедуру можно использовать с помощью команды
+`call`, реализующей RPC-запрос. Пример:
+
+```sql
+CALL proc1(11, 'Pez Cat', 2013)
+```
+
+При условии правильного указания параметров процедуры и отсутствия в
+таблице строки с `id` = 11, данная команда добавит новую строку.
+
+<!--
+### Переименование процедуры  {: #rename_proc }
+
+Ранее созданную процедуру можно переименовать, используя возможности `ALTER`. Пример команды:
+
+```sql
+ALTER PROCEDURE proc1
+RENAME TO proc2
+OPTION ( timeout = 4 )
+```
+-->
+
+### **DROP PROCEDURE** {: #drop_procedure }
+
+![DROP PROCEDURE](../images/ebnf/drop_procedure.svg)
+
+
+Пример удаления существующей хранимой процедуры:
+
+```sql
+DROP PROCEDURE proc1
+```
+
 ## Запрос SELECT {: #query_select }
 
 Запрос `SELECT` используется для получения информации из указанной
@@ -1361,15 +1445,15 @@ WHERE "stock" > 1000;
 вариант перемещения данных между узлами хранения. Существуют следующие
 четыре варианта:
 
-1. **Локальная вставка**. Представляет собой локальную материализацию
-   данных с подсчетом значений `bucket_id` для каждого кортежа
-   (соответственно, кортежи будут сгруппированы по этим бакетам).
-   Перемещения данных на другие узлы хранения через узел-маршрутизатор
-   не происходит. На текущем узле хранения будет локально создана
-   виртуальная таблица из результатов читающего запроса или из
-   переданных `VALUES`, а потом данные из нее будут вставлены локально в
-   целевую таблицу. Планировщик отобразит значение `motion [policy:
-   local segment]`.
+1. **Локальная вставка**. Представляет собой локальную [материализацию
+   данных](../overview/glossary.md#data_materialization) с подсчетом
+   значений `bucket_id` для каждого кортежа (соответственно, кортежи
+   будут сгруппированы по этим бакетам). Перемещения данных на другие
+   узлы хранения через узел-маршрутизатор не происходит. На текущем узле
+   хранения будет локально создана виртуальная таблица из результатов
+   читающего запроса или из переданных `VALUES`, а потом данные из нее
+   будут вставлены локально в целевую таблицу. Планировщик отобразит
+   значение `motion [policy: local segment]`.
 1. **Локальная материализация**. Данный вариант аналогичен предыдущему с
    той разницей, что при материализации данных не происходит вычисление
    `bucket_id`. При таком запросе планировщик отобразит значение `motion
diff --git a/docs/tutorial/access_control.md b/docs/tutorial/access_control.md
index f40db5a68427519932c81e176d0fcc2b64cbfbaa..daaa9636d26b134a8dfe097517df6d3962e854ff 100644
--- a/docs/tutorial/access_control.md
+++ b/docs/tutorial/access_control.md
@@ -131,6 +131,7 @@ GRANT DROP ROLE TO "admin"
 GRANT CREATE TABLE TO <grantee>
 GRANT CREATE USER TO <grantee>
 GRANT CREATE ROLE TO <grantee>
+GRANT CREATE PROCEDURE TO <grantee>
 ```
 
 Это обеспечивает наличие у администратора БД следующих прав:
@@ -140,6 +141,7 @@ GRANT CREATE ROLE TO <grantee>
 - управлять конфигурацией БД
 - назначать права доступа пользователям БД к объектам доступа БД
 - создавать резервные копии БД и восстанавливать БД из резервной копии
+- создавать, модифицировать и удалять хранимые процедуры
 
 При создании объекта пользователь становится его владельцем и
 автоматически получает на него следующие права (в зависимости от типа
@@ -158,6 +160,10 @@ GRANT DROP ON USER <user name> TO <owner>
 
 -- CREATE ROLE <role name>
 GRANT DROP ON ROLE <role name> TO <owner>
+
+-- CREATE PROCEDURE <procedure name>
+GRANT ALTER ON PROCEDURE <procedure name> TO <owner>
+GRANT DROP ON PROCEDURE <procedure name> TO <owner>
 ```
 
 ### Роли {: #roles }
@@ -403,6 +409,34 @@ GRANT <priv> ON TABLE <table name> TO <grantee>
 REVOKE <priv> ON TABLE <table name> FROM <grantee>
 ```
 
+## Управление доступом к хранимым процедурам {: #proc_access }
+
+Для того, чтобы пользователь в Picodata мог создавать хранимые
+процедуры, ему требуется соответствующая привилегия от Администратора
+СУБД:
+
+```sql
+GRANT CREATE PROCEDURE TO <grantee>
+```
+
+После этого <grantee> сможет не только создавать, но и управлять своими
+хранимыми процедурами. При этом, можно выдать привилегии для отдельных
+действий с процедурами, например на их исполнение и удаление. Это может
+быть полезно для настройки доступа к процедурам, созданным  другими
+пользователями:
+
+```sql
+GRANT EXECUTE PROCEDURE TO <grantee>
+GRANT DROP PROCEDURE TO <grantee>
+```
+
+Как и в остальных случаях, отозвать выданные привилегии можно при помощи команды `REVOKE`:
+
+```sql
+REVOKE EXECUTE PROCEDURE FROM <grantee>
+REVOKE DROP PROCEDURE FROM <grantee>
+```
+
 ## Дополнительные примеры SQL-запросов {: #sql_examples }
 
 ```sql
@@ -439,6 +473,9 @@ GRANT DROP ON TABLE <table name> TO <grantee>
 GRANT DROP ON USER <user name> TO <grantee>
 GRANT DROP ON ROLE <role name> TO <grantee>
 
+GRANT EXECUTE ON PROCEDURE <proc name> TO <grantee>
+GRANT DROP ON PROCEDURE <proc name> TO <grantee>
+
 GRANT <role name> TO <grantee>
 ```