docs(meta-dsl-spec): полное описание DSL внешних источников данных

Раздел 18 был указателем на reference навыка — это ломало принятое разделение: спецификация
в docs описывает DSL полно (для нас), инструкция навыка — только как применять (для модели).
Указатель экономил дублирование ценой того, что полного описания не оказалось нигде.

Теперь §18 описывает источник, таблицы, поля и функции целиком: ключи, XML-теги, умолчания,
конвенции ссылок на поля, presence-aware inputByString, различение BinaryData и ValueStorage,
запрет составного типа, и границы — почему в DSL нет значения незаполненного родителя (платформа
сбрасывает его при любой загрузке XML) и почему нет кубов OLAP.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NBsZA5cr2WFThtgp7i5WVi
This commit is contained in:
Nick Shirokov
2026-09-06 15:43:57 +03:00
co-authored by Claude Opus 5
parent 89ce1af9fa
commit 938db35c85
+158 -19
View File
@@ -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.<Таблица>`.