1113 lines
33 KiB
Markdown
1113 lines
33 KiB
Markdown
# 1C SQL Format Specification Draft
|
||
|
||
Date: 2026-06-20
|
||
|
||
This document describes universal 1C SQL storage rules observed and verified so
|
||
far. It must not contain knowledge about a particular infobase object such as
|
||
`АвансовыйОтчет`, except as test evidence in a separate report.
|
||
|
||
## Scope
|
||
|
||
The goal is a parser that understands 1C metadata storage, then applies that
|
||
understanding to any concrete infobase.
|
||
|
||
The parser should know:
|
||
|
||
- where metadata containers live in SQL;
|
||
- how to decode metadata payloads;
|
||
- how to read DBNames storage records;
|
||
- how to follow extension CAS manifests;
|
||
- how to map metadata objects and attributes to physical data tables/columns.
|
||
|
||
The parser should not hardcode:
|
||
|
||
- concrete configuration object names;
|
||
- concrete GUIDs from one customer base;
|
||
- physical table numbers from one infobase.
|
||
|
||
## SQL Containers
|
||
|
||
Observed top-level SQL containers:
|
||
|
||
- `Config`
|
||
- `ConfigSave`
|
||
- `ConfigCAS`
|
||
- `ConfigCASSave`
|
||
- `Params`
|
||
- `_ExtensionsInfo`
|
||
|
||
Current interpretation:
|
||
|
||
- `Config` stores direct metadata payloads for the main configuration.
|
||
- `ConfigSave` stores saved-but-not-applied configuration state when present.
|
||
- `ConfigCAS` stores content-addressed payload files.
|
||
- `ConfigCASSave` is the saved-state counterpart when present.
|
||
- `Params` stores platform metadata maps such as `DBNames`.
|
||
- `_ExtensionsInfo` stores extension registry rows and extension root package
|
||
pointers.
|
||
|
||
## Payload Encoding
|
||
|
||
Observed direct metadata payloads:
|
||
|
||
```text
|
||
SQL BinaryData -> raw deflate -> UTF-8 text with optional BOM -> brace tree
|
||
```
|
||
|
||
Known compression envelopes to try:
|
||
|
||
1. raw deflate
|
||
2. zlib
|
||
3. gzip
|
||
4. none
|
||
|
||
Known text encodings to try after decompression:
|
||
|
||
1. UTF-8 with BOM
|
||
2. UTF-8
|
||
3. UTF-16LE
|
||
4. UTF-16BE
|
||
5. CP1251
|
||
|
||
If UTF-8 BOM is present, it must take priority over heuristic encoding scores.
|
||
|
||
## Brace Tree Grammar
|
||
|
||
The decoded metadata text is a nested brace format:
|
||
|
||
```text
|
||
value = list | string | atom
|
||
list = "{" [value ("," value)*] "}"
|
||
string = '"' text-with-doubled-quotes '"'
|
||
atom = non-space text until one of "{", "}", ",", ":"
|
||
sequence = value+
|
||
```
|
||
|
||
The parser must preserve generic structure before assigning semantic names.
|
||
|
||
Generic node examples:
|
||
|
||
```json
|
||
{"type": "atom", "value": "1"}
|
||
{"type": "string", "value": "ru"}
|
||
{"type": "list", "items": [...]}
|
||
```
|
||
|
||
## DBNames
|
||
|
||
`Params/DBNames*` is the platform-maintained map from metadata/storage GUIDs to
|
||
storage roles and SQL numeric suffixes.
|
||
|
||
Observed decoded shape:
|
||
|
||
```text
|
||
{ root_number,
|
||
{ declared_count,
|
||
{ guid, storage_role, sql_number },
|
||
...
|
||
}
|
||
}
|
||
```
|
||
|
||
Parser output:
|
||
|
||
```json
|
||
{
|
||
"guid": "84e4c0c3-2a21-4aba-a7b0-f92b3f2878ec",
|
||
"storage_role": "Document",
|
||
"sql_number": 563,
|
||
"source": "DBNames"
|
||
}
|
||
```
|
||
|
||
Important rule: preserve `storage_role` exactly as stored by the platform. Do
|
||
not infer object kind from physical SQL table names.
|
||
|
||
Observed storage role families:
|
||
|
||
- table-like roles: `Document`, `Reference`, `Enum`, `InfoRg`, `AccumRg`,
|
||
`Const`, `Node`, `ScheduledJobs`;
|
||
- structural roles: `Fld`, `VT`, `LineNo`, `ByDims`, `ByField`,
|
||
`ByResource`, `ByProperty`.
|
||
|
||
Physical table/column names are derived after reading DBNames, for example:
|
||
|
||
```text
|
||
Document + 563 -> _Document563
|
||
Fld + 1234 -> _Fld1234
|
||
```
|
||
|
||
This mapping is a storage route, not a metadata object model by itself.
|
||
|
||
## Base Config Object Route
|
||
|
||
For main configuration metadata objects:
|
||
|
||
```text
|
||
Config.FileName = metadata_object_guid
|
||
Config.BinaryData = object payload
|
||
```
|
||
|
||
Observed object payload:
|
||
|
||
```text
|
||
root marker = 1
|
||
root list length = 8 or 9 for tested top-level objects
|
||
```
|
||
|
||
Object identity is inside the object body. Verified pattern:
|
||
|
||
```text
|
||
{1,0,<object_guid>}, "<name>", {1,"ru","<synonym>"}
|
||
```
|
||
|
||
This pattern has been observed for tested `Document`, `Catalog`, and
|
||
`InformationRegister` objects. The exact object-body position must be derived
|
||
from the tree and validated per object kind, not hardcoded from one sample.
|
||
|
||
## Extension Route
|
||
|
||
Extension metadata is not retrieved by `Config.FileName = object_guid` in the
|
||
tested cases. It is reached through the extension package path:
|
||
|
||
```text
|
||
_ExtensionsInfo
|
||
-> _ExtensionZippedInfo
|
||
-> root_cas_key
|
||
-> ConfigCAS[root_cas_key]
|
||
-> root manifest
|
||
-> object_id
|
||
-> cas_key
|
||
-> ConfigCAS[cas_key]
|
||
```
|
||
|
||
Observed `_ExtensionZippedInfo` binary structure:
|
||
|
||
```text
|
||
bytes 0..3 : marker
|
||
bytes 4..23 : root CAS SHA1 key
|
||
bytes 24.. : small extension metadata fragment
|
||
```
|
||
|
||
`ConfigCAS.FileName` equals `SHA1(BinaryData)` for the stored compressed bytes.
|
||
|
||
The root CAS file contains manifest pairs:
|
||
|
||
```text
|
||
object_id -> base64(20-byte sha1)
|
||
```
|
||
|
||
The decoded 20-byte SHA1 is the target ConfigCAS key.
|
||
|
||
`object_id` may be:
|
||
|
||
```text
|
||
<guid>
|
||
<guid>.0
|
||
<guid>.1
|
||
<guid>.2
|
||
<guid>.3
|
||
...
|
||
```
|
||
|
||
Suffix is only a part key. It is not a semantic label by itself.
|
||
|
||
## Extension/Object Part Payloads
|
||
|
||
Observed payload signatures:
|
||
|
||
- `root=1`: metadata object or child metadata object description;
|
||
- `root=4`: form payload structure;
|
||
- `root=5` with `#base64`: embedded help/text payload;
|
||
- `root=8` with `MOXCEL`: spreadsheet/template payload;
|
||
- `stream_headers`: binary/text stream container, often containing BSL module
|
||
payloads;
|
||
- `#base64`: embedded base64 data inside a brace tree.
|
||
|
||
Semantic assignment requires direct evidence:
|
||
|
||
- root shape;
|
||
- payload markers;
|
||
- declared stream sizes;
|
||
- exact SHA1 match with exported XML/BSL/HTML;
|
||
- normalized text match when byte-level line endings differ.
|
||
|
||
## Metadata Object Model
|
||
|
||
The universal parser should expose a model like:
|
||
|
||
```json
|
||
{
|
||
"guid": "...",
|
||
"kind": "Document",
|
||
"name": "...",
|
||
"synonyms": {"ru": "..."},
|
||
"attributes": [],
|
||
"tabular_sections": [],
|
||
"forms": [],
|
||
"templates": [],
|
||
"modules": [],
|
||
"storage": []
|
||
}
|
||
```
|
||
|
||
At the current stage only the following are accepted as proven fields:
|
||
|
||
- GUID;
|
||
- name;
|
||
- localized synonym where the identity block is found;
|
||
- generic root marker and root length;
|
||
- raw parsed tree;
|
||
- DBNames storage records;
|
||
- payload route.
|
||
|
||
Attributes, tabular sections, commands, forms, templates, and modules must be
|
||
added only after position-by-position validation against XML exports and SQL
|
||
routes.
|
||
|
||
## Position Validation Against XML
|
||
|
||
The parser must not globally assign section semantics from a single object
|
||
sample. The observed `root=1` payload shape is shared by multiple object kinds,
|
||
but section coordinates differ by kind.
|
||
|
||
Historical mechanical validation compared brace-tree positions with XML exports.
|
||
Those per-sample reports were removed from the current workspace; the stable
|
||
conclusions are kept here as format notes.
|
||
|
||
Observed examples:
|
||
|
||
- `Document`, 4 samples: XML-heavy top paths include `3`, `5`, and `1`.
|
||
- `Catalog`, 4 samples: XML-heavy top paths include `5`, `6`, and `1`.
|
||
- `InformationRegister`, 4 samples: XML-heavy top paths include `1`, `4`,
|
||
and `3`; path `7` appears in one tested sample.
|
||
- `ExchangePlan` extension sample: XML-heavy top paths include `3` and `5`.
|
||
|
||
These are evidence points, not final semantic names. A future object-kind parser
|
||
must attach names such as attributes or tabular sections only when the mapping is
|
||
confirmed across enough samples and matches XML structure.
|
||
|
||
Historical observed profiles with `min_support_ratio = 0.75`:
|
||
|
||
- `Document`: paths `3`, `5`, `1`.
|
||
- `Catalog`: paths `5`, `1`, `6`.
|
||
- `InformationRegister`: paths `1`, `4`, `3`.
|
||
|
||
The profile format keeps `semantic_status = unassigned`. It is a compact,
|
||
evidence-based section selector, not yet a semantic object-kind parser.
|
||
|
||
## XML Category Mapping
|
||
|
||
The next validation layer compares each observed profile section with structured
|
||
XML metadata categories. The XML extractor reads only stable metadata elements:
|
||
|
||
- the root object category, name, synonym, and UUID;
|
||
- generated types;
|
||
- standard attributes;
|
||
- child objects such as `Attribute`, `TabularSection`, `Form`, `Template`,
|
||
`Dimension`, and `Resource`.
|
||
|
||
The mapper scores a Config tree section against each XML category by exact name,
|
||
synonym, and UUID matches. This keeps the process mechanical: a section receives
|
||
a candidate semantic category only from direct evidence found in the XML export.
|
||
|
||
Historical evidence from 12 base objects, 4 per kind:
|
||
|
||
```text
|
||
Catalog path 1 -> Catalog 4/4
|
||
Catalog path 5 -> TabularSection 3/4 candidate
|
||
Catalog path 6 -> Attribute 4/4
|
||
Document path 1 -> Document 4/4
|
||
Document path 3 -> TabularSection 4/4
|
||
Document path 5 -> Attribute 4/4
|
||
InformationRegister path 1 -> InformationRegister 4/4
|
||
InformationRegister path 3 -> Resource 3/4 candidate
|
||
InformationRegister path 4 -> Dimension 4/4
|
||
```
|
||
|
||
Only 4/4 mappings are currently strong candidates for object-kind parser rules.
|
||
The 3/4 mappings remain candidate semantics until the missing or conflicting
|
||
sample is explained by object shape, optional sections, or a deeper path rule.
|
||
|
||
Follow-up parser projection reports:
|
||
|
||
- strict, only `support_ratio = 1.0`:
|
||
`reports/1c-sql/upo/structured-metadata-summary.json`
|
||
- candidate, `support_ratio >= 0.75`:
|
||
`reports/1c-sql/upo/structured-metadata-candidates-summary.json`
|
||
|
||
Strict projection currently extracts:
|
||
|
||
- `Document`: tabular sections from path `3`, attributes from path `5`;
|
||
- `Catalog`: attributes from path `6`;
|
||
- `InformationRegister`: dimensions from path `4`.
|
||
|
||
Candidate projection adds:
|
||
|
||
- `Catalog`: tabular sections from path `5`;
|
||
- `InformationRegister`: resources from path `3`.
|
||
|
||
The `Catalog path 5` 3/4 conflict is explained by nested content: the section
|
||
contains the tabular section object and may also contain attributes belonging to
|
||
that tabular section. A category scorer can therefore see more `Attribute`
|
||
matches than `TabularSection` matches, but the tabular section UUID is still
|
||
present in the section.
|
||
|
||
The `InformationRegister path 3` 3/4 result is explained by optional resources:
|
||
one tested register has no resource matches, while the other tested registers
|
||
map resources from the same path.
|
||
|
||
This means the next semantic parser step is not to add arbitrary conditions. It
|
||
is to represent section nesting explicitly:
|
||
|
||
```text
|
||
object section
|
||
-> child object list
|
||
-> child object identity
|
||
-> child attributes/properties
|
||
```
|
||
|
||
Until that nesting is decoded, the structured projection must keep evidence on
|
||
each item: source path, name hit, synonym hit, and UUID hit.
|
||
|
||
All-kind expansion was validated on 112 samples across 44 XML top object kinds.
|
||
The current retained summary used by SQL-read enrichment is
|
||
`reports/1c-sql/upo/structured-metadata-all-kinds-dbnames-summary.json`.
|
||
|
||
Examples of confirmed all-kind mappings:
|
||
|
||
```text
|
||
AccumulationRegister path 5 -> Resource
|
||
AccumulationRegister path 6 -> Attribute
|
||
AccumulationRegister path 7 -> Dimension
|
||
AccountingRegister path 3 -> Dimension
|
||
AccountingRegister path 5 -> Resource
|
||
AccountingRegister path 7 -> Attribute
|
||
BusinessProcess path 6 -> Attribute
|
||
Enum path 6 -> EnumValue
|
||
DocumentJournal path 4 -> Column
|
||
HTTPService path 3 -> URLTemplate
|
||
IntegrationService path 3 -> IntegrationServiceChannel
|
||
Report path 4 -> Attribute
|
||
Task path 5 -> Attribute
|
||
Task path 6 -> AddressingAttribute
|
||
Task path 8 -> Command
|
||
WebService path 3 -> Operation
|
||
```
|
||
|
||
For many metadata kinds, path `1` currently maps to the root object identity and
|
||
main properties. These mappings are useful for object identification but do not
|
||
yet expose child objects. Kinds with only root-object evidence still need deeper
|
||
payload analysis or related ConfigCAS/module payload routing.
|
||
|
||
## Child Record Boundaries
|
||
|
||
Many child-object sections use the same mechanical container shape:
|
||
|
||
```text
|
||
{ section_marker_guid, declared_count, record_0, record_1, ... }
|
||
```
|
||
|
||
The parser now detects these boundaries without assigning business semantics.
|
||
Semantic assignment is still evidence-based through XML category matching.
|
||
|
||
All 474 structured child items from the 112-sample batch are now attached to a
|
||
specific `record_path`:
|
||
|
||
```text
|
||
section_path = top-level section container
|
||
record_path = concrete child record inside that container
|
||
record_index = zero-based child record index
|
||
```
|
||
|
||
Examples:
|
||
|
||
```text
|
||
Enum value "Требуется" -> section 6, record 6.2
|
||
AccumulationRegister resource "Сумма" -> section 5, record 5.2
|
||
AccumulationRegister attribute "Подразделение" -> section 6, record 6.2
|
||
Document attribute "Автор" -> section 5, record 5.2
|
||
```
|
||
|
||
## DBNames Route Enrichment
|
||
|
||
Structured metadata reports can be enriched with `DBNames` routes:
|
||
|
||
- enrichment summary:
|
||
`reports/1c-sql/upo/structured-metadata-all-kinds-dbnames-summary.json`
|
||
- enriched reports:
|
||
`reports/1c-sql/upo/structured-metadata-all-kinds-dbnames/`
|
||
|
||
Current all-kind enrichment coverage:
|
||
|
||
```text
|
||
metadata items total: 474
|
||
items with DBNames routes: 420
|
||
object storage route count: 88
|
||
```
|
||
|
||
Observed child route coverage:
|
||
|
||
```text
|
||
attributes 338/341
|
||
tabular_sections 29/29
|
||
dimensions 19/19
|
||
resources 8/8
|
||
columns 17/17
|
||
addressing_attributes 4/4
|
||
accounting_flags 1/1
|
||
integration_service_channels 4/4
|
||
commands 0/4
|
||
enum_values 0/15
|
||
operations 0/15
|
||
url_templates 0/17
|
||
```
|
||
|
||
This is expected: persisted data fields have `DBNames` routes, while logical
|
||
metadata-only children such as enum values, web-service operations, commands,
|
||
and HTTP URL templates do not necessarily have independent SQL storage routes.
|
||
|
||
Known route families now classified:
|
||
|
||
```text
|
||
Fld -> field/column base name `_FldN`
|
||
Document, Reference, ... -> table names such as `_DocumentN`
|
||
AccumRg*, AccRg* -> register table families
|
||
VT, LineNo, ByField, ... -> structural routes needing parent context
|
||
IntegService*/IntegChannel* -> integration service tables
|
||
TurnoverDt/Ct/Turnover -> structural accounting-register routes
|
||
```
|
||
|
||
Column suffixes such as `_RRef`, `_TYPE`, `_RTRef`, and numeric/string value
|
||
columns are not inferred here. They require type decoding from the child record
|
||
and verification against the physical SQL table schema.
|
||
|
||
## Value Types And Simple Columns
|
||
|
||
The XML validation oracle now extracts value type information for child
|
||
metadata items:
|
||
|
||
```text
|
||
types = XML Type entries, for example xs:string or cfg:CatalogRef.Users
|
||
qualifiers = string length, number precision/scale, date fractions, etc.
|
||
is_composite = true when multiple Type entries are present
|
||
```
|
||
|
||
Validated simple column rules:
|
||
|
||
```text
|
||
single primitive type xs:string/xs:decimal/xs:boolean/xs:dateTime
|
||
+ DBNames Fld N
|
||
-> SQL column _FldN
|
||
|
||
single 1C reference type cfg:*Ref.Name
|
||
+ DBNames Fld N
|
||
-> SQL column _FldNRRef
|
||
```
|
||
|
||
Composite types are deliberately not guessed yet:
|
||
|
||
```text
|
||
multiple XML Type entries -> needs_composite_type_mapping
|
||
```
|
||
|
||
Validated composite column rules:
|
||
|
||
```text
|
||
multiple value types
|
||
-> _FldN_TYPE
|
||
|
||
multiple reference-capable value types
|
||
-> _FldN_TYPE
|
||
-> _FldN_RTRef
|
||
-> _FldN_RRRef
|
||
|
||
mixed string + reference value types
|
||
-> _FldN_TYPE
|
||
-> _FldN_S
|
||
-> _FldN_RRRef
|
||
```
|
||
|
||
`_RTRef` is present when the composite field needs to distinguish multiple
|
||
reference metadata types. When the composite type is a primitive/reference mix
|
||
with a single reference metadata type, `_TYPE` is enough to distinguish the
|
||
primitive vs reference branch, so `_RTRef` is not emitted.
|
||
|
||
Validated composite reference read rule:
|
||
|
||
```text
|
||
_FldN_TYPE -> branch marker
|
||
_FldN_RTRef -> DBNames SQL number of the selected reference target
|
||
_FldN_RRRef -> _IDRRef in the selected target table
|
||
```
|
||
|
||
For the current `Document.АвансовыйОтчет` smoke sample:
|
||
|
||
```text
|
||
Запасы.ЕдиницаИзмерения:
|
||
value types: cfg:CatalogRef.КлассификаторЕдиницИзмерения,
|
||
cfg:CatalogRef.ЕдиницыИзмерения
|
||
_TYPE: 08
|
||
_RTRef: 000000a6 -> decimal 166 -> DBNames Reference 166
|
||
target: Catalog.ЕдиницыИзмерения -> _Reference166
|
||
_RRRef: value _IDRRef in _Reference166
|
||
```
|
||
|
||
Composite reference smoke result:
|
||
|
||
- tool: `scripts/resolve_1c_sql_composite_references.ps1`;
|
||
- report: `reports/1c-sql/upo/sql-composite-reference-resolution-document-avansovy-otchet.json`;
|
||
- composite groups: `6`;
|
||
- resolved: `6`;
|
||
- target misses: `0`;
|
||
- row misses: `0`.
|
||
|
||
## SQL Composite Values
|
||
|
||
Composite value resolution is broader than composite reference resolution. A
|
||
single metadata field can have several physical columns:
|
||
|
||
```text
|
||
_FldN_TYPE -> type/branch marker
|
||
_FldN_S -> string branch
|
||
_FldN_N -> number branch
|
||
_FldN_L -> boolean branch
|
||
_FldN_T -> datetime branch
|
||
_FldN_RTRef -> reference target SQL number when multiple reference targets exist
|
||
_FldN_RRRef -> reference value id
|
||
```
|
||
|
||
The adapter currently selects the branch by observed physical values:
|
||
|
||
```text
|
||
non-zero RRRef -> reference
|
||
non-empty S/N/L/T -> primitive
|
||
otherwise -> empty_or_unknown
|
||
```
|
||
|
||
This intentionally avoids overclaiming the meaning of `_TYPE` until enough
|
||
non-empty examples are collected for every primitive/reference combination.
|
||
|
||
Current composite-value smoke result for `Document.АвансовыйОтчет`:
|
||
|
||
- tool: `scripts/resolve_1c_sql_composite_values.py`;
|
||
- report: `reports/1c-sql/upo/sql-composite-value-resolution-document-avansovy-otchet.json`;
|
||
- composite values: `11`;
|
||
- branch counts: `reference=6`, `empty_or_unknown=5`.
|
||
|
||
The only currently observed mixed primitive/reference metadata field in the
|
||
sample set is:
|
||
|
||
```text
|
||
BusinessProcess.ЗаявкаСотрудникаИзменитьЛичныеДанные.ДокументВид:
|
||
value types: xs:string, cfg:CatalogRef.ВидыДокументовФизическихЛиц
|
||
physical columns: _Fld46687_TYPE, _Fld46687_RRRef, _Fld46687_S
|
||
```
|
||
|
||
The target table has no rows in the current UPO SQL snapshot, so this remains a
|
||
structural rule without non-empty value observations.
|
||
|
||
## SQL Read View
|
||
|
||
`onec_sql_read_view.v1` is the first agent-facing read result. It combines:
|
||
|
||
```text
|
||
onec_sql_read_result.v1
|
||
+ onec_sql_reference_resolution.v1
|
||
+ onec_sql_composite_reference_resolution.v1
|
||
+ onec_sql_composite_value_resolution.v1
|
||
-> onec_sql_read_view.v1
|
||
```
|
||
|
||
The view keeps two row shapes:
|
||
|
||
```text
|
||
cells:
|
||
original physical-cell map from SQL aliases/columns, with resolved evidence
|
||
attached to the exact source cell when available
|
||
|
||
fields:
|
||
logical metadata_path groups, where multi-column fields such as composite
|
||
references are represented as one field with column evidence underneath
|
||
```
|
||
|
||
The view does not erase raw SQL evidence. It attaches presentations next to the
|
||
raw values:
|
||
|
||
```text
|
||
attributes.ВалютаДокумента -> resolved.presentation = "руб."
|
||
attributes.Организация -> resolved.presentation = "АЙЭФСИЭМ ГРУПП ООО"
|
||
Запасы.ЕдиницаИзмерения -> resolved.presentation = "шт" or "кг"
|
||
```
|
||
|
||
Every logical field also gets `display_value`, selected in this order:
|
||
|
||
```text
|
||
resolved.presentation
|
||
resolved.selected_presentation
|
||
composite primitive value
|
||
raw single-column value
|
||
null
|
||
```
|
||
|
||
For SQL date/time strings with the 1C SQL year offset, raw cell values are
|
||
preserved unchanged and only `display_value` is normalized by subtracting 2000
|
||
years from years `>= 3000`. Example: raw `4025-09-16T08:00:00.0000000` is
|
||
displayed as `2025-09-16T08:00:00.0000000`.
|
||
|
||
For one-byte SQL binary boolean values, raw cell values are also preserved
|
||
unchanged. `display_value` maps `00` to `false` and `01` to `true` only when
|
||
the field is a known standard boolean (`_Marked`, `_Posted`, `_Active`) or the
|
||
metadata value type contains `xs:boolean`.
|
||
|
||
## Enum Presentation
|
||
|
||
SQL enum tables store `_EnumOrder`, but agent-facing output needs enum value
|
||
names. The adapter builds:
|
||
|
||
```text
|
||
onec_enum_presentation_map.v1
|
||
```
|
||
|
||
from enum XML routes in the unified object route index:
|
||
|
||
```text
|
||
Enum.<Name>.ChildObjects.EnumValue order
|
||
-> value uuid
|
||
-> value name
|
||
-> ru synonym
|
||
```
|
||
|
||
When the same enum name exists in the base configuration and extensions, base
|
||
configuration XML has priority for the presentation map. Extension evidence is
|
||
not discarded from the route index; it is only not allowed to overwrite the
|
||
base enum presentation by name.
|
||
|
||
Current smoke examples:
|
||
|
||
```text
|
||
EnumRef.ТипыНалогообложенияНДС, _EnumOrder = 0
|
||
-> Облагается (с НДС)
|
||
|
||
EnumRef.ПоложениеРеквизитаНаФорме, _EnumOrder = 0
|
||
-> В шапке
|
||
```
|
||
|
||
Read views expose enum presentations as agent-facing display values. Example
|
||
shape:
|
||
|
||
```text
|
||
attributes.НалогообложениеНДС.display_value = Облагается (с НДС)
|
||
attributes.ПоложениеПроекта.display_value = В шапке
|
||
```
|
||
|
||
For declared-target misses with extension table hits, the view keeps both:
|
||
|
||
```text
|
||
resolved.found = false
|
||
resolved.selected_alternate = _Reference348X1 / other observed hit
|
||
resolved.selected_presentation = ...
|
||
```
|
||
|
||
The full read pipeline is available as one orchestrated command:
|
||
|
||
```text
|
||
scripts/read_1c_object_view.py
|
||
--kind <Kind>
|
||
--name <Name>
|
||
--summary <structured metadata DBNames batch summary>
|
||
--validation <predicted column validation>
|
||
--route-index <unified object route index>
|
||
--output-dir <run output directory>
|
||
```
|
||
|
||
For shells with uncertain Unicode handling, pass object names as UTF-8 base64:
|
||
|
||
```text
|
||
--name-b64 0JDQstCw0L3RgdC+0LLRi9C50J7RgtGH0LXRgg==
|
||
```
|
||
|
||
The orchestrator intentionally writes ASCII-only artifact file names. Russian
|
||
metadata names remain inside JSON payloads as data.
|
||
|
||
Current orchestrated smoke result:
|
||
|
||
- tool: `scripts/read_1c_object_view.py`;
|
||
- output directory: `reports/1c-sql/upo/object-read-runs`;
|
||
- object: `Document.АвансовыйОтчет`;
|
||
- final schema: `onec_sql_read_view.v1`;
|
||
- main rows: `5`;
|
||
- table parts: `5`;
|
||
- simple reference cells: `55`;
|
||
- composite reference groups: `6`;
|
||
- sample presentations: `attributes.Автор = Evgeniya.Matyushina`,
|
||
`Запасы.ЕдиницаИзмерения = шт`.
|
||
|
||
Validated tabular-section table rule:
|
||
|
||
```text
|
||
object primary table + DBNames VT N -> <object_table>_VTN
|
||
```
|
||
|
||
Only primary object tables are used for this prediction. Change tables and
|
||
auxiliary tables such as `ChngR`, `SInf`, `BPrPoints`, totals/options tables,
|
||
and similar companions are excluded from tabular-section table prediction.
|
||
|
||
Validated tabular-section attribute rule:
|
||
|
||
```text
|
||
TabularSection XML ChildObjects/Attribute
|
||
-> parent TabularSection UUID
|
||
-> nested SQL child record under the tabular section record
|
||
-> DBNames Fld N
|
||
-> parent tabular-section table column
|
||
```
|
||
|
||
The parser keeps both levels:
|
||
|
||
```text
|
||
tabular section:
|
||
section_path, record_path, VT/LineNo routes, physical table
|
||
|
||
tabular section attribute:
|
||
parent tabular section UUID/name
|
||
nested record_path
|
||
value type
|
||
Fld route
|
||
physical column in the parent tabular-section table
|
||
```
|
||
|
||
Physical column validation against the live SQL schema:
|
||
|
||
- script: `scripts/validate_1c_predicted_columns.ps1`
|
||
- report: `reports/1c-sql/upo/predicted-column-validation.json`
|
||
|
||
Current result:
|
||
|
||
```text
|
||
predicted columns: 695
|
||
found in SQL schema: 695
|
||
|
||
predicted tabular-section tables: 29
|
||
found in SQL schema: 29
|
||
```
|
||
|
||
Validated by kind:
|
||
|
||
```text
|
||
AccountingRegister columns 9/9
|
||
AccumulationRegister columns 25/25
|
||
BusinessProcess columns 136/136
|
||
Catalog columns 81/81, table parts 9/9
|
||
ChartOfAccounts columns 7/7, table parts 1/1
|
||
ChartOfCalculationTypes columns 27/27, table parts 2/2
|
||
Document columns 388/388, table parts 17/17
|
||
InformationRegister columns 3/3
|
||
Task columns 19/19
|
||
```
|
||
|
||
Validated by metadata field:
|
||
|
||
```text
|
||
attributes 361/361
|
||
tabular_section_attributes 304/304
|
||
dimensions 17/17
|
||
resources 8/8
|
||
addressing_attributes 4/4
|
||
accounting_flags 1/1
|
||
```
|
||
|
||
This gives a proven read route for simple persisted fields:
|
||
|
||
```text
|
||
metadata object GUID
|
||
-> Config payload
|
||
-> child section
|
||
-> child record
|
||
-> XML value type
|
||
-> DBNames Fld route
|
||
-> object DBNames table route
|
||
-> physical SQL column
|
||
```
|
||
|
||
## SQL Read Projection
|
||
|
||
The adapter can now build a read projection from enriched metadata:
|
||
|
||
- tool: `plugins/1c/tools/build_sql_read_projection.py`
|
||
- sample report:
|
||
`reports/1c-sql/upo/sql-read-projection-document-avansovy-otchet.json`
|
||
|
||
The projection contains:
|
||
|
||
```text
|
||
main_table
|
||
main_columns[]:
|
||
metadata_path
|
||
metadata_uuid
|
||
value_type
|
||
physical column
|
||
SQL type
|
||
stable SQL select alias
|
||
|
||
table_parts[]:
|
||
tabular section name/uuid
|
||
physical table
|
||
owner/line columns
|
||
nested tabular-section attribute columns
|
||
SELECT statement
|
||
```
|
||
|
||
SQL aliases are ASCII-only (`c001`, `c002`, ...). Human-readable Russian names
|
||
stay in JSON metadata fields. This avoids client encoding problems while keeping
|
||
the query result mechanically mappable back to metadata.
|
||
|
||
Smoke-tested generated SQL:
|
||
|
||
```text
|
||
Document.АвансовыйОтчет main table
|
||
SELECT FROM _Document563
|
||
result: 37 columns, 5 rows
|
||
|
||
Document.АвансовыйОтчет.Запасы tabular section
|
||
SELECT FROM _Document563_VT1614
|
||
result: 33 columns, 5 rows
|
||
```
|
||
|
||
This is the first end-to-end read chain:
|
||
|
||
```text
|
||
task asks for object data
|
||
-> metadata object identity
|
||
-> SQL storage table
|
||
-> metadata fields and tabular-section fields
|
||
-> physical SQL columns/tables
|
||
-> generated SELECT
|
||
-> result columns mapped back through projection JSON
|
||
```
|
||
|
||
Examples:
|
||
|
||
```text
|
||
Document.АвансовыйОтчет.Автор
|
||
cfg:CatalogRef.Пользователи + Fld 1584 -> _Document563._Fld1584RRef
|
||
|
||
Document.АвансовыйОтчет.Комментарий
|
||
xs:string + Fld 1587 -> _Document563._Fld1587
|
||
|
||
Document.АвансовыйОтчет.ДокументОснование
|
||
composite DocumentRef + Fld 1586
|
||
-> _Document563._Fld1586_TYPE
|
||
-> _Document563._Fld1586_RTRef
|
||
-> _Document563._Fld1586_RRRef
|
||
|
||
Document.АвансовыйОтчет.ВыданныеАвансы
|
||
VT 1610 -> _Document563_VT1610
|
||
|
||
Document.АвансовыйОтчет.ВыданныеАвансы.Документ
|
||
composite DocumentRef + Fld 1612
|
||
-> _Document563_VT1610._Fld1612_TYPE
|
||
-> _Document563_VT1610._Fld1612_RTRef
|
||
-> _Document563_VT1610._Fld1612_RRRef
|
||
|
||
Document.АвансовыйОтчет.ВыданныеАвансы.Сумма
|
||
xs:decimal(15,2) + Fld 1613
|
||
-> _Document563_VT1610._Fld1613
|
||
|
||
AccumulationRegister.АвансовыеПлатежиИностранцевПоНДФЛ.Сумма
|
||
xs:decimal(15,2) + Fld 45848 -> _AccumRg45843._Fld45848
|
||
```
|
||
|
||
## Metadata To Data Mapping
|
||
|
||
The target runtime chain is:
|
||
|
||
```text
|
||
metadata object -> DBNames storage role -> physical table
|
||
metadata field -> DBNames Fld/VT/LineNo records -> physical column/table part
|
||
```
|
||
|
||
Example shape, without hardcoded concrete values:
|
||
|
||
```text
|
||
Document object GUID
|
||
-> DBNames role Document, sql_number N
|
||
-> _DocumentN
|
||
|
||
Attribute GUID
|
||
-> DBNames role Fld, sql_number M
|
||
-> _FldM or _FldMRRef depending on stored value type
|
||
```
|
||
|
||
Implemented mechanical route helpers:
|
||
|
||
```text
|
||
Document + N -> _DocumentN
|
||
DocumentChngR + N -> _DocumentChngRN
|
||
Reference + N -> _ReferenceN
|
||
InfoRg + N -> _InfoRgN
|
||
Node + N -> _NodeN
|
||
Fld + N -> _FldN base column candidate
|
||
```
|
||
|
||
Structural roles such as `VT`, `LineNo`, and `ByDims` are not standalone
|
||
physical names in the parser core. They require parent object/section context.
|
||
|
||
The suffixes such as `RRef`, `_TYPE`, `_RTRef`, and version/deletion columns
|
||
must be specified from observed SQL schema and value type evidence before write
|
||
support is allowed.
|
||
|
||
## Safety Boundary
|
||
|
||
## SQL Read Result
|
||
|
||
The read path has three separate layers:
|
||
|
||
```text
|
||
structured metadata + DBNames + SQL schema
|
||
-> onec_sql_read_projection.v1
|
||
-> SQL SELECT execution
|
||
-> onec_sql_read_result.v1
|
||
```
|
||
|
||
`onec_sql_read_projection.v1` is the deterministic query plan. It contains the
|
||
main physical table, table-part physical tables, selected physical columns, SQL
|
||
aliases, and the metadata evidence for every selected column.
|
||
|
||
`onec_sql_read_result.v1` is the executed result. It must not add new metadata
|
||
guesses. Each returned cell is keyed as:
|
||
|
||
```text
|
||
<metadata_path>::<physical_column>
|
||
```
|
||
|
||
The cell body keeps:
|
||
|
||
- `alias`: generated SQL alias, for example `c006`;
|
||
- `column`: physical SQL column, for example `_Fld1584RRef`;
|
||
- `metadata_path`: logical metadata path, for example `attributes.Автор`;
|
||
- `metadata_name`, `metadata_uuid`, `metadata_field`;
|
||
- `value_type`: value type evidence from metadata;
|
||
- `sql_type`: SQL schema evidence;
|
||
- `value`: serialized SQL value.
|
||
|
||
Binary SQL values are serialized as:
|
||
|
||
```json
|
||
{"kind": "binary", "length": 16, "hex": "..."}
|
||
```
|
||
|
||
This keeps 1C references stable without pretending that a raw `RRef` is already
|
||
a resolved presentation. Reference resolution is a later layer that must use
|
||
metadata/DBNames type evidence to choose the target table and presentation
|
||
fields.
|
||
|
||
## SQL Reference Resolution
|
||
|
||
Reference resolution starts from `value_type`, not from the physical SQL column
|
||
name.
|
||
|
||
Observed single-reference mapping:
|
||
|
||
```text
|
||
cfg:CatalogRef.<Name> -> XML kind Catalog -> DBNames role Reference -> _ReferenceN
|
||
cfg:DocumentRef.<Name> -> XML kind Document -> DBNames role Document -> _DocumentN
|
||
cfg:EnumRef.<Name> -> XML kind Enum -> DBNames role Enum -> _EnumN
|
||
```
|
||
|
||
The declared target is selected through the unified object route index:
|
||
|
||
```text
|
||
value_type name -> XML top object -> object GUID -> DBNames storage role
|
||
```
|
||
|
||
The resolver then reads `_IDRRef` in the declared target table and returns only
|
||
available presentation columns, for example `_Code`, `_Description`,
|
||
`_Number`, `_Date_Time`, `_Posted`, `_Marked`, or `_EnumOrder`.
|
||
|
||
Extension-aware databases can contain physical table variants such as
|
||
`_Reference348X1`. Therefore a failed lookup in the declared target table is
|
||
not enough evidence that the value is invalid. The resolver records
|
||
`alternate_hits` across the same table family:
|
||
|
||
```text
|
||
Catalog -> _Reference%
|
||
Document -> _Document%
|
||
Enum -> _Enum%
|
||
```
|
||
|
||
The current smoke test found `attributes.Автор` declared as
|
||
`cfg:CatalogRef.Пользователи` with target `_Reference348`, while the same
|
||
`_IDRRef` also exists in `_Reference348X1`. The adapter must keep this evidence
|
||
explicit instead of silently replacing the declared target.
|
||
|
||
Current read support is implemented through `scripts/read_1c_object_view.py`.
|
||
The command must keep declared metadata targets, selected effective physical
|
||
tables, alternate extension hits, raw SQL values, and resolved presentations as
|
||
separate evidence fields.
|
||
|
||
SQL write support is out of scope until all of the following are proven:
|
||
|
||
- round-trip decode/encode for target payload type;
|
||
- validation against 1C Designer/Enterprise;
|
||
- backup and rollback workflow;
|
||
- saved-state handling for `ConfigSave`/`ConfigCASSave`;
|
||
- extension package rebuild validation.
|
||
|
||
## Implementation Status
|
||
|
||
This document is the SQL-format reference for the current adapter. The active
|
||
agent API is documented in `docs/1c-adapter-api-contract.md`, and the active
|
||
tool list is in `plugins/1c/tools/README.md`.
|
||
|
||
Current parser/library core:
|
||
|
||
- `plugins/1c/parser/payload.py`
|
||
- `plugins/1c/parser/dbnames.py`
|
||
- `plugins/1c/parser/extensions.py`
|
||
- `plugins/1c/parser/config_object.py`
|
||
- `plugins/1c/parser/storage.py`
|
||
- `plugins/1c/parser/config_sections.py`
|
||
- `plugins/1c/parser/xml_metadata.py`
|
||
- `plugins/1c/parser/structured_metadata.py`
|
||
- `plugins/1c/parser/child_records.py`
|
||
|
||
Current adapter-facing build/read tools:
|
||
|
||
- `scripts/extract_1c_dbnames.py`
|
||
- `scripts/build_1c_xml_guid_index.py`
|
||
- `scripts/compare_1c_sql_xml_guids.py`
|
||
- `scripts/build_1c_unified_object_route_index.py`
|
||
- `scripts/build_1c_metadata_from_resolved_object.py`
|
||
- `plugins/1c/tools/build_sql_read_projection.py`
|
||
- `scripts/validate_1c_predicted_columns.ps1`
|
||
- `scripts/execute_1c_sql_read_projection.ps1`
|
||
- `scripts/resolve_1c_sql_read_references.ps1`
|
||
- `scripts/resolve_1c_sql_composite_references.ps1`
|
||
- `scripts/resolve_1c_sql_composite_values.py`
|
||
- `scripts/build_1c_enum_presentation_map.py`
|
||
- `scripts/build_1c_sql_read_view.py`
|
||
- `scripts/read_1c_object_view.py`
|
||
|
||
Current required evidence artifacts:
|
||
|
||
- `reports/1c-sql/upo/dbnames.json`
|
||
- `reports/1c-sql/upo/xml-guid-index-combined.json`
|
||
- `reports/1c-sql/upo/sql-xml-guid-compare-combined.json`
|
||
- `reports/1c-sql/upo/unified-object-route-index.json`
|
||
- `reports/1c-sql/upo/structured-metadata-all-kinds-dbnames-summary.json`
|
||
- `reports/1c-sql/upo/predicted-column-validation.json`
|
||
- `reports/1c-sql/upo/enum-presentation-map.json`
|
||
|
||
The current adapter confirms:
|
||
|
||
- DBNames records are the authoritative bridge from metadata/storage GUIDs to
|
||
physical SQL table and column roles.
|
||
- XML and extension manifest evidence provide configurator-visible Russian
|
||
names, forms, modules, templates, and object relationships.
|
||
- `effective` metadata must apply active extension overlays before returning
|
||
attributes, forms, modules, or data views.
|
||
- SQL reads are allowed only through generated projections plus live
|
||
table-column validation.
|
||
- SQL writes to configuration/data storage remain outside the safe workflow.
|