From 938db35c8575f290f3b4cb876228a6ab1aac8702 Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Sun, 6 Sep 2026 15:43:57 +0300 Subject: [PATCH] =?UTF-8?q?docs(meta-dsl-spec):=20=D0=BF=D0=BE=D0=BB=D0=BD?= =?UTF-8?q?=D0=BE=D0=B5=20=D0=BE=D0=BF=D0=B8=D1=81=D0=B0=D0=BD=D0=B8=D0=B5?= =?UTF-8?q?=20DSL=20=D0=B2=D0=BD=D0=B5=D1=88=D0=BD=D0=B8=D1=85=20=D0=B8?= =?UTF-8?q?=D1=81=D1=82=D0=BE=D1=87=D0=BD=D0=B8=D0=BA=D0=BE=D0=B2=20=D0=B4?= =?UTF-8?q?=D0=B0=D0=BD=D0=BD=D1=8B=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Раздел 18 был указателем на reference навыка — это ломало принятое разделение: спецификация в docs описывает DSL полно (для нас), инструкция навыка — только как применять (для модели). Указатель экономил дублирование ценой того, что полного описания не оказалось нигде. Теперь §18 описывает источник, таблицы, поля и функции целиком: ключи, XML-теги, умолчания, конвенции ссылок на поля, presence-aware inputByString, различение BinaryData и ValueStorage, запрет составного типа, и границы — почему в DSL нет значения незаполненного родителя (платформа сбрасывает его при любой загрузке XML) и почему нет кубов OLAP. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01NBsZA5cr2WFThtgp7i5WVi --- docs/meta-dsl-spec.md | 177 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 158 insertions(+), 19 deletions(-) diff --git a/docs/meta-dsl-spec.md b/docs/meta-dsl-spec.md index 59b43431d..f58100301 100644 --- a/docs/meta-dsl-spec.md +++ b/docs/meta-dsl-spec.md @@ -1961,30 +1961,169 @@ ChildObjects и модулей. --- -## 18. Внешние источники данных (tables / functions) +## 18. Внешние источники данных (ExternalDataSource) -Только для `ExternalDataSource`. Полное описание — `.claude/skills/meta-compile/reference/external-data-source.md`: -таблицы, поля и функции, их свойства и умолчания. Здесь — только форма и связи с общими конвенциями, -чтобы таблица свойств не жила в двух местах и не разъезжалась. +Источник описывается одним JSON целиком: сам источник, его таблицы с полями и функции. Компилятор +пишет `ExternalDataSources/<Имя>.xml` плюс по файлу на таблицу в `<Имя>/Tables/` — единственный вид, +у которого объект складывается более чем из одного XML. + +Параметров соединения в конфигурации нет: строка соединения, пользователь, пароль и тип СУБД +задаются в режиме «Предприятие» (стандартная функция «Управление внешними источниками данных») +и хранятся в информационной базе. ```json -{ "type": "ExternalDataSource", "name": "PG", +{ + "type": "ExternalDataSource", + "name": "PG", + "dataLockControlMode": "AutomaticAndManaged", "tables": { - "prices": ["product_id: Number(10,0)", "price: Number(15,2)"], - "products": { "keyFields": ["id"], "presentationField": "name", - "fields": ["id: Number(10,0)", "name: String(150)"] } + "prices": ["product_id: Number(10,0)", "period: Date", "price: Number(15,2)"], + "products": { + "nameInDataSource": "eds.public.products", + "tableDataType": "ObjectData", + "keyFields": ["id"], + "presentationField": "name", + "parentField": "parent_id", + "fields": [ + "id: Number(10,0)", + "name: String(150)", + "article: String(50) | nullable", + "parent_id: ExternalDataSourceTableRef.PG.products | nullable" + ] + } }, - "functions": { "total": { "expression": "public.f_total(&1, &2)", "returns": "Number(15,2)" } } } + "functions": { + "total": { "expression": "public.f_total(&1, &2)", "returns": "Number(15,2)" }, + "nextKey": "NEXT VALUE FOR public.seq_key" + } +} ``` -- **`tables`** — dict имя → массив полей ЛИБО объект со свойствами и ключом `fields`: та же двойственность, - что у `tabularSections` (§5). -- **Поле** — обычный реквизит (§4) плюс три своих ключа: `nameInDataSource`, `readOnly`, `allowNull` - (флаги строковой формы — `readonly`, `nullable`). Составной тип у поля платформа запрещает. -- **Ссылки на поля** (`keyFields`, `presentationField`, `parentField`, `dataVersionField`, `inputByString`, - `dataLockFields`) — короткими именами полей этой же таблицы, как `inputByString` у справочника (§7.1.5). -- **`functions`** — dict имя → строка (выражение) ЛИБО объект, как `urlTemplates` (§16) и `operations` (§17). - Параметры не объекты: они записаны в самом выражении как `&1`, `&2`. +### 18.1 Свойства источника -Кубы OLAP не поддерживаются. Таблица — отдельный файл, поэтому `meta-compile` описывает источник целиком, -а дописать таблицу или функцию в существующий умеет `meta-edit` (`add.tables` / `add.functions`). +| Ключ | Умолчание | Значения | +|------|-----------|----------| +| `synonym` / `comment` | авто из имени / пусто | как у прочих объектов (§2, §4.4) | +| `dataLockControlMode` | `Automatic` | `Automatic` / `Managed` / `AutomaticAndManaged` | +| `tables` | `{}` | таблицы (§18.2) | +| `functions` | `{}` | функции (§18.4) | + +`AutomaticAndManaged` — значение, которого нет у прочих объектов: оно отдаёт решение о режиме +блокировок каждой таблице. При конкретном значении (`Automatic`/`Managed`) одноимённое свойство +таблицы платформа игнорирует. + +### 18.2 Таблицы + +Ключ — имя таблицы в конфигурации. Значение — **массив полей** либо **объект** со свойствами и +ключом `fields`: та же двойственность, что у `tabularSections` (§5). + +| Ключ | XML | Умолчание | Значения | +|------|-----|-----------|----------| +| `synonym` / `comment` | Synonym / Comment | авто из имени / пусто | ML (§4.4) | +| `tableType` | TableType | `Table` | `Table` — реальная таблица или представление; `Expression` — табличная функция/выражение | +| `nameInDataSource` | NameInDataSource | = имя таблицы | имя физической таблицы; платформа пишет три части `<база>.<схема>.<таблица>`. У `Expression` — пусто | +| `expressionInDataSource` | ExpressionInDataSource | пусто | выражение для `Expression`, напр. `public.f_by_parent(&1)`; имя базы в нём НЕ указывается | +| `tableDataType` | TableDataType | `NonobjectData` | `ObjectData` — запись определяется одним полем (аналог справочника); `NonobjectData` — составным ключом (аналог регистра сведений) | +| `keyFields` | KeyFields | `[]` | имена ключевых полей | +| `presentationField` | PresentationField | пусто | имя поля представления (осмысленно только у `ObjectData`) | +| `parentField` | ParentField | пусто | имя поля родителя; его тип обязан быть ссылкой на эту же таблицу | +| `inputByString` | InputByString | = `[presentationField]` | имена полей ввода по строке | +| `dataVersionField` | DataVersionField | пусто | имя поля, растущего при каждой записи (ловит конкурентное изменение) | +| `dataLockFields` | DataLockFields | `[]` | имена полей управляемой блокировки | +| `readOnly` | ReadOnly | `false` | запрет записи | +| `transactionsIsolationLevel` | TransactionsIsolationLevel | `Auto` | `Auto` / `ReadUncommitted` / `ReadCommitted` / `RepeatableRead` / `Serializable` | +| `dataLockControlMode` | DataLockControlMode | `Automatic` | `Automatic` / `Managed` / `AutomaticAndManaged` | +| `basedOn` | BasedOn | `[]` | ввод на основании, `MDObjectRef` verbatim (`Catalog.Контрагенты`) | +| `useStandardCommands` | UseStandardCommands | `true` | bool | +| `quickChoice` | QuickChoice | `false` | bool | +| `editType` | EditType | `InDialog` | `InDialog` / `InList` | +| `fields` | ChildObjects/Field | `[]` | поля (§18.3); синоним ключа — `columns` | + +Прочие свойства — представления (`objectPresentation`, `extendedObjectPresentation`, +`recordPresentation`, `extendedRecordPresentation`, `listPresentation`, `extendedListPresentation`), +формы по умолчанию (`defaultObjectForm`, `defaultRecordForm`, `defaultListForm`, `defaultChoiceForm`), +`characteristics`, `explanation`, `includeHelpInContents`, `createOnInput`, +`searchStringModeOnInputByString`, `choiceDataGetModeOnInputByString`, `choiceHistoryOnInput` — +общий слой, как у справочника. + +**Ссылки на поля** (`keyFields`, `presentationField`, `parentField`, `dataVersionField`, +`inputByString`, `dataLockFields`) задаются короткими именами полей этой же таблицы — конвенция §7.1.5. +Компилятор разворачивает их в полный путь `ExternalDataSource.<Источник>.Table.<Таблица>.Field.<Поле>`, +декомпилятор сворачивает обратно. Это единственные **шестичастные** ссылки в метаданных, поэтому +разбор с конца (как в ролях) на них не работает. + +**`inputByString` — presence-aware.** Ключа нет → компилятор выводит список из `presentationField` +(так делает платформа при загрузке). Явный список, в том числе пустой `[]`, уважается как есть. + +**Значение незаполненного родителя в DSL отсутствует намеренно.** Платформа сбрасывает его в пустое +при любой загрузке XML — включая загрузку собственной выгрузки (измерено на 8.3.24.1691 тремя +независимыми прогонами: значением `0` от Конфигуратора, `5` из XML и `1`, выставленным в Конфигураторе +осознанно). Задать его можно только интерактивно, поэтому компилятор эмитит форму, которая получается +после загрузки: `xsi:type="xs:string"` пусто при заданном `parentField`, иначе `xsi:nil="true"`. + +### 18.3 Поля таблицы + +Строковая и объектная форма — те же, что у реквизитов (§4). Своих ключа три: + +| Ключ | XML | Умолчание | Значения | +|------|-----|-----------|----------| +| `nameInDataSource` | NameInDataSource | = имя поля | имя колонки. Значение в одинарных кавычках уходит в SQL как есть; без кавычек экранируется двойными при спецсимволах | +| `readOnly` | ReadOnly | `false` | поле не записывается (вычисляемые, автоинкрементные, ключевые) | +| `allowNull` | AllowNull | `false` | допускает `NULL`; в форме показывается как «Не заполнено» | + +Флаги строковой формы: `readonly`, `nullable`. + +```json +"fields": [ + "id: Number(10,0) | readonly", + { "name": "article", "type": "String(50)", "nameInDataSource": "art_code", "allowNull": true } +] +``` + +Допустимые типы поля: `Number`, `String`, `Date`, `Boolean`, `UUID`, `BinaryData` (и `BinaryData(N)` +с ограничением длины) и ссылка на таблицу внешнего источника — +`ExternalDataSourceTableRef.<Источник>.<Таблица>`. + +`BinaryData` (ДвоичныеДанные) — **не** `ValueStorage`: платформа пишет его как `xs:base64Binary` +с `BinaryDataQualifiers`, тогда как ХранилищеЗначения — как `v8:ValueStorage`. Декомпилятор +различает их по наличию квалификаторов. + +**Составной тип у поля платформа запрещает** — при загрузке отвечает «Поле не может иметь составной +тип», хотя «Руководство разработчика» (17.5.2.3.4) его разрешает. Документация опережает реализацию. + +Набор свойств поля — канонический блок реквизита (§4) без `ChoiceFoldersAndItems`, `LinkByType`, +`Indexing`, `Use`, `FullTextSearch`, `DataHistory`: индексами и полнотекстовым поиском чужой таблицы +1С не владеет. `ChoiceForm` у поля стоит ПОСЛЕ `ChoiceHistoryOnInput`, а не перед — порядок снят +с выгрузки платформы. + +### 18.4 Функции + +Ключ — имя функции. Значение — строка (интерпретируется как `expression`) либо объект: как +`urlTemplates` (§16) и `operations` (§17). + +| Ключ | XML | Умолчание | Значения | +|------|-----|-----------|----------| +| `expression` | ExpressionInDataSource | — | выражение в источнике, обязательный | +| `returns` | Type | `String` | тип возвращаемого значения | +| `returnValue` | ReturnValue | `true` | `false` — процедура, тип не пишется | +| `synonym` / `comment` | Synonym / Comment | авто из имени / пусто | ML (§4.4) | + +Функция лежит полным узлом внутри файла источника, отдельного файла у неё нет. + +**Параметры функции не являются объектами метаданных.** Формальные параметры записываются прямо +в выражении как `&1`, `&2`; необязательные — в фигурных скобках `f(&1{, &2})`; переменное число — +`&n[]` (только последним в списке). Отдельного ключа для них нет и быть не может. + +### 18.5 Границы + +- **Кубы OLAP** (`Cube`, `DimensionTable`, `Dimension`, `Resource`) не поддерживаются: проверить их + платформой нечем — SSAS в контейнер не ставится, а эмит вслепую по документации уже давал осечку. +- **Формы, макеты и модули** таблиц компилятор не создаёт — как и у прочих объектов (`form-add`, + `form-compile`, `template-add`). +- **Таблица без ключевых полей** собирается: загрузку XML платформа принимает молча, хотя + Конфигуратор интерактивно ключ требует. `meta-validate` предупреждает, но не отвергает — рабочие + конфигурации без ключей существуют. Без ключа недоступны форма записи и набор записей. +- `meta-compile` описывает источник **целиком**: повторный запуск заменяет его файл, выдаёт новый uuid, + а таблицы, которых нет в описании, остаются на диске сиротами. Дописать таблицу или функцию + в существующий источник умеет `meta-edit` (`add.tables` / `add.functions`), удалить таблицу — + `meta-remove ExternalDataSource.<Источник>.Table.<Таблица>`.