diff --git a/app/panels/query_editor/common.js b/app/panels/query_editor/common.js
new file mode 100644
index 0000000..fc243db
--- /dev/null
+++ b/app/panels/query_editor/common.js
@@ -0,0 +1,23 @@
+/*
+ Парус 8 - Панели мониторинга - Редактор запросов
+ Обще ресурсы и константы
+*/
+
+//---------
+//Константы
+//---------
+
+//Типы данных
+const DATA_TYPE = { STR: 0, NUMB: 1, DATE: 2 };
+
+//Типы элементов диаграммы
+const NODE_TYPE = {
+ ENTITY: "entity",
+ ATTRIBUTE: "attribute"
+};
+
+//----------------
+//Интерфейс модуля
+//----------------
+
+export { DATA_TYPE, NODE_TYPE };
diff --git a/app/panels/query_editor/components/attribute/attribute.js b/app/panels/query_editor/components/attribute/attribute.js
new file mode 100644
index 0000000..f1d36ef
--- /dev/null
+++ b/app/panels/query_editor/components/attribute/attribute.js
@@ -0,0 +1,104 @@
+/*
+ Парус 8 - Панели мониторинга - Редактор запросов
+ Компоненты: Атрибут сущности
+*/
+
+//---------------------
+//Подключение библиотек
+//---------------------
+
+import React from "react"; //Классы React
+import PropTypes from "prop-types"; //Контроль свойств компонента
+import { Handle, Position, useStore } from "reactflow"; //Библиотека редактора диаграмм
+import { Box, Stack, Icon, Typography } from "@mui/material"; //Компоненты UI
+import { DATA_TYPE } from "../../common"; //Общие ресурсы и константы редактора
+
+//---------
+//Константы
+//---------
+
+//Типовые цвета точек привязки
+const HANDLE_BORDER_COLOR = "#69db7c";
+const HANDLE_BORDER_COLOR_DISABLED = "#adb5bd";
+
+//Стили
+const STYLES = {
+ CONTAINER: { display: "flex", width: "100%", height: "100%" },
+ HANDLE_SOURCE: isConnecting => ({
+ width: 14,
+ height: 14,
+ right: -10,
+ border: `2px solid ${isConnecting ? HANDLE_BORDER_COLOR_DISABLED : HANDLE_BORDER_COLOR}`,
+ borderRadius: 7,
+ background: "white"
+ }),
+ HANDLE_TARGET: isConnecting => ({
+ width: isConnecting ? 14 : 0,
+ height: 14,
+ left: isConnecting ? -7 : 0,
+ border: `2px solid ${HANDLE_BORDER_COLOR}`,
+ borderRadius: 7,
+ background: "white",
+ visibility: isConnecting ? "visible" : "hidden"
+ }),
+ CONTENT_STACK: { width: "100%" },
+ TITLE_NAME_STACK: { width: "100%", containerType: "inline-size" }
+};
+
+//Иконки
+const ICONS = {
+ [DATA_TYPE.STR]: "format_align_left",
+ [DATA_TYPE.NUMB]: "pin",
+ [DATA_TYPE.DATE]: "calendar_month",
+ DEFAULT: "category"
+};
+
+//Структура данных об атрибуте сущности
+const ATTRIBUTE_DATA_SHAPE = PropTypes.shape({
+ name: PropTypes.string.isRequired,
+ title: PropTypes.string.isRequired,
+ dataType: PropTypes.number.isRequired
+});
+
+//-----------
+//Тело модуля
+//-----------
+
+//Атрибут сущности
+const Attribute = ({ data }) => {
+ //Поиск идентификатора соединяемого элемента
+ const connectionNodeId = useStore(state => state.connectionNodeId);
+
+ //Флаг выполнения соединения сущностей
+ const isConnecting = Boolean(connectionNodeId);
+
+ //Формирование представления
+ return (
+
+
+
+
+ {ICONS[data.dataType] || ICONS.DEFAULT}
+
+
+ {data.title}
+
+
+ {data.name}
+
+
+
+
+ );
+};
+
+//Контроль свойств компонента - Атрибут сущности
+Attribute.propTypes = {
+ data: ATTRIBUTE_DATA_SHAPE
+};
+
+//----------------
+//Интерфейс модуля
+//----------------
+
+export { Attribute };
diff --git a/app/panels/query_editor/components/entity/entity.css b/app/panels/query_editor/components/entity/entity.css
new file mode 100644
index 0000000..1f64803
--- /dev/null
+++ b/app/panels/query_editor/components/entity/entity.css
@@ -0,0 +1,33 @@
+.entity__wrapper {
+ width: 100%;
+ height: 100%;
+ border: 1px solid var(--border-color-dark);
+ border-radius: 6px;
+ box-shadow: var(--shadow-entity);
+ overflow: hidden;
+ background-color: white;
+}
+
+.entity__wrapper[data-selected="true"] {
+ outline: 1px solid var(--outline-color);
+ border-color: var(--outline-color);
+}
+
+.entity__title {
+ width: 100%;
+ height: 50px;
+ align-content: center;
+ border-bottom: 1px solid var(--border-color);
+ font-weight: 900;
+ text-align: center;
+ background-color: var(--entity-title-bg);
+ cursor: move;
+}
+
+.entity__name {
+ width: 100%;
+ align-content: center;
+ text-align: center;
+ font-size: 0.8rem;
+ color: gray;
+}
diff --git a/app/panels/query_editor/components/entity/entity.js b/app/panels/query_editor/components/entity/entity.js
new file mode 100644
index 0000000..89b9797
--- /dev/null
+++ b/app/panels/query_editor/components/entity/entity.js
@@ -0,0 +1,50 @@
+/*
+ Парус 8 - Панели мониторинга - Редактор запросов
+ Компоненты: Сущность запроса
+*/
+
+//---------------------
+//Подключение библиотек
+//---------------------
+
+import React from "react"; //Классы React
+import PropTypes from "prop-types"; //Контроль свойств компонента
+import "./entity.css"; //Стили компомнента
+
+//---------
+//Константы
+//---------
+
+//Структура данных о сущности запроса
+const ENTITY_DATA_SHAPE = PropTypes.shape({
+ name: PropTypes.string.isRequired,
+ title: PropTypes.string.isRequired
+});
+
+//-----------
+//Тело модуля
+//-----------
+
+//Сущность запроса
+const Entity = ({ data, selected }) => {
+ return (
+
+
+
{data.title}
+
{data.name}
+
+
+ );
+};
+
+//Контроль свойств компонента - Сущность запроса
+Entity.propTypes = {
+ data: ENTITY_DATA_SHAPE,
+ selected: PropTypes.bool.isRequired
+};
+
+//----------------
+//Интерфейс модуля
+//----------------
+
+export { Entity };
diff --git a/app/panels/query_editor/components/entity_add_dialog/entity_add_dialog.js b/app/panels/query_editor/components/entity_add_dialog/entity_add_dialog.js
new file mode 100644
index 0000000..b67a589
--- /dev/null
+++ b/app/panels/query_editor/components/entity_add_dialog/entity_add_dialog.js
@@ -0,0 +1,43 @@
+/*
+ Парус 8 - Панели мониторинга - Редактор запросов
+ Компонент: Диалог добавления сущности запроса
+*/
+
+//---------------------
+//Подключение библиотек
+//---------------------
+
+import React from "react"; //Классы React
+import PropTypes from "prop-types"; //Контроль свойств компонента
+import { P8PDialog } from "../../../../components/p8p_dialog"; //Типовой диалог
+import { TITLES } from "../../../../../app.text"; //Общие текстовые ресурсы приложения
+
+//-----------
+//Тело модуля
+//-----------
+
+//Диалог добавления сущности запроса
+const EntityAddDialog = ({ onOk, onCancel }) => {
+ //Нажатие на кнопку "Ok"
+ const handleOk = values => onOk && onOk({ ...values });
+
+ //Нажатие на кнопку "Отмена"
+ const handleCancel = () => onCancel && onCancel();
+
+ //Генерация содержимого
+ return (
+
+ );
+};
+
+//Контроль свойств - Диалог добавления сущности запроса
+EntityAddDialog.propTypes = {
+ onOk: PropTypes.func,
+ onCancel: PropTypes.func
+};
+
+//----------------
+//Интерфейс модуля
+//----------------
+
+export { EntityAddDialog };
diff --git a/app/panels/query_editor/components/queries_manager/queries_list.js b/app/panels/query_editor/components/queries_manager/queries_list.js
new file mode 100644
index 0000000..f3347e9
--- /dev/null
+++ b/app/panels/query_editor/components/queries_manager/queries_list.js
@@ -0,0 +1,142 @@
+/*
+ Парус 8 - Панели мониторинга - Редактор запросов
+ Компонент: Список запросов
+*/
+
+//---------------------
+//Подключение библиотек
+//---------------------
+
+import React from "react"; //Классы React
+import PropTypes from "prop-types"; //Контроль свойств компонента
+import { Stack, List, ListItem, IconButton, Icon, ListItemButton, ListItemText, Typography } from "@mui/material"; //Интерфейсные компоненты MUI
+import { BUTTONS } from "../../../../../app.text"; //Общие текстовые ресурсы приложения
+
+//---------
+//Константы
+//---------
+
+//Стили
+const STYLES = {
+ SMALL_TOOL_ICON: {
+ fontSize: 20
+ }
+};
+
+//---------
+//Константы
+//---------
+
+//Структура данных о сущности запроса
+const QUERIES_LIST_ITEM_SHAPE = PropTypes.shape({
+ rn: PropTypes.number.isRequired,
+ code: PropTypes.string.isRequired,
+ name: PropTypes.string.isRequired,
+ author: PropTypes.string.isRequired,
+ chDate: PropTypes.string.isRequired,
+ modify: PropTypes.oneOf([0, 1]).isRequired,
+ pbl: PropTypes.oneOf([0, 1]).isRequired,
+ ready: PropTypes.oneOf([0, 1]).isRequired
+});
+
+//-----------
+//Тело модуля
+//-----------
+
+//Диалог открытия запроса
+const QueriesList = ({ queries = [], current = null, onSelect = null, onPbl = null, onReady = null, onEdit = null, onDelete = null } = {}) => {
+ //При выборе элемента списка
+ const handleSelectClick = query => {
+ onSelect && onSelect(query);
+ };
+
+ //При нажатии на общедоступность
+ const handlePblClick = (e, query) => {
+ e.stopPropagation();
+ onPbl && onPbl(query);
+ };
+
+ //При нажатии на готовность
+ const handleReadyClick = (e, query) => {
+ e.stopPropagation();
+ onReady && onReady(query);
+ };
+
+ //При нажатии на исправлении
+ const handleEditClick = (e, query) => {
+ e.stopPropagation();
+ onEdit && onEdit(query);
+ };
+
+ //При нажатии на удаление
+ const handleDeleteClick = (e, query) => {
+ e.stopPropagation();
+ onDelete && onDelete(query);
+ };
+
+ //Формирование представления
+ return (
+
+ {queries.map((query, i) => {
+ const selected = query.rn === current;
+ const disabled = !query.modify;
+ const pblTitle = `${query.pbl === 1 ? "Общедоступный" : "Приватный"}${!disabled ? " - нажмите, чтобы изменить" : ""}`;
+ const pblIcon = query.pbl === 1 ? "groups" : "lock_person";
+ const readyTitle = `${query.ready === 1 ? "Готов" : "Не готов"}${!disabled ? " - нажмите, чтобы изменить" : ""}`;
+ const readyIcon = query.ready === 1 ? "touch_app" : "do_not_touch";
+ return (
+
+ handleSelectClick(query)} selected={selected}>
+
+ {`${query.code}, ${query.author}, ${query.chDate}`}
+
+
+ handlePblClick(e, query)}>
+ {pblIcon}
+
+
+
+ handleReadyClick(e, query)}>
+ {readyIcon}
+
+
+
+
+ }
+ />
+
+ handleEditClick(e, query)} disabled={disabled} title={BUTTONS.UPDATE}>
+ edit
+
+ handleDeleteClick(e, query)} disabled={disabled} title={BUTTONS.DELETE}>
+ delete
+
+
+
+
+ );
+ })}
+
+ );
+};
+
+//Контроль свойств компонента - Список запросов
+QueriesList.propTypes = {
+ queries: PropTypes.arrayOf(QUERIES_LIST_ITEM_SHAPE),
+ current: PropTypes.number,
+ onSelect: PropTypes.func,
+ onPbl: PropTypes.func,
+ onReady: PropTypes.func,
+ onEdit: PropTypes.func,
+ onDelete: PropTypes.func
+};
+
+//----------------
+//Интерфейс модуля
+//----------------
+
+export { QueriesList };
diff --git a/app/panels/query_editor/components/queries_manager/queries_manager.js b/app/panels/query_editor/components/queries_manager/queries_manager.js
new file mode 100644
index 0000000..fe31615
--- /dev/null
+++ b/app/panels/query_editor/components/queries_manager/queries_manager.js
@@ -0,0 +1,105 @@
+/*
+ Парус 8 - Панели мониторинга - Редактор запросов
+ Компонент: Менеджер запросов
+*/
+
+//---------------------
+//Подключение библиотек
+//---------------------
+
+import React, { useState, useContext } from "react"; //Классы React
+import PropTypes from "prop-types"; //Контроль свойств компонента
+import { Button, Icon } from "@mui/material"; //Интерфейсные компоненты MUI
+import { MessagingСtx } from "../../../../context/messaging"; //Контекст сообщений
+import { BUTTONS } from "../../../../../app.text"; //Общие текстовые ресурсы приложения
+import { P8PConfigDialog } from "../../../../components/editors/p8p_config_dialog"; //Типовой диалог настройки
+import { useQuery } from "../../hooks"; //Пользовательские хуки
+import { QueriesList } from "./queries_list"; //Список запросов
+import { QueryIUDialog } from "./query_iu_dialog"; //Диалог добавления/исправления запроса
+
+//-----------
+//Тело модуля
+//-----------
+
+//Менеджер запросов
+const QueriesManager = ({ current = null, onQuerySelect = null, onCancel = null } = {}) => {
+ //Собственное состояние - изменяемый запрос
+ const [modQuery, setModQuery] = useState(null);
+
+ //Работа со списком запросов
+ const [queries, insertQuery, updateQuery, deleteQuery, setQueryReady, setQueryPbl] = useQuery();
+
+ //Подключение к контексту сообщений
+ const { showMsgWarn } = useContext(MessagingСtx);
+
+ //При добавлении запроса
+ const handleQueryAdd = () => setModQuery(true);
+
+ //При выборе запроса
+ const handleQuerySelect = query => onQuerySelect && onQuerySelect(query.rn);
+
+ //При установке признака публичности
+ const handleQueryPblSet = query => setQueryPbl(query.rn, query.pbl === 1 ? 0 : 1);
+
+ //При установке признака готовности
+ const handleQueryReadySet = query => setQueryReady(query.rn, query.ready === 1 ? 0 : 1);
+
+ //При исправлении запроса
+ const handleQueryEdit = query => setModQuery({ ...query });
+
+ //При удалении запроса
+ const handleQueryDelete = query => showMsgWarn("Удалить запрос?", () => deleteQuery(query.rn));
+
+ //При закрытии диалога добавления/исправления по "ОК"
+ const handleIUDialogOk = async values => {
+ if (modQuery === true) await insertQuery(values.code, values.name);
+ else await updateQuery(modQuery.rn, values.code, values.name);
+ setModQuery(null);
+ };
+
+ //При закрытии диалога добавления/исправления по "Отмена"
+ const handleIUDialogCancel = () => setModQuery(null);
+
+ //При закртии менеджера отменой
+ const handleCancel = () => onCancel && onCancel();
+
+ //Формирование представления
+ return (
+
+ {modQuery && (
+
+ )}
+
+
+
+ );
+};
+
+//Контроль свойств компонента - Менеджер запросов
+QueriesManager.propTypes = {
+ current: PropTypes.number,
+ onQuerySelect: PropTypes.func,
+ onCancel: PropTypes.func
+};
+
+//----------------
+//Интерфейс модуля
+//----------------
+
+export { QueriesManager };
diff --git a/app/panels/query_editor/components/queries_manager/query_iu_dialog.js b/app/panels/query_editor/components/queries_manager/query_iu_dialog.js
new file mode 100644
index 0000000..6053f07
--- /dev/null
+++ b/app/panels/query_editor/components/queries_manager/query_iu_dialog.js
@@ -0,0 +1,54 @@
+/*
+ Парус 8 - Панели мониторинга - Редактор запросов
+ Компонент: Диалог добавления/исправления запроса
+*/
+
+//---------------------
+//Подключение библиотек
+//---------------------
+
+import React from "react"; //Классы React
+import PropTypes from "prop-types"; //Контроль свойств компонента
+import { P8PDialog } from "../../../../components/p8p_dialog"; //Типовой диалог
+import { TITLES } from "../../../../../app.text"; //Общие текстовые ресурсы приложения
+
+//-----------
+//Тело модуля
+//-----------
+
+//Диалог добавления/исправления запроса
+const QueryIUDialog = ({ code = "", name = "", insert = true, onOk, onCancel }) => {
+ //Нажатие на кнопку "Ok"
+ const handleOk = values => onOk && onOk({ ...values });
+
+ //Нажатие на кнопку "Отмена"
+ const handleCancel = () => onCancel && onCancel();
+
+ //Генерация содержимого
+ return (
+
+ );
+};
+
+//Контроль свойств - Диалог добавления/исправления запроса
+QueryIUDialog.propTypes = {
+ code: PropTypes.string,
+ name: PropTypes.string,
+ insert: PropTypes.bool,
+ onOk: PropTypes.func,
+ onCancel: PropTypes.func
+};
+
+//----------------
+//Интерфейс модуля
+//----------------
+
+export { QueryIUDialog };
diff --git a/app/panels/query_editor/components/query_diagram/query_diagram.css b/app/panels/query_editor/components/query_diagram/query_diagram.css
new file mode 100644
index 0000000..535e228
--- /dev/null
+++ b/app/panels/query_editor/components/query_diagram/query_diagram.css
@@ -0,0 +1,9 @@
+.query_diagram {
+ --border-color: #dee2e6;
+ --border-color-dark: #adb5bd;
+ --outline-color: #74c0fc;
+ --entity-title-bg: #f1f3f5;
+ --shadow-entity: 0 -2px 5px 0 hsl(220 3% 15% / calc(1% + 2%)), 0 1px 1px -2px hsl(220 3% 15% / calc(1% + 3%)),
+ 0 2px 2px -2px hsl(220 3% 15% / calc(1% + 3%)), 0 5px 5px -2px hsl(220 3% 15% / calc(1% + 4%)), 0 9px 9px -2px hsl(220 3% 15% / calc(1% + 5%)),
+ 0 16px 16px -2px hsl(220 3% 15% / calc(1% + 6%));
+}
diff --git a/app/panels/query_editor/components/query_diagram/query_diagram.js b/app/panels/query_editor/components/query_diagram/query_diagram.js
new file mode 100644
index 0000000..2715653
--- /dev/null
+++ b/app/panels/query_editor/components/query_diagram/query_diagram.js
@@ -0,0 +1,160 @@
+/*
+ Парус 8 - Панели мониторинга - Редактор запросов
+ Диаграмма запроса
+*/
+
+//---------------------
+//Подключение библиотек
+//---------------------
+
+import React, { useState, useCallback, useEffect } from "react"; //Классы React
+import ReactFlow, { addEdge, Controls, getOutgoers, applyNodeChanges, applyEdgeChanges } from "reactflow"; //Библиотека редактора диаграмм
+import { NODE_TYPE } from "../../common"; //Общие ресурсы и константы редактора
+import { Entity } from "../entity/entity"; //Сущность запроса
+import { Attribute } from "../attribute/attribute"; //Атрибут сущности
+import "reactflow/dist/style.css"; //Типовые стили библиотеки редактора диаграмм
+import "./query_diagram.css"; //Стили компонента
+
+//---------
+//Константы
+//---------
+
+//Стили
+const STYLES = {
+ CONNECTION_LINE: {
+ strokeWidth: 2,
+ stroke: "gray"
+ },
+ EDGE: {
+ strokeWidth: 2
+ }
+};
+
+//Привязка компонтов диаграммы к типам
+const NODE_TYPES_COMPONENTS = {
+ [NODE_TYPE.ENTITY]: Entity,
+ [NODE_TYPE.ATTRIBUTE]: Attribute
+};
+
+//------------------------------------
+//Вспомогательные функции и компоненты
+//------------------------------------
+
+const hasCycle = (connection, target, nodes, edges, visited = new Set()) => {
+ if (visited.has(target.id)) {
+ return false;
+ }
+
+ visited.add(target.id);
+
+ for (const outgoer of getOutgoers(target, nodes, edges)) {
+ if (outgoer.id === connection.source || hasCycle(connection, outgoer, nodes, edges, visited)) {
+ return true;
+ }
+ }
+
+ return false;
+};
+
+const isValidConnection = (connection, nodes, edges) => {
+ if (!connection.source || !connection.target) {
+ return false;
+ }
+
+ const tableId = connection.source.split("-")[0];
+ const isSameTable = connection.target.startsWith(tableId);
+ if (isSameTable) {
+ return false;
+ }
+
+ const target = nodes.find(node => node.id === connection.target);
+ if (!target || target.id === connection.source) {
+ return false;
+ }
+
+ return !hasCycle(connection, target, nodes, edges);
+};
+
+//-----------
+//Тело модуля
+//-----------
+
+//Диаграмма запроса
+const QueryDiagram = ({ entities, relations, onEntityPositionChange, onEntityRemove, onRelationAdd, onRelationRemove }) => {
+ //Собственное состояние - элементы
+ const [nodes, setNodes] = useState(entities);
+
+ //Собственное состояние - связи
+ const [edges, setEdges] = useState(relations);
+
+ //Собственное состояние - перемещённый элемент
+ const [movedNode, setMovedNode] = useState(null);
+
+ //При изменении элементов на диаграмме
+ const handleNodesChange = useCallback(
+ changes => {
+ setNodes(nodesSnapshot => applyNodeChanges(changes, nodesSnapshot));
+ if (changes.length == 1 && changes[0].type == "position" && changes[0].dragging)
+ setMovedNode({ id: changes[0].id, position: { ...changes[0].position } });
+ if (changes.length == 1 && changes[0].type == "position" && !changes[0].dragging && movedNode) {
+ if (onEntityPositionChange) onEntityPositionChange(movedNode.id, movedNode.position);
+ setMovedNode(null);
+ }
+ if (changes[0].type == "remove" && entities.find(e => e.id == changes[0].id && e.type == NODE_TYPE.ENTITY) && onEntityRemove)
+ onEntityRemove(changes[0].id);
+ },
+ [movedNode, entities, onEntityPositionChange, onEntityRemove]
+ );
+
+ //При связывании элементов на диаграмме
+ const handleConnect = connection => {
+ setEdges(state => addEdge({ ...connection, id: `${connection.source}-${connection.target}` }, state));
+ onRelationAdd && onRelationAdd(connection.source, connection.target);
+ };
+
+ //При изменении связей на диаграмме
+ const handleEdgesChange = useCallback(
+ changes => {
+ setEdges(edgesSnapshot => applyEdgeChanges(changes, edgesSnapshot));
+ if (changes.length == 1 && changes[0].type == "remove" && onRelationRemove) onRelationRemove(changes[0].id);
+ },
+ [onRelationRemove]
+ );
+
+ const validateConnection = connection => {
+ return isValidConnection(connection, nodes, edges);
+ };
+
+ //При изменении состава сущностей
+ useEffect(() => setNodes(entities), [entities]);
+
+ //При изменении состава связей
+ useEffect(() => setEdges(relations), [relations]);
+
+ //Генерация содержимого
+ return (
+
+
+
+ );
+};
+
+//----------------
+//Интерфейс модуля
+//----------------
+
+export { QueryDiagram };
diff --git a/app/panels/query_editor/hooks.js b/app/panels/query_editor/hooks.js
new file mode 100644
index 0000000..eb6a9f9
--- /dev/null
+++ b/app/panels/query_editor/hooks.js
@@ -0,0 +1,290 @@
+/*
+ Парус 8 - Панели мониторинга - Редактор запросов
+ Пользовательские хуки
+*/
+
+//---------------------
+//Подключение библиотек
+//---------------------
+
+import { useState, useContext, useEffect, useCallback } from "react"; //Классы React
+import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
+import { NODE_TYPE } from "./common"; //Общие ресурсы и константы редактора
+
+//------------------------------------
+//Вспомогательные функции и компоненты
+//------------------------------------
+
+//Конвертация серверного описания сущностей запроса в элементы диаграммы
+const serverEntity2QueryDiagramNodes = entity => {
+ const groupWidth = 250;
+ const nameColumnHeight = 50;
+ const columns = entity?.XATTRS?.XATTR || [];
+ const columnsCount = columns.length;
+ const groupHeight = nameColumnHeight + columnsCount * 50;
+
+ const groupNode = {
+ id: entity.id,
+ type: NODE_TYPE.ENTITY,
+ data: { ...entity },
+ position: { x: entity.x, y: entity.y },
+ style: {
+ width: groupWidth,
+ height: groupHeight
+ },
+ draggable: true
+ };
+
+ const columnNodes = columns.map((column, index, columns) => {
+ const x = 1;
+ const y = 50 * (index + 1);
+ const width = groupWidth - 2;
+ const height = 50;
+
+ const isLast = index === columns.length - 1;
+ const defaultColumnStyles = {
+ borderBottom: "1px solid #dee2e6"
+ };
+ const lastColumnStyles = {
+ borderBottom: "none"
+ };
+ const otherStyles = isLast ? lastColumnStyles : defaultColumnStyles;
+
+ return {
+ id: column.id,
+ type: NODE_TYPE.ATTRIBUTE,
+ data: {
+ ...column,
+ included: false,
+ parentEntity: entity.id
+ },
+ position: { x, y },
+ parentId: entity.id,
+ extent: "parent",
+ style: {
+ width,
+ height,
+ ...otherStyles
+ },
+ draggable: false,
+ selectable: true
+ };
+ });
+
+ return [groupNode, ...columnNodes];
+};
+
+//Конвертация серверного описания запроса в данные для редактора диаграмм
+const serverQueryData2QueryDiagram = (entities, relations) => {
+ const result = { entities: [], relations: [...relations] };
+ entities.forEach(entity => {
+ const nodes = serverEntity2QueryDiagramNodes(entity);
+ result.entities = [...result.entities, ...nodes];
+ });
+ return result;
+};
+
+//-----------
+//Тело модуля
+//-----------
+
+//Работа с запросами
+const useQuery = () => {
+ //Собственное состояние - флаг инициализированности
+ const [isInit, setInit] = useState(false);
+
+ //Собственное состояние - флаг загрузки
+ const [isLoading, setLoading] = useState(false);
+
+ //Собственное состояние - флаг необходимости обновления
+ const [refresh, setRefresh] = useState(true);
+
+ //Собственное состояние - данные
+ const [data, setData] = useState(null);
+
+ //Подключение к контексту взаимодействия с сервером
+ const { executeStored } = useContext(BackEndСtx);
+
+ //Обновление данных
+ const doRefresh = () => setRefresh(true);
+
+ //Добавление запроса
+ const insertQuery = useCallback(
+ async (code, name) => {
+ await executeStored({ stored: "PKG_P8PANELS_QE.QUERY_INSERT", args: { SCODE: code, SNAME: name }, loader: false });
+ setRefresh(true);
+ },
+ [executeStored]
+ );
+
+ //Изменение запроса
+ const updateQuery = useCallback(
+ async (query, code, name) => {
+ await executeStored({ stored: "PKG_P8PANELS_QE.QUERY_UPDATE", args: { NRN: query, SCODE: code, SNAME: name }, loader: false });
+ setRefresh(true);
+ },
+ [executeStored]
+ );
+
+ //Удаление запроса
+ const deleteQuery = useCallback(
+ async query => {
+ await executeStored({ stored: "PKG_P8PANELS_QE.QUERY_DELETE", args: { NRN: query }, loader: false });
+ setRefresh(true);
+ },
+ [executeStored]
+ );
+
+ //Установка флага готовности запроса
+ const setQueryReady = useCallback(
+ async (query, ready) => {
+ await executeStored({ stored: "PKG_P8PANELS_QE.QUERY_READY_SET", args: { NRN: query, NREADY: ready }, loader: false });
+ setRefresh(true);
+ },
+ [executeStored]
+ );
+
+ //Установка флага публичности запроса
+ const setQueryPbl = useCallback(
+ async (query, pbl) => {
+ await executeStored({ stored: "PKG_P8PANELS_QE.QUERY_PBL_SET", args: { NRN: query, NPBL: pbl }, loader: false });
+ setRefresh(true);
+ },
+ [executeStored]
+ );
+
+ //При необходимости получить/обновить данные
+ useEffect(() => {
+ //Загрузка данных с сервера
+ const loadData = async () => {
+ try {
+ setLoading(true);
+ const data = await executeStored({
+ stored: "PKG_P8PANELS_QE.QUERY_LIST",
+ respArg: "COUT",
+ isArray: name => ["XQUERY"].includes(name),
+ attributeValueProcessor: (name, val) => (["code", "name"].includes(name) ? undefined : val),
+ loader: true
+ });
+ setData(data?.XQUERIES?.XQUERY || []);
+ setInit(true);
+ } finally {
+ setRefresh(false);
+ setLoading(false);
+ }
+ };
+ //Если надо обновить
+ if (refresh)
+ //Получим данные
+ loadData();
+ }, [refresh, executeStored]);
+
+ //Возвращаем интерфейс хука
+ return [data, insertQuery, updateQuery, deleteQuery, setQueryReady, setQueryPbl, doRefresh, isLoading, isInit];
+};
+
+//Работа с содержимым запроса
+const useQueryDesc = query => {
+ //Собственное состояние - флаг инициализированности
+ const [isInit, setInit] = useState(false);
+
+ //Собственное состояние - флаг загрузки
+ const [isLoading, setLoading] = useState(false);
+
+ //Собственное состояние - флаг необходимости обновления
+ const [refresh, setRefresh] = useState(true);
+
+ //Собственное состояние - данные
+ const [data, setData] = useState(null);
+
+ //Подключение к контексту взаимодействия с сервером
+ const { executeStored } = useContext(BackEndСtx);
+
+ //Обновление данных
+ const doRefresh = () => setRefresh(true);
+
+ //Добавление сущности в запрос
+ const addEnt = useCallback(
+ async (name, type) => {
+ await executeStored({ stored: "PKG_P8PANELS_QE.QUERY_ENT_ADD", args: { NRN: query, SNAME: name, STYPE: type }, loader: false });
+ setRefresh(true);
+ },
+ [query, executeStored]
+ );
+
+ //Удаление сущности из запроса
+ const removeEnt = useCallback(
+ async ent => {
+ await executeStored({ stored: "PKG_P8PANELS_QE.QUERY_ENT_REMOVE", args: { NRN: query, SID: ent }, loader: false });
+ setRefresh(true);
+ },
+ [query, executeStored]
+ );
+
+ //Сохранение координат сущности на диаграммем
+ const setEntPosition = useCallback(
+ async (ent, x, y) => {
+ await executeStored({ stored: "PKG_P8PANELS_QE.QUERY_ENT_POSITION_SET", args: { NRN: query, SID: ent, NX: x, NY: y }, loader: false });
+ },
+ [query, executeStored]
+ );
+
+ //Добавление отношения сущностей в запрос
+ const addRl = useCallback(
+ async (source, target) => {
+ await executeStored({ stored: "PKG_P8PANELS_QE.QUERY_RL_ADD", args: { NRN: query, SSOURCE: source, STARGET: target }, loader: false });
+ setRefresh(true);
+ },
+ [query, executeStored]
+ );
+
+ //Удаление отношения сущностей из запроса
+ const removeRl = useCallback(
+ async rl => {
+ await executeStored({ stored: "PKG_P8PANELS_QE.QUERY_RL_REMOVE", args: { NRN: query, SID: rl }, loader: false });
+ setRefresh(true);
+ },
+ [query, executeStored]
+ );
+
+ //При необходимости получить/обновить данные
+ useEffect(() => {
+ //Загрузка данных с сервера
+ const loadData = async () => {
+ try {
+ setLoading(true);
+ const data = await executeStored({
+ stored: "PKG_P8PANELS_QE.QUERY_DESC",
+ args: { NRN: query },
+ respArg: "COUT",
+ isArray: name => ["XENT", "XATTR", "XRL"].includes(name),
+ loader: true
+ });
+ setData(serverQueryData2QueryDiagram(data?.XENTS?.XENT || [], data?.XRLS?.XRL || []));
+ setInit(true);
+ } finally {
+ setRefresh(false);
+ setLoading(false);
+ }
+ };
+ //Если надо обновить
+ if (refresh)
+ if (query)
+ //Если есть для чего получать данные
+ loadData();
+ //Нет идентификатора запроса - нет данных
+ else setData(null);
+ }, [refresh, query, executeStored]);
+
+ //При изменении входных свойств - поднимаем флаг обновления
+ useEffect(() => setRefresh(true), [query]);
+
+ //Возвращаем интерфейс хука
+ return [data, addEnt, removeEnt, setEntPosition, addRl, removeRl, doRefresh, isLoading, isInit];
+};
+
+//----------------
+//Интерфейс модуля
+//----------------
+
+export { useQuery, useQueryDesc };
diff --git a/app/panels/query_editor/index.js b/app/panels/query_editor/index.js
new file mode 100644
index 0000000..7675a82
--- /dev/null
+++ b/app/panels/query_editor/index.js
@@ -0,0 +1,16 @@
+/*
+ Парус 8 - Панели мониторинга - Редактор запросов
+ Редактор запросов: точка входа
+*/
+
+//---------------------
+//Подключение библиотек
+//---------------------
+
+import { QueryEditor } from "./query_editor"; //Корневая панель редактора
+
+//----------------
+//Интерфейс модуля
+//----------------
+
+export const RootClass = QueryEditor;
diff --git a/app/panels/query_editor/query_editor.js b/app/panels/query_editor/query_editor.js
new file mode 100644
index 0000000..22e4069
--- /dev/null
+++ b/app/panels/query_editor/query_editor.js
@@ -0,0 +1,137 @@
+/*
+ Парус 8 - Панели мониторинга - Редактор запросов
+ Корневой компонент
+*/
+
+//---------------------
+//Подключение библиотек
+//---------------------
+
+import React, { useState } from "react"; //Классы React
+import { Box, Grid, Button, Icon } from "@mui/material"; //Интерфейсные компоненты MUI
+import { APP_BAR_HEIGHT } from "../../components/p8p_app_workspace"; //Компоненты рабочего стола
+import { P8PEditorToolBar } from "../../components/editors/p8p_editor_toolbar"; //Панель инструментов редактора
+import { BUTTONS } from "../../../app.text"; //Общие текстовые ресурсы приложения
+import { QueryDiagram } from "./components/query_diagram/query_diagram"; //Диаграмма запроса
+import { QueriesManager } from "./components/queries_manager/queries_manager"; //Менеджер запросов
+import { EntityAddDialog } from "./components/entity_add_dialog/entity_add_dialog"; //Диалог добавления сущности
+import { P8PEditorBox } from "../../components/editors/p8p_editor_box"; //Контейнер параметров редактора
+import { P8PEditorSubHeader } from "../../components/editors/p8p_editor_sub_header"; //Подзаголовок группы параметров редактора
+import { useQueryDesc } from "./hooks"; //Пользовательские хуки
+
+//---------
+//Константы
+//---------
+
+//Стили
+const STYLES = {
+ CONTAINER: { display: "flex" },
+ GRID_CONTAINER: { height: `calc(100vh - ${APP_BAR_HEIGHT})` },
+ GRID_ITEM_INSPECTOR: { backgroundColor: "#e9ecef" }
+};
+
+//-----------
+//Тело модуля
+//-----------
+
+//Корневой компонент редактора запросов
+const QueryEditor = () => {
+ //Текущий запрос
+ const [query, setQuery] = useState(null);
+
+ //Отображения менеджера запросов
+ const [openQueriesManager, setOpenQueriesManager] = useState(true);
+
+ //Отображение диалога добавления сущности
+ const [openEntityAddDialog, setOpenEntityAddDialog] = useState(false);
+
+ //Получение данных запроса
+ const [queryDiagram, addEnt, removeEnt, setEntPosition, addRl, removeRl] = useQueryDesc(query);
+
+ //Обработка изменения положения сущности на диаграмме
+ const handleEntityPositionChange = (ent, position) => setEntPosition(ent, position.x, position.y);
+
+ //Обработка удаления сущности из запроса
+ const handleEntityRemove = ent => removeEnt(ent);
+
+ //Обработка добавления отношения cущностей
+ const handleRelationAdd = (source, target) => addRl(source, target);
+
+ //Обработка удаления отношения cущностей
+ const handleRelationRemove = rl => removeRl(rl);
+
+ //Открытие менеджера запросов
+ const handleOpenQueriesManager = () => setOpenQueriesManager(true);
+
+ //Закрытие менеджера запросов
+ const handleCancelQueriesManager = () => setOpenQueriesManager(false);
+
+ //Закрытие запроса
+ const handleQueryClose = () => setQuery(null);
+
+ //При выборе запроса
+ const handleQuerySelect = query => {
+ setQuery(query);
+ setOpenQueriesManager(false);
+ };
+
+ //При добавлении сущности в запрос
+ const handleEntityAdd = () => setOpenEntityAddDialog(true);
+
+ //Закрытие диалога добавления сущности по "ОК"
+ const handleEntityAddDialogOk = async values => {
+ await addEnt(values.name, "VIEW");
+ setOpenEntityAddDialog(false);
+ };
+
+ //Закрытие диалога добавления сущности по "ОК"
+ const handleEntityAddDialogCancel = () => setOpenEntityAddDialog(false);
+
+ //Панель инструмментов
+ const toolBar = (
+
+ );
+
+ //Генерация содержимого
+ return (
+
+ {openQueriesManager && }
+ {openEntityAddDialog && }
+
+
+ {queryDiagram && (
+
+ )}
+
+
+ {toolBar}
+ {query && (
+
+
+
+
+ )}
+
+
+
+ );
+};
+
+//----------------
+//Интерфейс модуля
+//----------------
+
+export { QueryEditor };
diff --git a/db/P8PNL_QE_QUERY.sql b/db/P8PNL_QE_QUERY.sql
new file mode 100644
index 0000000..318bc7d
--- /dev/null
+++ b/db/P8PNL_QE_QUERY.sql
@@ -0,0 +1,25 @@
+/*
+ Парус 8 - Панели мониторинга - Общие - Редактор запросов
+ Запросы
+*/
+create table P8PNL_QE_QUERY
+(
+ RN number(17) not null, -- Рег. номер записи
+ CODE varchar2(100) not null, -- Мнемокод
+ NAME varchar2(2000) not null, -- Наименование
+ AUTHOR varchar2(30) not null, -- Автор
+ CH_DATE date not null, -- Дата последнего изменения
+ READY number(1) default 0 not null, -- Флаг готовности к использованию (0 - нет, 1 - да)
+ PBL number(1) default 0 not null, -- Флаг публичности (0 - нет, 1 - да)
+ OPTS clob, -- Параметры запроса
+ ENTS clob, -- Сущности запроса
+ RLS clob, -- Отношения сущностей запроса
+ QRY clob, -- Запрос
+ constraint C_P8PNL_QE_QUERY_RN_PK primary key (RN),
+ constraint C_P8PNL_QE_QUERY_CODE_NB check (rtrim(CODE) is not null),
+ constraint C_P8PNL_QE_QUERY_NAME_NB check (rtrim(NAME) is not null),
+ constraint C_P8PNL_QE_QUERY_AUTHOR_FK foreign key (AUTHOR) references USERLIST (AUTHID) on delete cascade,
+ constraint C_P8PNL_QE_QUERY_READY_VAL check (READY in (0, 1)),
+ constraint C_P8PNL_QE_QUERY_PBL_VAL check (PBL in (0, 1)),
+ constraint C_P8PNL_QE_QUERY_UN unique (CODE)
+);
diff --git a/db/PKG_P8PANELS_QE.pck b/db/PKG_P8PANELS_QE.pck
new file mode 100644
index 0000000..a0f68c6
--- /dev/null
+++ b/db/PKG_P8PANELS_QE.pck
@@ -0,0 +1,342 @@
+create or replace package PKG_P8PANELS_QE as
+
+ /* Получение списка запросов */
+ procedure QUERY_LIST
+ (
+ COUT out clob -- Сериализованный список запросов
+ );
+
+ /* Добавление запроса */
+ procedure QUERY_INSERT
+ (
+ SCODE in varchar2, -- Мнемокод
+ SNAME in varchar2, -- Наименование
+ NRN out number -- Рег. номер добавленного запроса
+ );
+
+ /* Исправление запроса */
+ procedure QUERY_UPDATE
+ (
+ NRN in number, -- Рег. номер запроса
+ SCODE in varchar2, -- Мнемокод
+ SNAME in varchar2 -- Наименование
+ );
+
+ /* Удаление запроса */
+ procedure QUERY_DELETE
+ (
+ NRN in number -- Рег. номер запроса
+ );
+
+ /* Получение данных о запросе */
+ procedure QUERY_DESC
+ (
+ NRN in number, -- Рег. номер запроса
+ COUT out clob -- Сериализованное описание запроса
+ );
+
+ /* Добавление сущности в запрос */
+ procedure QUERY_ENT_ADD
+ (
+ NRN in number, -- Рег. номер запроса
+ SNAME in varchar2, -- Имя
+ STYPE in varchar2 -- Тип (см. константы SENT_TYPE_*)
+ );
+
+ /* Удаление сущности из запроса */
+ procedure QUERY_ENT_REMOVE
+ (
+ NRN in number, -- Рег. номер запроса
+ SID in varchar2 -- Идентификатор сущности
+ );
+
+ /* Установка координат сущности в редакторе диаграммы запроса */
+ procedure QUERY_ENT_POSITION_SET
+ (
+ NRN in number, -- Рег. номер запроса
+ SID in varchar2, -- Идентификатор сущности
+ NX in number, -- Координата по оси абсцисс
+ NY in number -- Координата по оси ординат
+ );
+
+ /* Добавление связи в запрос */
+ procedure QUERY_RL_ADD
+ (
+ NRN in number, -- Рег. номер запроса
+ SSOURCE in varchar2, -- Идентификатор атрибута-источника
+ STARGET in varchar2 -- Идентификатор атрибута-приёмника
+ );
+
+ /* Удаление связи из запроса */
+ procedure QUERY_RL_REMOVE
+ (
+ NRN in number, -- Рег. номер запроса
+ SID in varchar2 -- Идентификатор связи
+ );
+
+ /* Установка признака "готовности" запроса */
+ procedure QUERY_READY_SET
+ (
+ NRN in number, -- Рег. номер запроса
+ NREADY in number -- Флаг готовности к использованию (0 - нет, 1 - да)
+ );
+
+ /* Установка признака "публичности" запроса */
+ procedure QUERY_PBL_SET
+ (
+ NRN in number, -- Рег. номер запроса
+ NPBL in number -- Флаг публичности (0 - приватный, 1 - публичный)
+ );
+
+end PKG_P8PANELS_QE;
+/
+create or replace package body PKG_P8PANELS_QE as
+
+ /* Получение списка запросов */
+ procedure QUERY_LIST
+ (
+ COUT out clob -- Сериализованный список запросов
+ )
+ is
+ begin
+ /* Базово сформируем список */
+ COUT := PKG_P8PANELS_QE_BASE.QUERY_LIST_GET(SUSER => UTILIZER());
+ end QUERY_LIST;
+
+ /* Добавление запроса */
+ procedure QUERY_INSERT
+ (
+ SCODE in varchar2, -- Мнемокод
+ SNAME in varchar2, -- Наименование
+ NRN out number -- Рег. номер добавленного запроса
+ )
+ is
+ begin
+ /* Базовое добавление */
+ PKG_P8PANELS_QE_BASE.QUERY_INSERT(SCODE => SCODE, SNAME => SNAME, NRN => NRN);
+ end QUERY_INSERT;
+
+ /* Исправление запроса */
+ procedure QUERY_UPDATE
+ (
+ NRN in number, -- Рег. номер запроса
+ SCODE in varchar2, -- Мнемокод
+ SNAME in varchar2 -- Наименование
+ )
+ is
+ begin
+ /* Провим права доступа */
+ PKG_P8PANELS_QE_BASE.QUERY_ACCESS_MODIFY(NRN => NRN, SUSER => UTILIZER());
+ /* Базовое исправление */
+ PKG_P8PANELS_QE_BASE.QUERY_UPDATE(NRN => NRN, SCODE => SCODE, SNAME => SNAME);
+ end QUERY_UPDATE;
+
+ /* Удаление запроса */
+ procedure QUERY_DELETE
+ (
+ NRN in number -- Рег. номер запроса
+ )
+ is
+ begin
+ /* Провим права доступа */
+ PKG_P8PANELS_QE_BASE.QUERY_ACCESS_MODIFY(NRN => NRN, SUSER => UTILIZER());
+ /* Базовое удаление */
+ PKG_P8PANELS_QE_BASE.QUERY_DELETE(NRN => NRN);
+ end QUERY_DELETE;
+
+ /* Получение описания запроса */
+ procedure QUERY_DESC
+ (
+ NRN in number, -- Рег. номер запроса
+ COUT out clob -- Сериализованное описание запроса
+ )
+ is
+ begin
+ /* Проверим права доступа */
+ PKG_P8PANELS_QE_BASE.QUERY_ACCESS_VIEW(NRN => NRN, SUSER => UTILIZER());
+ /* Получим описание запроса */
+ COUT := PKG_P8PANELS_QE_BASE.QUERY_DESC_GET(NRN => NRN);
+ end QUERY_DESC;
+
+ /* Добавление сущности в запрос */
+ procedure QUERY_ENT_ADD
+ (
+ NRN in number, -- Рег. номер запроса
+ SNAME in varchar2, -- Имя
+ STYPE in varchar2 -- Тип (см. константы SENT_TYPE_*)
+ )
+ is
+ RQ P8PNL_QE_QUERY%rowtype; -- Запись запроса
+ RENTS PKG_P8PANELS_QE_BASE.TENTS; -- Коллекция существующих сущностей
+ begin
+ /* Провим права доступа */
+ PKG_P8PANELS_QE_BASE.QUERY_ACCESS_MODIFY(NRN => NRN, SUSER => UTILIZER());
+ /* Читаем запись запроса */
+ RQ := PKG_P8PANELS_QE_BASE.QUERY_GET(NRN => NRN);
+ /* Читаем существующие сущности */
+ RENTS := PKG_P8PANELS_QE_BASE.QUERY_ENTS_GET(CENTS => RQ.ENTS);
+ /* Формируем описание новой сущности и добавляем её в коллекцию */
+ PKG_P8PANELS_QE_BASE.TENTS_APPEND(RENTS => RENTS, SNAME => SNAME, STYPE => STYPE);
+ /* Сохраняем обновленный набор сущностей */
+ PKG_P8PANELS_QE_BASE.QUERY_ENTS_SET(NRN => RQ.RN, RENTS => RENTS);
+ end QUERY_ENT_ADD;
+
+ /* Удаление сущности из запроса */
+ procedure QUERY_ENT_REMOVE
+ (
+ NRN in number, -- Рег. номер запроса
+ SID in varchar2 -- Идентификатор сущности
+ )
+ is
+ RQ P8PNL_QE_QUERY%rowtype; -- Запись запроса
+ RENTS PKG_P8PANELS_QE_BASE.TENTS; -- Коллекция существующих сущностей
+ RENT PKG_P8PANELS_QE_BASE.TENT; -- Удаляемая сущность
+ RRLS PKG_P8PANELS_QE_BASE.TRLS; -- Коллекция существующих связей
+ RRLS_TMP PKG_P8PANELS_QE_BASE.TRLS; -- Буфер для коллекции удаляемых связей
+ begin
+ /* Провим права доступа */
+ PKG_P8PANELS_QE_BASE.QUERY_ACCESS_MODIFY(NRN => NRN, SUSER => UTILIZER());
+ /* Читаем запись запроса */
+ RQ := PKG_P8PANELS_QE_BASE.QUERY_GET(NRN => NRN);
+ /* Читаем существующие сущности */
+ RENTS := PKG_P8PANELS_QE_BASE.QUERY_ENTS_GET(CENTS => RQ.ENTS);
+ /* Читаем свзяи */
+ RRLS := PKG_P8PANELS_QE_BASE.QUERY_RLS_GET(CRLS => RQ.RLS);
+ /* Находим удаляемую сущность */
+ begin
+ RENT := RENTS(PKG_P8PANELS_QE_BASE.TENTS_INDEX_BY_ID(RENTS => RENTS, SID => SID));
+ exception
+ when VALUE_ERROR then
+ P_EXCEPTION(0,
+ 'Сущность с идентификатором "%s" в запросе "%s" не определена.',
+ COALESCE(SID, '<НЕ УКАЗАН>'),
+ TO_CHAR(NRN));
+ end;
+ /* Удаляем сущность из коллекции */
+ PKG_P8PANELS_QE_BASE.TENTS_REMOVE(RENTS => RENTS, SID => SID);
+ /* Обходим атрибуты сущности */
+ if ((RENT.RATTRS is not null) and (RENT.RATTRS.COUNT > 0)) then
+ for I in RENT.RATTRS.FIRST .. RENT.RATTRS.LAST
+ loop
+ /* Если атрибут есть в связях (как источник или как приёмник) */
+ for J in 0 .. 1
+ loop
+ RRLS_TMP := PKG_P8PANELS_QE_BASE.TRLS_LIST_BY_ST(RRLS => RRLS,
+ SSOURCE_TARGET => RENT.RATTRS(I).SID,
+ NLIST_TYPE => J);
+ /* То связь должна быть удалена */
+ if ((RRLS_TMP is not null) and (RRLS_TMP.COUNT > 0)) then
+ for K in RRLS_TMP.FIRST .. RRLS_TMP.LAST
+ loop
+ PKG_P8PANELS_QE_BASE.TRLS_REMOVE(RRLS => RRLS, SID => RRLS_TMP(K).SID);
+ end loop;
+ end if;
+ end loop;
+ end loop;
+ end if;
+ /* Сохраняем обновленный набор сущностей */
+ PKG_P8PANELS_QE_BASE.QUERY_ENTS_SET(NRN => RQ.RN, RENTS => RENTS);
+ /* Сохраняем обновленный набор связей */
+ PKG_P8PANELS_QE_BASE.QUERY_RLS_SET(NRN => RQ.RN, RRLS => RRLS);
+ end QUERY_ENT_REMOVE;
+
+ /* Установка координат сущности в редакторе диаграммы запроса */
+ procedure QUERY_ENT_POSITION_SET
+ (
+ NRN in number, -- Рег. номер запроса
+ SID in varchar2, -- Идентификатор сущности
+ NX in number, -- Координата по оси абсцисс
+ NY in number -- Координата по оси ординат
+ )
+ is
+ RQ P8PNL_QE_QUERY%rowtype; -- Запись запроса
+ RENTS PKG_P8PANELS_QE_BASE.TENTS; -- Коллекция существующих сущностей
+ begin
+ /* Читаем запись запроса */
+ RQ := PKG_P8PANELS_QE_BASE.QUERY_GET(NRN => NRN);
+ /* Сохранять расположение будем только если это автор - так остальные могут перемещать на экране диаграмму, но не запоминать расположение сущностей */
+ if (RQ.AUTHOR = UTILIZER()) then
+ /* Читаем существующие сущности */
+ RENTS := PKG_P8PANELS_QE_BASE.QUERY_ENTS_GET(CENTS => RQ.ENTS);
+ /* Меняем координаты сущности в коллекции */
+ PKG_P8PANELS_QE_BASE.TENTS_POSITION_SET(RENTS => RENTS, SID => SID, NX => NX, NY => NY);
+ /* Сохраняем обновленный набор сущностей */
+ PKG_P8PANELS_QE_BASE.QUERY_ENTS_SET(NRN => RQ.RN, RENTS => RENTS);
+ end if;
+ end QUERY_ENT_POSITION_SET;
+
+ /* Добавление связи в запрос */
+ procedure QUERY_RL_ADD
+ (
+ NRN in number, -- Рег. номер запроса
+ SSOURCE in varchar2, -- Идентификатор атрибута-источника
+ STARGET in varchar2 -- Идентификатор атрибута-приёмника
+ )
+ is
+ RQ P8PNL_QE_QUERY%rowtype; -- Запись запроса
+ RRLS PKG_P8PANELS_QE_BASE.TRLS; -- Коллекция существующих связей
+ begin
+ /* Провим права доступа */
+ PKG_P8PANELS_QE_BASE.QUERY_ACCESS_MODIFY(NRN => NRN, SUSER => UTILIZER());
+ /* Читаем запись запроса */
+ RQ := PKG_P8PANELS_QE_BASE.QUERY_GET(NRN => NRN);
+ /* Читаем существующие связи */
+ RRLS := PKG_P8PANELS_QE_BASE.QUERY_RLS_GET(CRLS => RQ.RLS);
+ /* Формируем описание новой связи и добавляем её в коллекцию */
+ PKG_P8PANELS_QE_BASE.TRLS_APPEND(RRLS => RRLS, SSOURCE => SSOURCE, STARGET => STARGET);
+ /* Сохраняем обновленный набор связей */
+ PKG_P8PANELS_QE_BASE.QUERY_RLS_SET(NRN => RQ.RN, RRLS => RRLS);
+ end QUERY_RL_ADD;
+
+ /* Удаление связи из запроса */
+ procedure QUERY_RL_REMOVE
+ (
+ NRN in number, -- Рег. номер запроса
+ SID in varchar2 -- Идентификатор связи
+ )
+ is
+ RQ P8PNL_QE_QUERY%rowtype; -- Запись запроса
+ RRLS PKG_P8PANELS_QE_BASE.TRLS; -- Коллекция существующих связей
+ begin
+ /* Провим права доступа */
+ PKG_P8PANELS_QE_BASE.QUERY_ACCESS_MODIFY(NRN => NRN, SUSER => UTILIZER());
+ /* Читаем запись запроса */
+ RQ := PKG_P8PANELS_QE_BASE.QUERY_GET(NRN => NRN);
+ /* Читаем существующие связи */
+ RRLS := PKG_P8PANELS_QE_BASE.QUERY_RLS_GET(CRLS => RQ.RLS);
+ /* Удаляем связи из коллекции */
+ PKG_P8PANELS_QE_BASE.TRLS_REMOVE(RRLS => RRLS, SID => SID);
+ /* Сохраняем обновленный набор связей */
+ PKG_P8PANELS_QE_BASE.QUERY_RLS_SET(NRN => RQ.RN, RRLS => RRLS);
+ end QUERY_RL_REMOVE;
+
+ /* Установка признака "готовности" запроса */
+ procedure QUERY_READY_SET
+ (
+ NRN in number, -- Рег. номер запроса
+ NREADY in number -- Флаг готовности к использованию (0 - нет, 1 - да)
+ )
+ is
+ begin
+ /* Провим права доступа */
+ PKG_P8PANELS_QE_BASE.QUERY_ACCESS_MODIFY(NRN => NRN, SUSER => UTILIZER());
+ /* Базовая установка признака готовности */
+ PKG_P8PANELS_QE_BASE.QUERY_READY_SET(NRN => NRN, NREADY => NREADY);
+ end QUERY_READY_SET;
+
+ /* Установка признака "публичности" запроса */
+ procedure QUERY_PBL_SET
+ (
+ NRN in number, -- Рег. номер запроса
+ NPBL in number -- Флаг публичности (0 - приватный, 1 - публичный)
+ )
+ is
+ begin
+ /* Провим права доступа */
+ PKG_P8PANELS_QE_BASE.QUERY_ACCESS_MODIFY(NRN => NRN, SUSER => UTILIZER());
+ /* Базовая установка признака публичности */
+ PKG_P8PANELS_QE_BASE.QUERY_PBL_SET(NRN => NRN, NPBL => NPBL);
+ end QUERY_PBL_SET;
+
+end PKG_P8PANELS_QE;
+/
diff --git a/db/PKG_P8PANELS_QE_BASE.pck b/db/PKG_P8PANELS_QE_BASE.pck
new file mode 100644
index 0000000..2e30f80
--- /dev/null
+++ b/db/PKG_P8PANELS_QE_BASE.pck
@@ -0,0 +1,1573 @@
+create or replace package PKG_P8PANELS_QE_BASE as
+
+ /* Константы - Типы сущностей */
+ SENT_TYPE_TABLE constant PKG_STD.TSTRING := 'TABLE'; -- Таблица
+ SENT_TYPE_VIEW constant PKG_STD.TSTRING := 'VIEW'; -- Представление
+
+ /* Типы данных - Атрибут сущности */
+ type TATTR is record
+ (
+ SID PKG_STD.TSTRING, -- Уникальный идентификатор в запросе
+ SNAME PKG_STD.TSTRING, -- Имя
+ STITLE PKG_STD.TSTRING, -- Заголовок
+ NDATA_TYPE PKG_STD.TNUMBER -- Тип данных (см. константы PKG_STD.DATA_TYPE_*)
+ );
+
+ /* Типы данных - Коллекция атрибутов сущности */
+ type TATTRS is table of TATTR;
+
+ /* Типы данных - Сущность */
+ type TENT is record
+ (
+ SID PKG_STD.TSTRING, -- Уникальный идентификатор в запросе
+ SNAME PKG_STD.TSTRING, -- Имя
+ STITLE PKG_STD.TSTRING, -- Заголовок
+ STYPE PKG_STD.TSTRING, -- Тип (см. константы SENT_TYPE_*)
+ NX PKG_STD.TNUMBER := 0, -- Координата по оси абсцисс
+ NY PKG_STD.TNUMBER := 0, -- Координата по оси ординат
+ RATTRS TATTRS -- Атрибуты
+ );
+
+ /* Типы данных - Коллекция сущностей */
+ type TENTS is table of TENT;
+
+ /* Типы данных - Отношение */
+ type TRL is record
+ (
+ SID PKG_STD.TSTRING, -- Уникальный идентификатор в запросе
+ SSOURCE PKG_STD.TSTRING, -- Идентификатор атрибута-источника
+ STARGET PKG_STD.TSTRING -- Идентификатор атрибута-приёмника
+ );
+
+ /* Типы данных - Коллекция отношений */
+ type TRLS is table of TRL;
+
+ /* Поиск индекса сущности по идентификатору */
+ function TENTS_INDEX_BY_ID
+ (
+ RENTS in TENTS, -- Коллекция сущностей
+ SID in varchar2 -- Искомый идентификатор
+ ) return number; -- Индекс найденной сущности (null - если не найдено)
+
+ /* Добавление сущности в коллекцию */
+ procedure TENTS_APPEND
+ (
+ RENTS in out nocopy TENTS, -- Изменяемая коллекция
+ SNAME in varchar2, -- Имя
+ STYPE in varchar2 -- Тип (см. константы SENT_TYPE_*)
+ );
+
+ /* Удаление сущности из коллекции */
+ procedure TENTS_REMOVE
+ (
+ RENTS in out nocopy TENTS, -- Изменяемая коллекция
+ SID in varchar2 -- Идентификатор удялемой сущности
+ );
+
+ /* Установка координат сущности в коллекции */
+ procedure TENTS_POSITION_SET
+ (
+ RENTS in out nocopy TENTS, -- Изменяемая коллекция
+ SID in varchar2, -- Идентификатор сущности
+ NX in number, -- Координата по оси абсцисс
+ NY in number -- Координата по оси ординат
+ );
+
+ /* Формирование коллекции связей по источнику/приёмнику */
+ function TRLS_LIST_BY_ST
+ (
+ RRLS in TRLS, -- Коллекция связей
+ SSOURCE_TARGET in varchar2, -- Идентификатор источника/приёмкника
+ NLIST_TYPE in number -- Тип формирования коллекции (0 - по источнику, 1 - по приёмнику
+ ) return TRLS; -- Сформированная коллекция
+
+ /* Добавление связи в коллекцию */
+ procedure TRLS_APPEND
+ (
+ RRLS in out nocopy TRLS, -- Изменяемая коллекция
+ SSOURCE in varchar2, -- Источник
+ STARGET in varchar2 -- Приёмник
+ );
+
+ /* Удаление связи из коллекции */
+ procedure TRLS_REMOVE
+ (
+ RRLS in out nocopy TRLS, -- Изменяемая коллекция
+ SID in varchar2 -- Идентификатор удялемой связи
+ );
+
+ /* Считывание записи запроса */
+ function QUERY_GET
+ (
+ NRN in number -- Рег. номер запроса
+ ) return P8PNL_QE_QUERY%rowtype; -- Запись запроса
+
+ /* Получение признака возможности изменения запроса */
+ function QUERY_ACCESS_SIGN_MODIFY
+ (
+ NRN in number, -- Рег. номер запроса
+ SUSER in varchar2 -- Имя пользователя
+ ) return number; -- Признак возможности изменения запроса (0 - нет, 1 - да)
+
+ /* Проверка возможности изменения запроса */
+ procedure QUERY_ACCESS_MODIFY
+ (
+ NRN in number, -- Рег. номер запроса
+ SUSER in varchar2 -- Имя пользователя
+ );
+
+ /* Получение признака возможности просмотра запроса */
+ function QUERY_ACCESS_SIGN_VIEW
+ (
+ NRN in number, -- Рег. номер запроса
+ SUSER in varchar2 -- Имя пользователя
+ ) return number; -- Признак возможности просмотра запроса (0 - нет, 1 - да)
+
+ /* Проверка возможности просмотра запроса */
+ procedure QUERY_ACCESS_VIEW
+ (
+ NRN in number, -- Рег. номер запроса
+ SUSER in varchar2 -- Имя пользователя
+ );
+
+ /* Добавление запроса */
+ procedure QUERY_INSERT
+ (
+ SCODE in varchar2, -- Мнемокод
+ SNAME in varchar2, -- Наименование
+ NRN out number -- Рег. номер добавленного запроса
+ );
+
+ /* Исправление запроса */
+ procedure QUERY_UPDATE
+ (
+ NRN in number, -- Рег. номер запроса
+ SCODE in varchar2, -- Мнемокод
+ SNAME in varchar2 -- Наименование
+ );
+
+ /* Удаление запроса */
+ procedure QUERY_DELETE
+ (
+ NRN in number -- Рег. номер запроса
+ );
+
+ /* Формирование списка запросов */
+ function QUERY_LIST_GET
+ (
+ SUSER in varchar2 -- Имя пользователя
+ ) return clob; -- Список запросов
+
+ /* Получение описания запроса */
+ function QUERY_DESC_GET
+ (
+ NRN in number -- Рег. номер запроса
+ ) return clob; -- XML-описание
+
+ /* Чтение сущностей запроса */
+ function QUERY_ENTS_GET
+ (
+ CENTS in clob -- Сериализованное описание сущностей
+ ) return TENTS; -- Коллекция сущностей
+
+ /* Запись сущностей запроса */
+ procedure QUERY_ENTS_SET
+ (
+ NRN in number, -- Рег. номер запроса
+ RENTS in TENTS -- Коллекция сущностей
+ );
+
+ /* Чтение связей запроса */
+ function QUERY_RLS_GET
+ (
+ CRLS in clob -- Сериализованное описание связей
+ ) return TRLS; -- Коллекция связей
+
+ /* Запись связей запроса */
+ procedure QUERY_RLS_SET
+ (
+ NRN in number, -- Рег. номер запроса
+ RRLS in TRLS -- Коллекция связей
+ );
+
+ /* Установка признака "готовности" запроса */
+ procedure QUERY_READY_SET
+ (
+ NRN in number, -- Рег. номер запроса
+ NREADY in number -- Флаг готовности к использованию (0 - нет, 1 - да)
+ );
+
+ /* Установка признака "публичности" запроса */
+ procedure QUERY_PBL_SET
+ (
+ NRN in number, -- Рег. номер запроса
+ NPBL in number -- Флаг публичности (0 - приватный, 1 - публичный)
+ );
+
+end PKG_P8PANELS_QE_BASE;
+/
+create or replace package body PKG_P8PANELS_QE_BASE as
+
+ /* Константы - Теги для сериализации */
+ STAG_DATA constant PKG_STD.TSTRING := 'XDATA'; -- Данные
+ STAG_QUERIES constant PKG_STD.TSTRING := 'XQUERIES'; -- Запросы
+ STAG_QUERY constant PKG_STD.TSTRING := 'XQUERY'; -- Запрос
+ STAG_ATTRS constant PKG_STD.TSTRING := 'XATTRS'; -- Атрибуты сущности
+ STAG_ATTR constant PKG_STD.TSTRING := 'XATTR'; -- Атрибут сущности
+ STAG_ENTS constant PKG_STD.TSTRING := 'XENTS'; -- Сущности
+ STAG_ENT constant PKG_STD.TSTRING := 'XENT'; -- Сущность
+ STAG_RLS constant PKG_STD.TSTRING := 'XRLS'; -- Связи
+ STAG_RL constant PKG_STD.TSTRING := 'XRL'; -- Связь
+ STAG_OPTS constant PKG_STD.TSTRING := 'XOPTS'; -- Параметры
+ STAG_OPT constant PKG_STD.TSTRING := 'XOPT'; -- Параметр
+
+ /* Константы - Атрибуты для сериализации */
+ SATTR_ID constant PKG_STD.TSTRING := 'id'; -- Идентификатор
+ SATTR_RN constant PKG_STD.TSTRING := 'rn'; -- Регистрационный номер
+ SATTR_CODE constant PKG_STD.TSTRING := 'code'; -- Код
+ SATTR_NAME constant PKG_STD.TSTRING := 'name'; -- Имя
+ SATTR_AUTHOR constant PKG_STD.TSTRING := 'author'; -- Автор
+ SATTR_CH_DATE constant PKG_STD.TSTRING := 'chDate'; -- Дата изменения
+ SATTR_READY constant PKG_STD.TSTRING := 'ready'; -- Готовность к использованию
+ SATTR_PBL constant PKG_STD.TSTRING := 'pbl'; -- Публичность
+ SATTR_MODIFY constant PKG_STD.TSTRING := 'modify'; -- Изменяемость
+ SATTR_TITLE constant PKG_STD.TSTRING := 'title'; -- Заголовок
+ SATTR_TYPE constant PKG_STD.TSTRING := 'type'; -- Тип
+ SATTR_DATA_TYPE constant PKG_STD.TSTRING := 'dataType'; -- Тип данных
+ SATTR_X constant PKG_STD.TSTRING := 'x'; -- Координата по X
+ SATTR_Y constant PKG_STD.TSTRING := 'y'; -- Координата по Y
+ SATTR_SOURCE constant PKG_STD.TSTRING := 'source'; -- Источник
+ SATTR_TARGET constant PKG_STD.TSTRING := 'target'; -- Приёмник
+
+ /* Получение заголовка представления из метаданных */
+ function DMSCLVIEWS_TITLE_GET
+ (
+ SVIEW_NAME in varchar2 -- Имя представления
+ ) return varchar2 -- Заголовок представления из метаданных
+ is
+ begin
+ /* Обратимся к метаописанию представления */
+ for V in (select T.VIEW_NOTE,
+ UL.UNITNAME
+ from DMSCLVIEWS T,
+ UNITLIST UL
+ where T.VIEW_NAME = SVIEW_NAME
+ and T.CUSTOM_QUERY = 0
+ and T.PRN = UL.RN)
+ loop
+ if (V.VIEW_NOTE = SVIEW_NAME) then
+ return V.UNITNAME;
+ else
+ return V.VIEW_NOTE;
+ end if;
+ end loop;
+ /* Ничего не нашли - вернём обычное имя */
+ return SVIEW_NAME;
+ end DMSCLVIEWS_TITLE_GET;
+
+ /* Получение заголовка атрибута представления из метаданных */
+ function DMSCLVIEWSATTRS_TITLE_GET
+ (
+ SVIEW_NAME in varchar2, -- Имя представления
+ SATTR_NAME in varchar2 -- Имя атрибута
+ ) return varchar2 -- Заголовок атрибута представления из метаданных
+ is
+ begin
+ /* Обратимся к метаописанию представления */
+ for V in (select T.RN
+ from DMSCLVIEWS T
+ where T.VIEW_NAME = SVIEW_NAME
+ and T.CUSTOM_QUERY = 0)
+ loop
+ /* Обходим поля найденного представления */
+ for F in (select A.CAPTION
+ from DMSCLVIEWSATTRS T,
+ DMSCLATTRS A
+ where T.PRN = V.RN
+ and T.COLUMN_NAME = SATTR_NAME
+ and T.ATTR = A.RN)
+ loop
+ return F.CAPTION;
+ end loop;
+ end loop;
+ /* Ничего не нашли - вернём обычное имя */
+ return SATTR_NAME;
+ end DMSCLVIEWSATTRS_TITLE_GET;
+
+ /* Формирование идентификатора атрибута сущности */
+ function TATTR_ID_MAKE
+ (
+ SENT_ID in varchar2, -- Уникальный идентификатор родительской сущности
+ SNAME in varchar2 -- Имя атрибута
+ ) return varchar2 -- Сформированный идентификатор
+ is
+ begin
+ /* Проверим параметры */
+ if (SNAME is null) then
+ P_EXCEPTION(0, 'Не указано имя атрибута сущности.');
+ end if;
+ if (SENT_ID is null) then
+ P_EXCEPTION(0,
+ 'Не указан идентификатор родительской сущности атрибута.');
+ end if;
+ /* Соберем идентификатор */
+ return SENT_ID || '.' || SNAME;
+ end TATTR_ID_MAKE;
+
+ /* Сериализация атрибута сущности */
+ procedure TATTR_TO_XML
+ (
+ RATTR in TATTR -- Атрибут сущности
+ )
+ is
+ begin
+ /* Открываем описание атрибута сущности */
+ PKG_XFAST.DOWN_NODE(SNAME => STAG_ATTR);
+ /* Атрибут */
+ PKG_XFAST.ATTR(SNAME => SATTR_ID, SVALUE => RATTR.SID);
+ PKG_XFAST.ATTR(SNAME => SATTR_NAME, SVALUE => RATTR.SNAME);
+ PKG_XFAST.ATTR(SNAME => SATTR_TITLE, SVALUE => RATTR.STITLE);
+ PKG_XFAST.ATTR(SNAME => SATTR_DATA_TYPE, NVALUE => RATTR.NDATA_TYPE);
+ /* Закрываем описание атрибута сущности */
+ PKG_XFAST.UP();
+ end TATTR_TO_XML;
+
+ /* Десериализация атрибута сущности */
+ function TATTR_FROM_XML
+ (
+ CXML in clob -- XML-описание атрибута сущности
+ ) return TATTR -- Атрибут сущности
+ is
+ RRES TATTR; -- Буфер для результата
+ XDOC PKG_XPATH.TDOCUMENT; -- Документ XML
+ XROOT PKG_XPATH.TNODE; -- Корень документа XML
+ XNODE PKG_XPATH.TNODE; -- Буфер узла документа
+ begin
+ /* Если данные есть */
+ if (CXML is not null) then
+ begin
+ /* Разбираем XML */
+ XDOC := PKG_XPATH.PARSE_FROM_CLOB(LCXML => CXML);
+ /* Считываем корневой узел */
+ XROOT := PKG_XPATH.ROOT_NODE(RDOCUMENT => XDOC);
+ XNODE := PKG_XPATH.SINGLE_NODE(RPARENT_NODE => XROOT, SPATTERN => '/' || STAG_ATTR);
+ /* Получаем значения */
+ RRES.SID := PKG_XPATH.ATTRIBUTE(RNODE => XNODE, SNAME => SATTR_ID);
+ RRES.SNAME := PKG_XPATH.ATTRIBUTE(RNODE => XNODE, SNAME => SATTR_NAME);
+ RRES.STITLE := PKG_XPATH.ATTRIBUTE(RNODE => XNODE, SNAME => SATTR_TITLE);
+ RRES.NDATA_TYPE := PKG_XPATH.ATTRIBUTE_NUM(RNODE => XNODE, SNAME => SATTR_DATA_TYPE);
+ /* Освободим документ */
+ PKG_XPATH.FREE(RDOCUMENT => XDOC);
+ exception
+ when others then
+ /* Освободим документ */
+ PKG_XPATH.FREE(RDOCUMENT => XDOC);
+ /* Вернем ошибку */
+ PKG_STATE.DIAGNOSTICS_STACKED();
+ P_EXCEPTION(0, PKG_STATE.SQL_ERRM());
+ end;
+ end if;
+ /* Вернём результат */
+ return RRES;
+ end TATTR_FROM_XML;
+
+ /* Сериализация коллекции атрибутов сущности */
+ procedure TATTRS_TO_XML
+ (
+ RATTRS in TATTRS -- Коллекция атрибутов сущности
+ )
+ is
+ begin
+ /* Открываем описание атрибутов сущности */
+ PKG_XFAST.DOWN_NODE(SNAME => STAG_ATTRS);
+ /* Обходим атрибуты из коллекции */
+ if ((RATTRS is not null) and (RATTRS.COUNT > 0)) then
+ for I in RATTRS.FIRST .. RATTRS.LAST
+ loop
+ /* Добавляем описание атрибута сущности */
+ TATTR_TO_XML(RATTR => RATTRS(I));
+ end loop;
+ end if;
+ /* Закрываем описание атрибутов сущности */
+ PKG_XFAST.UP();
+ end TATTRS_TO_XML;
+
+ /* Десериализация коллекции атрибутов сущности */
+ function TATTRS_FROM_XML
+ (
+ CXML in clob -- XML-описание коллекции атрибутов сущности
+ ) return TATTRS -- Коллекция атрибутов сущности
+ is
+ RRES TATTRS; -- Буфер для результата
+ XDOC PKG_XPATH.TDOCUMENT; -- Документ XML
+ XROOT PKG_XPATH.TNODE; -- Корень документа XML
+ XNODE PKG_XPATH.TNODE; -- Буфер узла документа
+ XNODES PKG_XPATH.TNODES; -- Буфер коллекции узлов документа
+ begin
+ /* Инициализируем результат */
+ RRES := TATTRS();
+ /* Если данные есть */
+ if (CXML is not null) then
+ begin
+ /* Разбираем XML */
+ XDOC := PKG_XPATH.PARSE_FROM_CLOB(LCXML => CXML);
+ /* Считываем корневой узел */
+ XROOT := PKG_XPATH.ROOT_NODE(RDOCUMENT => XDOC);
+ /* Считывание списка атрибутов */
+ XNODES := PKG_XPATH.LIST_NODES(RPARENT_NODE => XROOT, SPATTERN => '/' || STAG_ATTRS || '/' || STAG_ATTR);
+ /* Цикл по списку атрибутов */
+ for I in 1 .. PKG_XPATH.COUNT_NODES(RNODES => XNODES)
+ loop
+ /* Считаем элемент по его номеру */
+ XNODE := PKG_XPATH.ITEM_NODE(RNODES => XNODES, INUMBER => I);
+ /* Сериализуем и добавим его в коллекцию */
+ RRES.EXTEND();
+ RRES(RRES.LAST) := TATTR_FROM_XML(CXML => PKG_XPATH.SERIALIZE_TO_CLOB(RNODE => XNODE));
+ end loop;
+ /* Освободим документ */
+ PKG_XPATH.FREE(RDOCUMENT => XDOC);
+ exception
+ when others then
+ /* Освободим документ */
+ PKG_XPATH.FREE(RDOCUMENT => XDOC);
+ /* Вернем ошибку */
+ PKG_STATE.DIAGNOSTICS_STACKED();
+ P_EXCEPTION(0, PKG_STATE.SQL_ERRM());
+ end;
+ end if;
+ /* Вернём результат */
+ return RRES;
+ end TATTRS_FROM_XML;
+
+ /* Формирование идентификатора сущности */
+ function TENT_ID_MAKE
+ (
+ SNAME in varchar2, -- Имя сущности
+ NNUMB in number -- Номер сущности в запросе
+ ) return varchar2 -- Сформированный идентификатор
+ is
+ begin
+ /* Проверим параметры */
+ if (SNAME is null) then
+ P_EXCEPTION(0, 'Не указано имя сущности.');
+ end if;
+ /* Соберем идентификатор */
+ if (NNUMB is null) then
+ return SNAME;
+ else
+ return SNAME || TO_CHAR(NNUMB);
+ end if;
+ end TENT_ID_MAKE;
+
+ /* Формирование описания сущности */
+ function TENT_MAKE
+ (
+ SNAME in varchar2, -- Имя
+ STYPE in varchar2, -- Тип (см. константы SENT_TYPE_*)
+ NNUMB in number -- Номер сущности
+ ) return TENT -- Описание сущности
+ is
+ RENT TENT; -- Буфер для результата
+ RVIEW PKG_OBJECT_DESC.TVIEW; -- Описание представления
+ RVIEW_FIELDS PKG_OBJECT_DESC.TCOLUMNS; -- Коллекция описаний полей представления
+ RVIEW_FIELD PKG_OBJECT_DESC.TCOLUMN; -- Описание поля представления
+ begin
+ /* Проверим корректность типа сущности */
+ if (STYPE not in (SENT_TYPE_TABLE, SENT_TYPE_VIEW)) then
+ P_EXCEPTION(0,
+ 'Сущности типа "%s" не поддерживаются.',
+ COALESCE(STYPE, '<НЕ УКАЗАН>'));
+ end if;
+ /* Если сущность это представление */
+ if (STYPE = SENT_TYPE_VIEW) then
+ /* Получим описание представления */
+ RVIEW := PKG_OBJECT_DESC.DESC_VIEW(SVIEW_NAME => SNAME, BRAISE_ERROR => true);
+ /* Получим список полей представления */
+ RVIEW_FIELDS := PKG_OBJECT_DESC.DESC_SEL_COLUMNS(SSELECT_NAME => SNAME, BRAISE_ERROR => true);
+ /* Собираем заголовок сущности */
+ RENT.SID := TENT_ID_MAKE(SNAME => RVIEW.VIEW_NAME, NNUMB => NNUMB);
+ RENT.SNAME := RVIEW.VIEW_NAME;
+ RENT.STITLE := DMSCLVIEWS_TITLE_GET(SVIEW_NAME => RENT.SNAME);
+ RENT.STYPE := SENT_TYPE_VIEW;
+ RENT.RATTRS := TATTRS();
+ /* Собираем атрибуты в ответ */
+ for I in 1 .. PKG_OBJECT_DESC.COUNT_COLUMNS(RCOLUMNS => RVIEW_FIELDS)
+ loop
+ /* По умолчанию - первые 10 */
+ exit when I > 10;
+ /* Считываем очередное поле из коллекции описания полей представления */
+ RVIEW_FIELD := PKG_OBJECT_DESC.FETCH_COLUMN(RCOLUMNS => RVIEW_FIELDS, IINDEX => I);
+ /* Формируем описание поля и добавляем в коллекцию атрибутов сущности */
+ RENT.RATTRS.EXTEND();
+ RENT.RATTRS(RENT.RATTRS.LAST).SID := TATTR_ID_MAKE(SENT_ID => RENT.SID, SNAME => RVIEW_FIELD.COLUMN_NAME);
+ RENT.RATTRS(RENT.RATTRS.LAST).SNAME := RVIEW_FIELD.COLUMN_NAME;
+ RENT.RATTRS(RENT.RATTRS.LAST).STITLE := DMSCLVIEWSATTRS_TITLE_GET(SVIEW_NAME => RENT.SNAME,
+ SATTR_NAME => RENT.RATTRS(RENT.RATTRS.LAST)
+ .SNAME);
+ RENT.RATTRS(RENT.RATTRS.LAST).NDATA_TYPE := RVIEW_FIELD.DATA_TYPE;
+ end loop;
+ end if;
+ /* Если сущность это таблица */
+ if (STYPE = SENT_TYPE_TABLE) then
+ P_EXCEPTION(0,
+ 'Поддержка сущностей типа "Таблица" ещё не реализована.');
+ end if;
+ /* Вернем полученное */
+ return RENT;
+ end TENT_MAKE;
+
+ /* Сериализация сущности */
+ procedure TENT_TO_XML
+ (
+ RENT in TENT -- Сущность
+ )
+ is
+ begin
+ /* Открываем описание сущности */
+ PKG_XFAST.DOWN_NODE(SNAME => STAG_ENT);
+ /* Cущность */
+ PKG_XFAST.ATTR(SNAME => SATTR_ID, SVALUE => RENT.SID);
+ PKG_XFAST.ATTR(SNAME => SATTR_NAME, SVALUE => RENT.SNAME);
+ PKG_XFAST.ATTR(SNAME => SATTR_TITLE, SVALUE => RENT.STITLE);
+ PKG_XFAST.ATTR(SNAME => SATTR_TYPE, SVALUE => RENT.STYPE);
+ PKG_XFAST.ATTR(SNAME => SATTR_X, NVALUE => RENT.NX);
+ PKG_XFAST.ATTR(SNAME => SATTR_Y, NVALUE => RENT.NY);
+ /* Атрибуты */
+ TATTRS_TO_XML(RATTRS => RENT.RATTRS);
+ /* Закрываем описание сущности */
+ PKG_XFAST.UP();
+ end TENT_TO_XML;
+
+ /* Десериализация сущности */
+ function TENT_FROM_XML
+ (
+ CXML in clob -- XML-описание сущности
+ ) return TENT -- Сущность
+ is
+ RRES TENT; -- Буфер для результата
+ XDOC PKG_XPATH.TDOCUMENT; -- Документ XML
+ XROOT PKG_XPATH.TNODE; -- Корень документа XML
+ XNODE PKG_XPATH.TNODE; -- Буфер узла документа
+ begin
+ /* Инициализируем сущность */
+ RRES.RATTRS := TATTRS();
+ /* Если данные есть */
+ if (CXML is not null) then
+ begin
+ /* Разбираем XML */
+ XDOC := PKG_XPATH.PARSE_FROM_CLOB(LCXML => CXML);
+ /* Считываем корневой узел */
+ XROOT := PKG_XPATH.ROOT_NODE(RDOCUMENT => XDOC);
+ /* Считаваем узел сущности */
+ XNODE := PKG_XPATH.SINGLE_NODE(RPARENT_NODE => XROOT, SPATTERN => '/' || STAG_ENT);
+ /* Получаем значения */
+ RRES.SID := PKG_XPATH.ATTRIBUTE(RNODE => XNODE, SNAME => SATTR_ID);
+ RRES.SNAME := PKG_XPATH.ATTRIBUTE(RNODE => XNODE, SNAME => SATTR_NAME);
+ RRES.STITLE := PKG_XPATH.ATTRIBUTE(RNODE => XNODE, SNAME => SATTR_TITLE);
+ RRES.STYPE := PKG_XPATH.ATTRIBUTE(RNODE => XNODE, SNAME => SATTR_TYPE);
+ RRES.NX := PKG_XPATH.ATTRIBUTE_NUM(RNODE => XNODE, SNAME => SATTR_X);
+ RRES.NY := PKG_XPATH.ATTRIBUTE_NUM(RNODE => XNODE, SNAME => SATTR_Y);
+ RRES.RATTRS := TATTRS_FROM_XML(CXML => PKG_XPATH.SERIALIZE_TO_CLOB(RNODE => PKG_XPATH.SINGLE_NODE(RPARENT_NODE => XNODE,
+ SPATTERN => STAG_ATTRS)));
+ /* Освободим документ */
+ PKG_XPATH.FREE(RDOCUMENT => XDOC);
+ exception
+ when others then
+ /* Освободим документ */
+ PKG_XPATH.FREE(RDOCUMENT => XDOC);
+ /* Вернем ошибку */
+ PKG_STATE.DIAGNOSTICS_STACKED();
+ P_EXCEPTION(0, PKG_STATE.SQL_ERRM());
+ end;
+ end if;
+ /* Вернём результат */
+ return RRES;
+ end TENT_FROM_XML;
+
+ /* Поиск индекса сущности по идентификатору */
+ function TENTS_INDEX_BY_ID
+ (
+ RENTS in TENTS, -- Коллекция сущностей
+ SID in varchar2 -- Искомый идентификатор
+ ) return number -- Индекс найденной сущности (null - если не найдено)
+ is
+ begin
+ /* Обходим коллекцию */
+ if ((RENTS is not null) and (RENTS.COUNT > 0)) then
+ for I in RENTS.FIRST .. RENTS.LAST
+ loop
+ begin
+ /* Возвращаем найденный индекс */
+ if (RENTS(I).SID = SID) then
+ return I;
+ end if;
+ exception
+ when NO_DATA_FOUND then
+ null;
+ end;
+ end loop;
+ end if;
+ /* Ничего не нашли */
+ return null;
+ end TENTS_INDEX_BY_ID;
+
+ /* Поиск номера сущности в коллекции */
+ function TENTS_NEXT_NUMB
+ (
+ RENTS in TENTS, -- Коллекция сущностей
+ SNAME in varchar2 -- Имя
+ ) return number -- Номер сущности в коллекции
+ is
+ NNUMB PKG_STD.TNUMBER := 0; -- Буфер для результата
+ begin
+ /* Подбираем первый свободный номер */
+ while (TENTS_INDEX_BY_ID(RENTS => RENTS, SID => TENT_ID_MAKE(SNAME => SNAME, NNUMB => NNUMB)) is not null)
+ loop
+ NNUMB := NNUMB + 1;
+ end loop;
+ /* Возвращаем его */
+ return NNUMB;
+ end TENTS_NEXT_NUMB;
+
+ /* Добавление сущности в коллекцию */
+ procedure TENTS_APPEND
+ (
+ RENTS in out nocopy TENTS, -- Изменяемая коллекция
+ SNAME in varchar2, -- Имя
+ STYPE in varchar2 -- Тип (см. константы SENT_TYPE_*)
+ )
+ is
+ RENT TENT; -- Добавляемая сущность
+ begin
+ /* Инициализируем коллекцию если необходимо */
+ if (RENTS is null) then
+ RENTS := TENTS();
+ end if;
+ /* Формируем пописание сущности */
+ RENT := TENT_MAKE(SNAME => SNAME, STYPE => STYPE, NNUMB => TENTS_NEXT_NUMB(RENTS => RENTS, SNAME => SNAME));
+ /* Добавляем её в коллекцию */
+ RENTS.EXTEND();
+ RENTS(RENTS.LAST) := RENT;
+ end TENTS_APPEND;
+
+ /* Удаление сущности из коллекции */
+ procedure TENTS_REMOVE
+ (
+ RENTS in out nocopy TENTS, -- Изменяемая коллекция
+ SID in varchar2 -- Идентификатор удялемой сущности
+ )
+ is
+ NIND PKG_STD.TNUMBER; -- Индекс сущности в коллекции
+ begin
+ NIND := TENTS_INDEX_BY_ID(RENTS => RENTS, SID => SID);
+ if (NIND is not null) then
+ RENTS.DELETE(NIND);
+ end if;
+ end TENTS_REMOVE;
+
+ /* Установка координат сущности в коллекции */
+ procedure TENTS_POSITION_SET
+ (
+ RENTS in out nocopy TENTS, -- Изменяемая коллекция
+ SID in varchar2, -- Идентификатор сущности
+ NX in number, -- Координата по оси абсцисс
+ NY in number -- Координата по оси ординат
+ )
+ is
+ NIND PKG_STD.TNUMBER; -- Индекс сущности в коллекции
+ begin
+ NIND := TENTS_INDEX_BY_ID(RENTS => RENTS, SID => SID);
+ if (NIND is not null) then
+ RENTS(NIND).NX := NX;
+ RENTS(NIND).NY := NY;
+ end if;
+ end TENTS_POSITION_SET;
+
+ /* Сериализация коллекции сущностей */
+ procedure TENTS_TO_XML
+ (
+ RENTS in TENTS -- Коллекция сущностей
+ )
+ is
+ begin
+ /* Открываем описание сущностей */
+ PKG_XFAST.DOWN_NODE(SNAME => STAG_ENTS);
+ /* Обходим сущности из коллекции */
+ if ((RENTS is not null) and (RENTS.COUNT > 0)) then
+ for I in RENTS.FIRST .. RENTS.LAST
+ loop
+ begin
+ /* Добавляем описание сущности */
+ TENT_TO_XML(RENT => RENTS(I));
+ exception
+ when NO_DATA_FOUND then
+ null;
+ end;
+ end loop;
+ end if;
+ /* Закрываем описание сущностей */
+ PKG_XFAST.UP();
+ end TENTS_TO_XML;
+
+ /* Десериализация коллекции сущностей */
+ function TENTS_FROM_XML
+ (
+ CXML in clob -- XML-описание коллекции сущностей
+ ) return TENTS -- Коллекция сущностей
+ is
+ RRES TENTS; -- Буфер для результата
+ XDOC PKG_XPATH.TDOCUMENT; -- Документ XML
+ XROOT PKG_XPATH.TNODE; -- Корень документа XML
+ XNODE PKG_XPATH.TNODE; -- Буфер узла документа
+ XNODES PKG_XPATH.TNODES; -- Буфер коллекции узлов документа
+ begin
+ /* Инициализируем результат */
+ RRES := TENTS();
+ /* Если данные есть */
+ if (CXML is not null) then
+ begin
+ /* Разбираем XML */
+ XDOC := PKG_XPATH.PARSE_FROM_CLOB(LCXML => CXML);
+ /* Считываем корневой узел */
+ XROOT := PKG_XPATH.ROOT_NODE(RDOCUMENT => XDOC);
+ /* Считывание списка сущностей */
+ XNODES := PKG_XPATH.LIST_NODES(RPARENT_NODE => XROOT, SPATTERN => '/' || STAG_ENTS || '/' || STAG_ENT);
+ /* Цикл по списку сущностей */
+ for I in 1 .. PKG_XPATH.COUNT_NODES(RNODES => XNODES)
+ loop
+ /* Считаем элемент по его номеру */
+ XNODE := PKG_XPATH.ITEM_NODE(RNODES => XNODES, INUMBER => I);
+ /* Сериализуем и добавим его в коллекцию */
+ RRES.EXTEND();
+ RRES(RRES.LAST) := TENT_FROM_XML(CXML => PKG_XPATH.SERIALIZE_TO_CLOB(RNODE => XNODE));
+ end loop;
+ /* Освободим документ */
+ PKG_XPATH.FREE(RDOCUMENT => XDOC);
+ exception
+ when others then
+ /* Освободим документ */
+ PKG_XPATH.FREE(RDOCUMENT => XDOC);
+ /* Вернем ошибку */
+ PKG_STATE.DIAGNOSTICS_STACKED();
+ P_EXCEPTION(0, PKG_STATE.SQL_ERRM());
+ end;
+ end if;
+ /* Вернём результат */
+ return RRES;
+ end TENTS_FROM_XML;
+
+ /* Формирование идентификатора связи */
+ function TRL_ID_MAKE
+ (
+ SSOURCE in varchar2, -- Источник
+ STARGET in varchar2 -- Приёмник
+ ) return varchar2 -- Сформированный идентификатор
+ is
+ begin
+ /* Проверим параметры */
+ if (SSOURCE is null) then
+ P_EXCEPTION(0, 'Не указан источник связи.');
+ end if;
+ if (STARGET is null) then
+ P_EXCEPTION(0, 'Не указан приёмник связи.');
+ end if;
+ /* Соберем идентификатор */
+ return SSOURCE || '-' || STARGET;
+ end TRL_ID_MAKE;
+
+ /* Формирование описания связи */
+ function TRL_MAKE
+ (
+ SID in varchar2 := null, -- Идентификатор (null - автоформирование)
+ SSOURCE in varchar2, -- Источник
+ STARGET in varchar2 -- Приёмник
+ ) return TRL -- Описание связи
+ is
+ RRL TRL; -- Буфер для результата
+ begin
+ /* Собираем описание связи */
+ RRL.SID := COALESCE(SID, TRL_ID_MAKE(SSOURCE => SSOURCE, STARGET => STARGET));
+ RRL.SSOURCE := SSOURCE;
+ RRL.STARGET := STARGET;
+ /* Вернем полученное */
+ return RRL;
+ end TRL_MAKE;
+
+ /* Сериализация связи */
+ procedure TRL_TO_XML
+ (
+ RRL in TRL -- Связь
+ )
+ is
+ begin
+ /* Открываем описание связи */
+ PKG_XFAST.DOWN_NODE(SNAME => STAG_RL);
+ /* Связь */
+ PKG_XFAST.ATTR(SNAME => SATTR_ID, SVALUE => RRL.SID);
+ PKG_XFAST.ATTR(SNAME => SATTR_SOURCE, SVALUE => RRL.SSOURCE);
+ PKG_XFAST.ATTR(SNAME => SATTR_TARGET, SVALUE => RRL.STARGET);
+ /* Закрываем описание связи */
+ PKG_XFAST.UP();
+ end TRL_TO_XML;
+
+ /* Десериализация связи */
+ function TRL_FROM_XML
+ (
+ CXML in clob -- XML-описание связи
+ ) return TRL -- Связь
+ is
+ RRES TRL; -- Буфер для результата
+ XDOC PKG_XPATH.TDOCUMENT; -- Документ XML
+ XROOT PKG_XPATH.TNODE; -- Корень документа XML
+ XNODE PKG_XPATH.TNODE; -- Буфер узла документа
+ begin
+ /* Если данные есть */
+ if (CXML is not null) then
+ begin
+ /* Разбираем XML */
+ XDOC := PKG_XPATH.PARSE_FROM_CLOB(LCXML => CXML);
+ /* Считываем корневой узел */
+ XROOT := PKG_XPATH.ROOT_NODE(RDOCUMENT => XDOC);
+ /* Считаваем узел связи */
+ XNODE := PKG_XPATH.SINGLE_NODE(RPARENT_NODE => XROOT, SPATTERN => '/' || STAG_RL);
+ /* Получаем значения */
+ RRES.SID := PKG_XPATH.ATTRIBUTE(RNODE => XNODE, SNAME => SATTR_ID);
+ RRES.SSOURCE := PKG_XPATH.ATTRIBUTE(RNODE => XNODE, SNAME => SATTR_SOURCE);
+ RRES.STARGET := PKG_XPATH.ATTRIBUTE(RNODE => XNODE, SNAME => SATTR_TARGET);
+ /* Освободим документ */
+ PKG_XPATH.FREE(RDOCUMENT => XDOC);
+ exception
+ when others then
+ /* Освободим документ */
+ PKG_XPATH.FREE(RDOCUMENT => XDOC);
+ /* Вернем ошибку */
+ PKG_STATE.DIAGNOSTICS_STACKED();
+ P_EXCEPTION(0, PKG_STATE.SQL_ERRM());
+ end;
+ end if;
+ /* Вернём результат */
+ return RRES;
+ end TRL_FROM_XML;
+
+ /* Поиск индекса связи по идентификатору */
+ function TRLS_INDEX_BY_ID
+ (
+ RRLS in TRLS, -- Коллекция связей
+ SID in varchar2 -- Искомый идентификатор
+ ) return number -- Индекс найденной связи (null - если не найдено)
+ is
+ begin
+ /* Обходим коллекцию */
+ if ((RRLS is not null) and (RRLS.COUNT > 0)) then
+ for I in RRLS.FIRST .. RRLS.LAST
+ loop
+ begin
+ /* Возвращаем найденный индекс */
+ if (RRLS(I).SID = SID) then
+ return I;
+ end if;
+ exception
+ when NO_DATA_FOUND then
+ null;
+ end;
+ end loop;
+ end if;
+ /* Ничего не нашли */
+ return null;
+ end TRLS_INDEX_BY_ID;
+
+ /* Формирование коллекции связей по источнику/приёмнику */
+ function TRLS_LIST_BY_ST
+ (
+ RRLS in TRLS, -- Коллекция связей
+ SSOURCE_TARGET in varchar2, -- Идентификатор источника/приёмкника
+ NLIST_TYPE in number -- Тип формирования коллекции (0 - по источнику, 1 - по приёмнику
+ ) return TRLS -- Сформированная коллекция
+ is
+ RRES TRLS; -- Буфер для результата
+ begin
+ /* Инициализируем результат */
+ RRES := TRLS();
+ /* Обходим входную коллекцию */
+ if ((RRLS is not null) and (RRLS.COUNT > 0)) then
+ for I in RRLS.FIRST .. RRLS.LAST
+ loop
+ begin
+ /* Формируем выходную коллекцию */
+ if (((NLIST_TYPE = 0) and (RRLS(I).SSOURCE = SSOURCE_TARGET)) or
+ ((NLIST_TYPE = 1) and (RRLS(I).STARGET = SSOURCE_TARGET))) then
+ RRES.EXTEND();
+ RRES(RRES.LAST) := TRL_MAKE(SID => RRLS(I).SID, SSOURCE => RRLS(I).SSOURCE, STARGET => RRLS(I).STARGET);
+ end if;
+ exception
+ when NO_DATA_FOUND then
+ null;
+ end;
+ end loop;
+ end if;
+ /* Вернем результат */
+ return RRES;
+ end TRLS_LIST_BY_ST;
+
+ /* Добавление связи в коллекцию */
+ procedure TRLS_APPEND
+ (
+ RRLS in out nocopy TRLS, -- Изменяемая коллекция
+ SSOURCE in varchar2, -- Источник
+ STARGET in varchar2 -- Приёмник
+ )
+ is
+ RRL TRL; -- Добавляемая связь
+ begin
+ /* Инициализируем коллекцию если необходимо */
+ if (RRLS is null) then
+ RRLS := TRLS();
+ end if;
+ /* Формируем пописание связи */
+ RRL := TRL_MAKE(SSOURCE => SSOURCE, STARGET => STARGET);
+ /* Добавляем её в коллекцию */
+ RRLS.EXTEND();
+ RRLS(RRLS.LAST) := RRL;
+ end TRLS_APPEND;
+
+ /* Удаление связи из коллекции */
+ procedure TRLS_REMOVE
+ (
+ RRLS in out nocopy TRLS, -- Изменяемая коллекция
+ SID in varchar2 -- Идентификатор удялемой связи
+ )
+ is
+ NIND PKG_STD.TNUMBER; -- Индекс связи в коллекции
+ begin
+ NIND := TRLS_INDEX_BY_ID(RRLS => RRLS, SID => SID);
+ if (NIND is not null) then
+ RRLS.DELETE(NIND);
+ end if;
+ end TRLS_REMOVE;
+
+ /* Сериализация коллекции связей */
+ procedure TRLS_TO_XML
+ (
+ RRLS in TRLS -- Коллекция связей
+ )
+ is
+ begin
+ /* Открываем описание связей */
+ PKG_XFAST.DOWN_NODE(SNAME => STAG_RLS);
+ /* Обходим связи из коллекции */
+ if ((RRLS is not null) and (RRLS.COUNT > 0)) then
+ for I in RRLS.FIRST .. RRLS.LAST
+ loop
+ begin
+ /* Добавляем описание связи */
+ TRL_TO_XML(RRL => RRLS(I));
+ exception
+ when NO_DATA_FOUND then
+ null;
+ end;
+ end loop;
+ end if;
+ /* Закрываем описание связей */
+ PKG_XFAST.UP();
+ end TRLS_TO_XML;
+
+ /* Десериализация коллекции связей */
+ function TRLS_FROM_XML
+ (
+ CXML in clob -- XML-описание коллекции связей
+ ) return TRLS -- Коллекция связей
+ is
+ RRES TRLS; -- Буфер для результата
+ XDOC PKG_XPATH.TDOCUMENT; -- Документ XML
+ XROOT PKG_XPATH.TNODE; -- Корень документа XML
+ XNODE PKG_XPATH.TNODE; -- Буфер узла документа
+ XNODES PKG_XPATH.TNODES; -- Буфер коллекции узлов документа
+ begin
+ /* Инициализируем результат */
+ RRES := TRLS();
+ /* Если данные есть */
+ if (CXML is not null) then
+ begin
+ /* Разбираем XML */
+ XDOC := PKG_XPATH.PARSE_FROM_CLOB(LCXML => CXML);
+ /* Считываем корневой узел */
+ XROOT := PKG_XPATH.ROOT_NODE(RDOCUMENT => XDOC);
+ /* Считывание списка связей */
+ XNODES := PKG_XPATH.LIST_NODES(RPARENT_NODE => XROOT, SPATTERN => '/' || STAG_RLS || '/' || STAG_RL);
+ /* Цикл по списку связей */
+ for I in 1 .. PKG_XPATH.COUNT_NODES(RNODES => XNODES)
+ loop
+ /* Считаем элемент по его номеру */
+ XNODE := PKG_XPATH.ITEM_NODE(RNODES => XNODES, INUMBER => I);
+ /* Сериализуем и добавим его в коллекцию */
+ RRES.EXTEND();
+ RRES(RRES.LAST) := TRL_FROM_XML(CXML => PKG_XPATH.SERIALIZE_TO_CLOB(RNODE => XNODE));
+ end loop;
+ /* Освободим документ */
+ PKG_XPATH.FREE(RDOCUMENT => XDOC);
+ exception
+ when others then
+ /* Освободим документ */
+ PKG_XPATH.FREE(RDOCUMENT => XDOC);
+ /* Вернем ошибку */
+ PKG_STATE.DIAGNOSTICS_STACKED();
+ P_EXCEPTION(0, PKG_STATE.SQL_ERRM());
+ end;
+ end if;
+ /* Вернём результат */
+ return RRES;
+ end TRLS_FROM_XML;
+
+ /* Считывание записи запроса */
+ function QUERY_GET
+ (
+ NRN in number -- Рег. номер запроса
+ ) return P8PNL_QE_QUERY%rowtype -- Запись запроса
+ is
+ RRES P8PNL_QE_QUERY%rowtype; -- Буфер для результата
+ begin
+ select T.* into RRES from P8PNL_QE_QUERY T where T.RN = NRN;
+ return RRES;
+ exception
+ when NO_DATA_FOUND then
+ PKG_MSG.RECORD_NOT_FOUND(NFLAG_SMART => 0, NDOCUMENT => NRN, SUNIT_TABLE => 'P8PNL_QE_QUERY');
+ end QUERY_GET;
+
+ /* Получение признака возможности изменения запроса */
+ function QUERY_ACCESS_SIGN_MODIFY
+ (
+ NRN in number, -- Рег. номер запроса
+ SUSER in varchar2 -- Имя пользователя
+ ) return number -- Признак возможности изменения запроса (0 - нет, 1 - да)
+ is
+ RQ P8PNL_QE_QUERY%rowtype; -- Проверяемая запись запроса
+ begin
+ /* Читаем запрос */
+ RQ := QUERY_GET(NRN => NRN);
+ /* Менять можно только свой запрос */
+ if (RQ.AUTHOR = SUSER) then
+ return 1;
+ end if;
+ /* Проверки не пройдены - менять нельзя */
+ return 0;
+ end QUERY_ACCESS_SIGN_MODIFY;
+
+ /* Проверка возможности изменения запроса */
+ procedure QUERY_ACCESS_MODIFY
+ (
+ NRN in number, -- Рег. номер запроса
+ SUSER in varchar2 -- Имя пользователя
+ )
+ is
+ begin
+ /* Получим признак возможности измнения */
+ if (QUERY_ACCESS_SIGN_MODIFY(NRN => NRN, SUSER => SUSER) = 0) then
+ /* Менять нельзя */
+ P_EXCEPTION(0, 'У Вас нет прав доступа для измнения запроса.');
+ end if;
+ end QUERY_ACCESS_MODIFY;
+
+ /* Получение признака возможности просмотра запроса */
+ function QUERY_ACCESS_SIGN_VIEW
+ (
+ NRN in number, -- Рег. номер запроса
+ SUSER in varchar2 -- Имя пользователя
+ ) return number -- Признак возможности просмотра запроса (0 - нет, 1 - да)
+ is
+ RQ P8PNL_QE_QUERY%rowtype; -- Проверяемая запись запроса
+ begin
+ /* Читаем запрос */
+ RQ := QUERY_GET(NRN => NRN);
+ /* Смотреть можно только свой или публичный запрос */
+ if ((RQ.PBL = 1) or (RQ.AUTHOR = SUSER)) then
+ return 1;
+ end if;
+ /* Проверки не пройдены - нельзя смотреть */
+ return 0;
+ end QUERY_ACCESS_SIGN_VIEW;
+
+ /* Проверка возможности просмотра запроса */
+ procedure QUERY_ACCESS_VIEW
+ (
+ NRN in number, -- Рег. номер запроса
+ SUSER in varchar2 -- Имя пользователя
+ )
+ is
+ begin
+ /* Получим признак возможности просмотра */
+ if (QUERY_ACCESS_SIGN_VIEW(NRN => NRN, SUSER => SUSER) = 0) then
+ /* Смотреть нельзя */
+ P_EXCEPTION(0, 'У Вас нет прав доступа для просмотра запроса.');
+ end if;
+ end QUERY_ACCESS_VIEW;
+
+ /* Проверка атрибутов запроса */
+ procedure QUERY_CHECK
+ (
+ SCODE in varchar2, -- Мнемокод
+ SNAME in varchar2 -- Наименование
+ )
+ is
+ begin
+ /* Мнемокод должен быть задан */
+ if (SCODE is null) then
+ P_EXCEPTION(0, 'Не задан мнемокод запроса.');
+ end if;
+ /* Наименование должно быть задано */
+ if (SNAME is null) then
+ P_EXCEPTION(0, 'Не задано наименование запроса.');
+ end if;
+ end QUERY_CHECK;
+
+ /* Синхронизация даты изменения запроса с текущим временем */
+ procedure QUERY_CH_DATE_SYNC
+ (
+ NRN in number -- Рег. номер запроса
+ )
+ is
+ begin
+ /* Установим текущую дату изменения */
+ update P8PNL_QE_QUERY T set T.CH_DATE = sysdate where T.RN = NRN;
+ /* Контроль изменения данных */
+ if (sql%notfound) then
+ PKG_MSG.RECORD_NOT_FOUND(NDOCUMENT => NRN, SUNIT_TABLE => 'P8PNL_QE_QUERY');
+ end if;
+ end QUERY_CH_DATE_SYNC;
+
+ /* Добавление запроса */
+ procedure QUERY_INSERT
+ (
+ SCODE in varchar2, -- Мнемокод
+ SNAME in varchar2, -- Наименование
+ NRN out number -- Рег. номер добавленного запроса
+ )
+ is
+ begin
+ /* Проверим параметры */
+ QUERY_CHECK(SCODE => SCODE, SNAME => SNAME);
+ /* Формируем рег. номер */
+ NRN := GEN_ID();
+ /* Добавляем данные */
+ insert into P8PNL_QE_QUERY
+ (RN, CODE, name, AUTHOR, CH_DATE, READY, PBL)
+ values
+ (NRN, SCODE, SNAME, UTILIZER(), sysdate, 0, 0);
+ end QUERY_INSERT;
+
+ /* Исправление запроса */
+ procedure QUERY_UPDATE
+ (
+ NRN in number, -- Рег. номер запроса
+ SCODE in varchar2, -- Мнемокод
+ SNAME in varchar2 -- Наименование
+ )
+ is
+ begin
+ /* Проверим параметры */
+ QUERY_CHECK(SCODE => SCODE, SNAME => SNAME);
+ /* Изменяем данные */
+ update P8PNL_QE_QUERY T
+ set T.CODE = SCODE,
+ T.NAME = SNAME
+ where T.RN = NRN;
+ /* Контроль изменения данных */
+ if (sql%notfound) then
+ PKG_MSG.RECORD_NOT_FOUND(NDOCUMENT => NRN, SUNIT_TABLE => 'P8PNL_QE_QUERY');
+ end if;
+ /* Обновим дату изменения запроса */
+ QUERY_CH_DATE_SYNC(NRN => NRN);
+ end QUERY_UPDATE;
+
+ /* Удаление запроса */
+ procedure QUERY_DELETE
+ (
+ NRN in number -- Рег. номер запроса
+ )
+ is
+ begin
+ /* Удаляем запись */
+ delete from P8PNL_QE_QUERY T where T.RN = NRN;
+ /* Контроль изменения данных */
+ if (sql%notfound) then
+ PKG_MSG.RECORD_NOT_FOUND(NDOCUMENT => NRN, SUNIT_TABLE => 'P8PNL_QE_QUERY');
+ end if;
+ end QUERY_DELETE;
+
+ /* Сериализация сущностей запроса */
+ function QUERY_ENTS_TO_XML
+ (
+ RENTS in TENTS -- Коллекция сущностей
+ ) return clob -- XML-описание
+ is
+ CRES clob; -- Буфер для результата
+ begin
+ /* Если сущности есть */
+ if ((RENTS is not null) and (RENTS.COUNT > 0)) then
+ /* Начинаем формирование XML */
+ PKG_XFAST.PROLOGUE(ITYPE => PKG_XFAST.CONTENT_);
+ /* Добавляем сущности */
+ TENTS_TO_XML(RENTS => RENTS);
+ /* Сериализуем */
+ CRES := PKG_XFAST.SERIALIZE_TO_CLOB();
+ /* Завершаем формирование XML */
+ PKG_XFAST.EPILOGUE();
+ else
+ CRES := null;
+ end if;
+ /* Возвращаем полученное */
+ return CRES;
+ exception
+ when others then
+ /* Завершаем формирование XML */
+ PKG_XFAST.EPILOGUE();
+ /* Вернем ошибку */
+ PKG_STATE.DIAGNOSTICS_STACKED();
+ P_EXCEPTION(0, PKG_STATE.SQL_ERRM());
+ end QUERY_ENTS_TO_XML;
+
+ /* Десериализация сущностей запроса */
+ function QUERY_ENTS_FROM_XML
+ (
+ CXML in clob -- XML-описание коллекции сущностей запроса
+ ) return TENTS -- Коллекция сущностей
+ is
+ RENTS TENTS; -- Буфер для результата
+ XDOC PKG_XPATH.TDOCUMENT; -- Документ XML
+ XROOT PKG_XPATH.TNODE; -- Корень документа XML
+ XNODE PKG_XPATH.TNODE; -- Буфер узла документа
+ begin
+ /* Инициализируем результат */
+ RENTS := TENTS();
+ /* Если данные есть */
+ if (CXML is not null) then
+ begin
+ /* Разбираем XML */
+ XDOC := PKG_XPATH.PARSE_FROM_CLOB(LCXML => CXML);
+ /* Считываем корневой узел */
+ XROOT := PKG_XPATH.ROOT_NODE(RDOCUMENT => XDOC);
+ /* Считываем узел сущностей */
+ XNODE := PKG_XPATH.SINGLE_NODE(RPARENT_NODE => XROOT, SPATTERN => '/' || STAG_ENTS);
+ /* Десериализуем его */
+ RENTS := TENTS_FROM_XML(CXML => PKG_XPATH.SERIALIZE_TO_CLOB(RNODE => XNODE));
+ /* Освободим документ */
+ PKG_XPATH.FREE(RDOCUMENT => XDOC);
+ exception
+ when others then
+ /* Освободим документ */
+ PKG_XPATH.FREE(RDOCUMENT => XDOC);
+ /* Вернем ошибку */
+ PKG_STATE.DIAGNOSTICS_STACKED();
+ P_EXCEPTION(0, PKG_STATE.SQL_ERRM());
+ end;
+ end if;
+ /* Вернём сформированное */
+ return RENTS;
+ end QUERY_ENTS_FROM_XML;
+
+ /* Сериализация связей запроса */
+ function QUERY_RLS_TO_XML
+ (
+ RRLS in TRLS -- Коллекция связей
+ ) return clob -- XML-описание
+ is
+ CRES clob; -- Буфер для результата
+ begin
+ /* Если связи есть */
+ if ((RRLS is not null) and (RRLS.COUNT > 0)) then
+ /* Начинаем формирование XML */
+ PKG_XFAST.PROLOGUE(ITYPE => PKG_XFAST.CONTENT_);
+ /* Добавляем связи */
+ TRLS_TO_XML(RRLS => RRLS);
+ /* Сериализуем */
+ CRES := PKG_XFAST.SERIALIZE_TO_CLOB();
+ /* Завершаем формирование XML */
+ PKG_XFAST.EPILOGUE();
+ else
+ CRES := null;
+ end if;
+ /* Возвращаем полученное */
+ return CRES;
+ exception
+ when others then
+ /* Завершаем формирование XML */
+ PKG_XFAST.EPILOGUE();
+ /* Вернем ошибку */
+ PKG_STATE.DIAGNOSTICS_STACKED();
+ P_EXCEPTION(0, PKG_STATE.SQL_ERRM());
+ end QUERY_RLS_TO_XML;
+
+ /* Десериализация связей запроса */
+ function QUERY_RLS_FROM_XML
+ (
+ CXML in clob -- XML-описание коллекции связей запроса
+ ) return TRLS -- Коллекция связей
+ is
+ RRLS TRLS; -- Буфер для результата
+ XDOC PKG_XPATH.TDOCUMENT; -- Документ XML
+ XROOT PKG_XPATH.TNODE; -- Корень документа XML
+ XNODE PKG_XPATH.TNODE; -- Буфер узла документа
+ begin
+ /* Инициализируем результат */
+ RRLS := TRLS();
+ /* Если данные есть */
+ if (CXML is not null) then
+ begin
+ /* Разбираем XML */
+ XDOC := PKG_XPATH.PARSE_FROM_CLOB(LCXML => CXML);
+ /* Считываем корневой узел */
+ XROOT := PKG_XPATH.ROOT_NODE(RDOCUMENT => XDOC);
+ /* Считываем узел связей */
+ XNODE := PKG_XPATH.SINGLE_NODE(RPARENT_NODE => XROOT, SPATTERN => '/' || STAG_RLS);
+ /* Десериализуем его */
+ RRLS := TRLS_FROM_XML(CXML => PKG_XPATH.SERIALIZE_TO_CLOB(RNODE => XNODE));
+ /* Освободим документ */
+ PKG_XPATH.FREE(RDOCUMENT => XDOC);
+ exception
+ when others then
+ /* Освободим документ */
+ PKG_XPATH.FREE(RDOCUMENT => XDOC);
+ /* Вернем ошибку */
+ PKG_STATE.DIAGNOSTICS_STACKED();
+ P_EXCEPTION(0, PKG_STATE.SQL_ERRM());
+ end;
+ end if;
+ /* Вернём сформированное */
+ return RRLS;
+ end QUERY_RLS_FROM_XML;
+
+ /* Сериализация запроса */
+ function QUERY_TO_XML
+ (
+ NRN in number -- Рег. номер запроса
+ ) return clob -- XML-описание
+ is
+ RQ P8PNL_QE_QUERY%rowtype; -- Запись запроса
+ CRES clob; -- Буфер для сериализации
+ begin
+ /* Читаем запрос */
+ RQ := QUERY_GET(NRN => NRN);
+ /* Начинаем формирование XML */
+ PKG_XFAST.PROLOGUE(ITYPE => PKG_XFAST.CONTENT_, BALINE => true, BINDENT => true);
+ /* Открываем корень */
+ PKG_XFAST.DOWN_NODE(SNAME => STAG_DATA);
+ /* Параметры */
+ if (RQ.OPTS is not null) then
+ null;
+ end if;
+ /* Сущности (можно использовать просто PKG_XFAST.VALUE_XML и сразу отдать готовый XML из RQ.ENTS, но это приводит к формированию некорректного документа с лишней ">" между группами) */
+ if (RQ.ENTS is not null) then
+ TENTS_TO_XML(RENTS => QUERY_ENTS_FROM_XML(CXML => RQ.ENTS));
+ end if;
+ /* Связи (можно использовать просто PKG_XFAST.VALUE_XML и сразу отдать готовый XML из RQ.RLS, но это приводит к формированию некорректного документа с лишней ">" между группами) */
+ if (RQ.RLS is not null) then
+ TRLS_TO_XML(RRLS => QUERY_RLS_FROM_XML(CXML => RQ.RLS));
+ end if;
+ /* Закрываем корень */
+ PKG_XFAST.UP();
+ /* Сериализуем */
+ CRES := PKG_XFAST.SERIALIZE_TO_CLOB();
+ /* Завершаем формирование XML */
+ PKG_XFAST.EPILOGUE();
+ /* Возвращаем результат */
+ return CRES;
+ exception
+ when others then
+ /* Завершаем формирование XML */
+ PKG_XFAST.EPILOGUE();
+ /* Вернем ошибку */
+ PKG_STATE.DIAGNOSTICS_STACKED();
+ P_EXCEPTION(0, PKG_STATE.SQL_ERRM());
+ end QUERY_TO_XML;
+
+ /* Формирование списка запросов */
+ function QUERY_LIST_GET
+ (
+ SUSER in varchar2 -- Имя пользователя
+ ) return clob -- Список запросов
+ is
+ CRES clob; -- Буфер для сериализации
+ begin
+ /* Начинаем формирование XML */
+ PKG_XFAST.PROLOGUE(ITYPE => PKG_XFAST.CONTENT_, BALINE => true, BINDENT => true);
+ /* Открываем корень */
+ PKG_XFAST.DOWN_NODE(SNAME => STAG_DATA);
+ /* Открываем список запросов */
+ PKG_XFAST.DOWN_NODE(SNAME => STAG_QUERIES);
+ /* Обходим запросы - данного пользователя и публичные */
+ for C in (select T.RN NRN,
+ T.CODE SCODE,
+ T.NAME SNAME,
+ UL.NAME SAUTHOR,
+ TO_CHAR(T.CH_DATE, 'dd.mm.yyyy hh24:mi:ss') SCH_DATE,
+ T.READY NREADY,
+ T.PBL NPBL,
+ QUERY_ACCESS_SIGN_MODIFY(T.RN, SUSER) NMODIFY
+ from P8PNL_QE_QUERY T,
+ USERLIST UL
+ where T.AUTHOR = UL.AUTHID
+ and QUERY_ACCESS_SIGN_VIEW(T.RN, SUSER) = 1
+ order by T.CODE)
+ loop
+ /* Открываем описание запроса */
+ PKG_XFAST.DOWN_NODE(SNAME => STAG_QUERY);
+ /* Запрос */
+ PKG_XFAST.ATTR(SNAME => SATTR_RN, NVALUE => C.NRN);
+ PKG_XFAST.ATTR(SNAME => SATTR_CODE, SVALUE => C.SCODE);
+ PKG_XFAST.ATTR(SNAME => SATTR_NAME, SVALUE => C.SNAME);
+ PKG_XFAST.ATTR(SNAME => SATTR_AUTHOR, SVALUE => C.SAUTHOR);
+ PKG_XFAST.ATTR(SNAME => SATTR_CH_DATE, SVALUE => C.SCH_DATE);
+ PKG_XFAST.ATTR(SNAME => SATTR_READY, NVALUE => C.NREADY);
+ PKG_XFAST.ATTR(SNAME => SATTR_PBL, NVALUE => C.NPBL);
+ PKG_XFAST.ATTR(SNAME => SATTR_MODIFY, NVALUE => C.NMODIFY);
+ /* Закрываем описание запроса */
+ PKG_XFAST.UP();
+ end loop;
+ /* Закрываем список запросов */
+ PKG_XFAST.UP();
+ /* Закрываем корень */
+ PKG_XFAST.UP();
+ /* Сериализуем */
+ CRES := PKG_XFAST.SERIALIZE_TO_CLOB();
+ /* Завершаем формирование XML */
+ PKG_XFAST.EPILOGUE();
+ /* Возвращаем результат */
+ return CRES;
+ exception
+ when others then
+ /* Завершаем формирование XML */
+ PKG_XFAST.EPILOGUE();
+ /* Вернем ошибку */
+ PKG_STATE.DIAGNOSTICS_STACKED();
+ P_EXCEPTION(0, PKG_STATE.SQL_ERRM());
+ end QUERY_LIST_GET;
+
+ /* Получение описания запроса */
+ function QUERY_DESC_GET
+ (
+ NRN in number -- Рег. номер запроса
+ ) return clob -- XML-описание
+ is
+ begin
+ /* Сериализуем запрос */
+ return QUERY_TO_XML(NRN => NRN);
+ end QUERY_DESC_GET;
+
+ /* Чтение сущностей запроса */
+ function QUERY_ENTS_GET
+ (
+ CENTS in clob -- Сериализованное описание сущностей
+ ) return TENTS -- Коллекция сущностей
+ is
+ begin
+ /* Десериализуем */
+ return QUERY_ENTS_FROM_XML(CXML => CENTS);
+ end QUERY_ENTS_GET;
+
+ /* Запись сущностей запроса */
+ procedure QUERY_ENTS_SET
+ (
+ NRN in number, -- Рег. номер запроса
+ RENTS in TENTS -- Коллекция сущностей
+ )
+ is
+ CENTS clob; -- Буфер для сериализации
+ begin
+ /* Сериализуем полученную коллекцию сущностей */
+ CENTS := QUERY_ENTS_TO_XML(RENTS => RENTS);
+ /* Сохраним её */
+ update P8PNL_QE_QUERY T set T.ENTS = CENTS where T.RN = NRN;
+ /* Контроль изменения данных */
+ if (sql%notfound) then
+ PKG_MSG.RECORD_NOT_FOUND(NDOCUMENT => NRN, SUNIT_TABLE => 'P8PNL_QE_QUERY');
+ end if;
+ /* Обновим дату изменения запроса */
+ QUERY_CH_DATE_SYNC(NRN => NRN);
+ end QUERY_ENTS_SET;
+
+ /* Чтение связей запроса */
+ function QUERY_RLS_GET
+ (
+ CRLS in clob -- Сериализованное описание связей
+ ) return TRLS -- Коллекция связей
+ is
+ begin
+ /* Десериализуем */
+ return QUERY_RLS_FROM_XML(CXML => CRLS);
+ end QUERY_RLS_GET;
+
+ /* Запись связей запроса */
+ procedure QUERY_RLS_SET
+ (
+ NRN in number, -- Рег. номер запроса
+ RRLS in TRLS -- Коллекция связей
+ )
+ is
+ CRLS clob; -- Буфер для сериализации
+ begin
+ /* Сериализуем полученную коллекцию связей */
+ CRLS := QUERY_RLS_TO_XML(RRLS => RRLS);
+ /* Сохраним её */
+ update P8PNL_QE_QUERY T set T.RLS = CRLS where T.RN = NRN;
+ /* Контроль изменения данных */
+ if (sql%notfound) then
+ PKG_MSG.RECORD_NOT_FOUND(NDOCUMENT => NRN, SUNIT_TABLE => 'P8PNL_QE_QUERY');
+ end if;
+ /* Обновим дату изменения запроса */
+ QUERY_CH_DATE_SYNC(NRN => NRN);
+ end QUERY_RLS_SET;
+
+ /* Установка признака "готовности" запроса */
+ procedure QUERY_READY_SET
+ (
+ NRN in number, -- Рег. номер запроса
+ NREADY in number -- Флаг готовности к использованию (0 - нет, 1 - да)
+ )
+ is
+ begin
+ /* Проверим параметры */
+ if (NREADY is null) then
+ P_EXCEPTION(0,
+ 'Не задано значение признака готовности запроса к использованию.');
+ end if;
+ if (NREADY not in (0, 1)) then
+ P_EXCEPTION(0,
+ 'Значение признака готовности запроса к использованию задано некорректно (ожидалось 0 или 1).');
+ end if;
+ /* Установим флаг готовности к использованию */
+ update P8PNL_QE_QUERY T set T.READY = NREADY where T.RN = NRN;
+ /* Контроль изменения данных */
+ if (sql%notfound) then
+ PKG_MSG.RECORD_NOT_FOUND(NDOCUMENT => NRN, SUNIT_TABLE => 'P8PNL_QE_QUERY');
+ end if;
+ /* Обновим дату изменения запроса */
+ QUERY_CH_DATE_SYNC(NRN => NRN);
+ end QUERY_READY_SET;
+
+ /* Установка признака "публичности" запроса */
+ procedure QUERY_PBL_SET
+ (
+ NRN in number, -- Рег. номер запроса
+ NPBL in number -- Флаг публичности (0 - приватный, 1 - публичный)
+ )
+ is
+ begin
+ /* Проверим параметры */
+ if (NPBL is null) then
+ P_EXCEPTION(0, 'Не задано значение признака публичности запроса.');
+ end if;
+ if (NPBL not in (0, 1)) then
+ P_EXCEPTION(0,
+ 'Значение признака публичноти запроса задано некорректно (ожидалось 0 или 1).');
+ end if;
+ /* Установим флаг публичности */
+ update P8PNL_QE_QUERY T set T.PBL = NPBL where T.RN = NRN;
+ /* Контроль изменения данных */
+ if (sql%notfound) then
+ PKG_MSG.RECORD_NOT_FOUND(NDOCUMENT => NRN, SUNIT_TABLE => 'P8PNL_QE_QUERY');
+ end if;
+ /* Обновим дату изменения запроса */
+ QUERY_CH_DATE_SYNC(NRN => NRN);
+ end QUERY_PBL_SET;
+
+end PKG_P8PANELS_QE_BASE;
+/
diff --git a/db/grants.sql b/db/grants.sql
index c12f082..34d7065 100644
--- a/db/grants.sql
+++ b/db/grants.sql
@@ -2,4 +2,6 @@ grant execute on PKG_P8PANELS to public;
grant execute on PKG_P8PANELS_PROJECTS to public;
grant execute on PKG_P8PANELS_SAMPLES to public;
grant execute on PKG_P8PANELS_EQUIPSRV to public;
-grant execute on PKG_P8PANELS_RRPCONFED to public;
\ No newline at end of file
+grant execute on PKG_P8PANELS_RRPCONFED to public;
+grant execute on PKG_P8PANELS_PE to public;
+grant execute on PKG_P8PANELS_QE to public;