317 lines
18 KiB
Markdown
317 lines
18 KiB
Markdown
# Controlled Designer-to-SQL Decoding
|
||
|
||
Use the disposable `upo_test` base to learn SQL encodings that cannot be
|
||
established from static samples. The mutation is performed by 1C Designer or a
|
||
1C Enterprise client. The adapter remains a read-only SQL observer. The active
|
||
rule is `plugins/1c/connector/policies/designer-sql-decoding-policy.yaml`.
|
||
|
||
## Credential rule
|
||
|
||
Keep the 1C user password outside the repository. Supply it for one process
|
||
through an environment variable or an operating-system credential store. Do
|
||
not add it to `.env`, YAML, JSON, test fixtures, reports, or command examples.
|
||
|
||
## Metadata experiment
|
||
|
||
1. Select an object by a public 1C name.
|
||
2. Confirm there are no unrelated pending Designer changes.
|
||
3. Capture the target from live SQL, including active and saved-state origin.
|
||
4. In Designer change exactly one property and save it without applying the
|
||
configuration when saved-state evidence is sufficient.
|
||
5. Capture `ConfigSave` or `ConfigCASSave` again and compute the structural and
|
||
byte-level diff.
|
||
6. Repeat with a second value; one pair is only a hypothesis.
|
||
7. Promote a decoder only after the name, type, path, and ownership are stable.
|
||
8. Revert through Designer and verify rollback in SQL.
|
||
|
||
Applying the configuration is a separate explicit phase because it can change
|
||
`Config`, `ConfigCAS`, and the physical application-data schema.
|
||
|
||
## Application-data experiment
|
||
|
||
1. Resolve the object through `data.schema` using its public name.
|
||
2. Capture `data.count` and a narrowly filtered `data.list`.
|
||
3. Create or edit one test record through 1C Enterprise, never through SQL.
|
||
4. Capture the same logical filter after the 1C transaction commits.
|
||
5. Correlate logical values with SQL columns, including composite branches.
|
||
6. Revert or delete through 1C Enterprise and verify rollback read-only.
|
||
|
||
## Noise controls
|
||
|
||
- Record configuration-check errors that existed before the experiment.
|
||
- Do not run two experiments against the same object concurrently.
|
||
- Separate base configuration and extension ownership.
|
||
- Treat timestamps, version bytes, caches, and background service data as
|
||
volatile unless explicitly targeted.
|
||
- Discard a run when more than one semantic property changed.
|
||
|
||
Each accepted experiment produces a manifest with the public selector,
|
||
intended change, before/after SQL hashes, changed paths or columns, semantic
|
||
rule, second verification case, rollback evidence, and regression tests. XML
|
||
may be attached as offline naming evidence but is not read by the adapter.
|
||
|
||
## Confirmed baseline
|
||
|
||
The first live `upo_test` CAS comparison is recorded in
|
||
`reports/1c-sql/upo_test/designer-sql-baseline-20260714.json`. It proves a BSL
|
||
module-text change at tree path `$.2`. The accompanying `pos` and `end` changes
|
||
are stream-directory offsets recalculated from the text length; they are not
|
||
independent metadata properties and must be filtered as derived evidence.
|
||
|
||
## Saved extension metadata descriptors
|
||
|
||
The `test2` experiment on 2026-07-15 added a minimal calculation register and
|
||
its required chart of calculation types through Designer, then saved the
|
||
extension without applying it. The objects remain intentionally `saved_only`
|
||
for adapter regression checks.
|
||
|
||
Observed read-only SQL signatures in `ConfigCASSave`:
|
||
|
||
- `CalculationRegister`: brace root marker `1`, root length `10`, metadata
|
||
block marker `21`;
|
||
- `ChartOfCalculationTypes`: brace root marker `1`, root length `8`, metadata
|
||
block marker `35`;
|
||
- saved extension descriptor names use
|
||
`<extension-guid>__<object-guid>`; child/module parts add a numeric suffix.
|
||
|
||
The runtime adapter derives these signatures only from SQL payloads. The XML
|
||
export is offline evidence used to confirm the public object kind, name, GUID,
|
||
and the register-to-chart relationship; it is not a runtime data source.
|
||
|
||
When `state=working`, `extension.objects.find` must overlay these descriptors
|
||
from `ConfigCASSave` and report `saved_only` or `saved_override`. With
|
||
`state=active`, the same unapplied objects must not be returned. Route-cache
|
||
rebuilds must reclassify active descriptors so an earlier guessed kind cannot
|
||
survive as a false match.
|
||
|
||
Public saved-state card selectors expose `extension_guid`, object `guid`, and
|
||
`table=ConfigCASSave`; they do not expose the physical descriptor name. The
|
||
adapter reconstructs `<extension-guid>__<object-guid>` internally before the
|
||
read-only SQL lookup.
|
||
|
||
For `ChartOfCalculationTypes`, the verified kind-specific map currently covers
|
||
17 properties: the five scalar code/name settings at paths `1.24`–`1.30`, six
|
||
default/auxiliary form references, five localized presentations, and
|
||
`ActionPeriodUse` at `1.57`. The paths were cross-checked on the live
|
||
`Начисления` and `Удержания` descriptors against their offline XML exports.
|
||
|
||
For the saved-only `CalculationRegister`, the verified header map covers 13
|
||
properties: periodicity, action/base-period flags, list-form references, chart
|
||
reference, standard-command/help flags, lock and full-text modes, and three
|
||
localized list presentations. `metadata.object.properties` accepts the public
|
||
`extension_guid` + object `guid` selector and reconstructs the `ConfigCASSave`
|
||
descriptor name internally. Runtime property responses remain SQL-only.
|
||
|
||
The saved register descriptor also confirms all seven variable child-part
|
||
collections: `Attribute` at root path `3`, `Recalculation` at `4`, `Template`
|
||
at `5`, `Resource` at `6`, `Form` at `7`, `Command` at `8`, and `Dimension` at
|
||
`9`. The three field roles were distinguished by a controlled Designer sample
|
||
containing one `Реквизит1`, `Ресурс1`, and `Измерение1`. Designer saved the
|
||
extension without applying it; the adapter identified the records read-only
|
||
from the resulting live `ConfigCASSave` payload.
|
||
|
||
Saved-only register fields are available through `metadata.object.attributes`
|
||
with `table=ConfigCASSave`, `extension_guid`, and the public object `guid`.
|
||
The response returns names and decoded types while reconstructing the physical
|
||
saved descriptor key internally. `metadata.object.related` accepts the same
|
||
selector for recalculations, templates, forms, and commands; saved child keys
|
||
are also prefixed internally and remain hidden unless storage diagnostics are
|
||
explicitly requested.
|
||
|
||
A second controlled Designer sample added one item to every related collection:
|
||
`Перерасчет1`, `Макет`, `ФормаСписка`, and `Команда1`. The root descriptor then
|
||
reported a declared count of one at paths `4`, `5`, `7`, and `8`. Recalculation,
|
||
template, and form descriptors use the internal
|
||
`<extension-guid>__<child-guid>` key. A saved command exposes its BSL payload as
|
||
`<extension-guid>__<command-guid>.2`; the additional command-class GUID inside
|
||
the owner record is type evidence, not a second related command. Public related
|
||
results therefore keep only the record identity GUID and probe the `.2` module
|
||
route internally when the direct saved command descriptor is absent.
|
||
|
||
The specialized `metadata.object.forms`, `metadata.object.form.details`,
|
||
`metadata.object.commands`, and `metadata.object.modules` methods accept the
|
||
same saved-state selector. Form enumeration and detail decoding use the
|
||
internally reconstructed `<extension-guid>__<form-guid>.0` payload. Object
|
||
commands are returned from the owner descriptor and their saved `.2` payload
|
||
is verified without exposing the physical key in normal responses. Each saved
|
||
object command also returns a ready public `modules.read` selector, so callers
|
||
select the command by name and never need to calculate its GUID or SQL route.
|
||
The `.2` payload is a raw-deflate multi-stream container; `modules.read`
|
||
automatically selects its single BSL-marked stream when whole-payload text
|
||
decoding is not applicable.
|
||
|
||
Object-scoped `modules.search` and its `code.search` wrapper include these
|
||
saved command modules alongside the owner's regular modules. A caller can
|
||
therefore search by the public register selector plus BSL text and receive a
|
||
ready `modules.read`/`code.read` selector for the matching command module.
|
||
`metadata.definition.find` with `areas=["modules"]` follows the same saved
|
||
selector, enumerates command-module routines, and returns the exact procedure
|
||
or function definition with a routine-scoped read selector.
|
||
|
||
`metadata.object.parts` and `metadata.object.decode` also reconstruct the
|
||
saved descriptor prefix before reading. The former enumerates the root and
|
||
suffix payloads under `<extension-guid>__<object-guid>`; the latter decodes the
|
||
root descriptor from that key while keeping physical storage coordinates
|
||
hidden by default.
|
||
|
||
The combined `metadata.object.full` profile promotes saved object-command
|
||
selectors into its `modules` collection as `command_module` handles. This
|
||
keeps the profile lightweight (no BSL text is loaded there) while ensuring the
|
||
reported module count includes code carriers owned by commands.
|
||
|
||
Targeted `metadata.code_index.build` runs on saved command `.2` containers now
|
||
retain only streams that are positively identified as BSL. Command owner
|
||
metadata cached from `metadata.object.commands` is inherited by the concrete
|
||
`#stream:N` index row, and obsolete non-BSL rows plus their vector chunks are
|
||
pruned when the source file is rebuilt. The extension GUID is recovered from
|
||
the saved module route and retained in the indexed owner metadata.
|
||
|
||
For saved extensions, `metadata.object.get` resolves an object name through
|
||
the saved extension manifest/state route before reading its descriptor.
|
||
The public `/rpc` dispatcher preserves this name selector when
|
||
`extension_guid` and `table=ConfigCASSave` are supplied; callers may use the
|
||
object name directly and do not have to resolve its GUID first. An explicit
|
||
`guid` still takes precedence when both selectors are present.
|
||
`metadata.code_index.build` can therefore be scoped by `kind` plus `name` and
|
||
`extension_guid`; it discovers owner and command module files internally and
|
||
does not require callers to pass a physical prefix or command GUID.
|
||
The same object-scoped build enumerates saved forms, decodes their embedded
|
||
modules separately from container streams, and indexes a form only when a
|
||
valid non-empty BSL module is present. Empty generated forms are not emitted
|
||
as code carriers. Build counts distinguish `forms_scanned`,
|
||
`form_modules_discovered`, `empty_form_modules`, and `form_module_errors`, so
|
||
coverage and decoding failures are observable separately.
|
||
|
||
`CalculationRegister` is included in the public register code-carrier matrix.
|
||
Its record-set/register/command module handles use the same name-first SQL-only
|
||
read and saved-state write contract as the other register kinds.
|
||
|
||
Offline `Form.xml` analysis uses the same public vocabulary as the SQL form
|
||
decoder for form commands, events, attributes and value-table columns, check
|
||
box fields, and search/view-status/search-control additions. The XML profile
|
||
also exposes semantic properties of the root `Form` node, including command
|
||
bar location and visibility. XML remains comparison evidence only and is never
|
||
consulted by runtime adapter calls.
|
||
|
||
The SQL/XML comparison report records property-route evidence as
|
||
`XML kind/property -> SQL marker/parameter/source`. Across the 11 controlled
|
||
test-extension forms, 764 elements and 3,361 properties compare without
|
||
mismatches or XML-only properties; 23 routes have at least two matching
|
||
examples and a single SQL route. `UsualGroup.Visible` remains intentionally
|
||
variant-aware because marker `22` uses parameter `26` or `28` in two observed
|
||
structures. A controlled Designer probe on
|
||
`t_FORM_ContainerTableBehaviorVariants` changed only
|
||
`ГруппаФорма.Visible=false -> true`: parameter `26` changed `0 -> 1`, while
|
||
parameter `10` and nested `Группа1` remained unchanged. In the nested shape,
|
||
parameter `26` is a GUID and parameter `28` is the boolean visibility slot.
|
||
Saved-state writes therefore select parameter `26` only when it is boolean;
|
||
otherwise they select boolean parameter `28`. The extension was restored with
|
||
`/RollbackCfg -Extension test`, and its `ConfigCASSave` prefix was verified
|
||
empty after the probe.
|
||
|
||
Root form properties are compared separately from element properties. A
|
||
controlled `ShowCommandBar=false -> true` Designer probe changed exactly two
|
||
form atoms: parameter `17` from `0` to `2` and parameter `56` from `0` to `1`.
|
||
The SQL profile exposes this as `ОтображатьКоманднуюПанель` with
|
||
`write_shape=paired_scalar`; it is readable but must not be routed through a
|
||
single-scalar writer. `АвтоКоманднаяПанель` is resolved from the decoded form
|
||
item with `id=-1`. Before the next controlled probe, 33 root properties
|
||
matched and 44 remained.
|
||
|
||
A controlled `WindowOpeningMode=DontBlock -> LockOwner` Designer probe changed
|
||
exactly form parameter `2` from `0` to `1` and companion parameter `54` from
|
||
`0` to `1`. The SQL profile exposes the confirmed values as
|
||
`РежимОткрытияОкна=DontBlock|LockOwner` with `write_shape=paired_scalar`.
|
||
Other platform enum values remain undecoded until separately observed. The
|
||
probe was loaded only into the saved extension configuration, then rolled back;
|
||
the `test` extension prefix in `ConfigCASSave` was verified empty afterwards.
|
||
Across the 11 fixtures, 44 root properties now match; the remaining 33 are
|
||
three scalar properties repeated on each form: `AutoSaveDataInSettings`, root
|
||
`Group`, and `CommandBarLocation`.
|
||
|
||
A controlled `AutoSaveDataInSettings=Use -> DontUse` Designer probe changed
|
||
only form parameter `7` from `1` to `0`. The SQL profile exposes this as
|
||
`АвтоСохранениеДанныхВНастройках=Use|DontUse` with
|
||
`write_shape=scalar_enum`. The extension was rolled back and the `test`
|
||
prefix in `ConfigCASSave` was verified empty. Across the 11 fixtures, 55 root
|
||
properties now match; the remaining 22 are root `Group` and
|
||
`CommandBarLocation`, each repeated on all forms.
|
||
|
||
A controlled root `Group=Vertical -> Horizontal` Designer probe changed four
|
||
form parameters together: `11`, `40`, `47`, and `57`, all from `0` to `1`.
|
||
The SQL profile exposes `Группировка=Vertical|Horizontal` with
|
||
`write_shape=composite_scalar`; partial single-atom writes are not safe. The
|
||
extension was rolled back and the `test` prefix was verified empty. Across the
|
||
11 fixtures, 66 root properties now match; only `CommandBarLocation` remains.
|
||
|
||
`CommandBarLocation` shares the same parameter pair as `ShowCommandBar`. With
|
||
the panel enabled, `Top` produced `[17,56]=[2,1]` and `Bottom` produced
|
||
`[3,1]`; the hidden state is `[0,0]` and is exposed as `None`. XML `None` with
|
||
`ShowCommandBar=true` normalizes to the same SQL state as `Top`, so SQL exposes
|
||
the effective position. The profile returns
|
||
`ПоложениеКоманднойПанели=None|Top|Bottom` with
|
||
`write_shape=paired_scalar_shared`; both properties must be encoded together.
|
||
After each probe the extension was rolled back and its saved prefix was empty.
|
||
All 77 root properties across the 11 controlled forms now match SQL to XML.
|
||
|
||
The platform's third window-opening mode was verified separately because it is
|
||
not present in the UPO XML inventory. The accepted XML literal is
|
||
`LockWholeInterface`; a controlled Designer probe changed form parameters
|
||
`[2,54]` from `[0,0]` to `[2,2]`. `РежимОткрытияОкна` now decodes the complete
|
||
confirmed enum: `DontBlock=[0,0]`, `LockOwner=[1,1]`, and
|
||
`LockWholeInterface=[2,2]`. The failed tentative `LockUI` literal was rejected
|
||
by XDTO before any SQL saved state was created. The successful probe was rolled
|
||
back and the extension saved prefix was verified empty.
|
||
|
||
The complete root `Group` enum observed in UPO was verified with two additional
|
||
Designer probes. `AlwaysHorizontal` maps to
|
||
`[11,40,47,57]=[1,1,3,3]`, while `HorizontalIfPossible` maps to
|
||
`[1,2,2,2]`. Together with `Vertical=[0,0,0,0]` and
|
||
`Horizontal=[1,1,1,1]`, the decoder now covers every root form grouping value
|
||
present in the XML inventory. Each probe was rolled back and the saved prefix
|
||
was verified empty.
|
||
|
||
Active base-configuration forms also use compact root layouts. Across multiple
|
||
SQL-only samples, form payload versions 49 and 50 store
|
||
`WindowOpeningMode` directly in parameter `2`; values `0` and `1` were
|
||
confirmed by `DontBlock` list forms and `LockOwner` item forms. In these
|
||
layouts, parameter `11=0` consistently identifies root `Group=Vertical` even
|
||
when the newer companion positions are absent. The decoder applies these
|
||
fallbacks only to versions 49/50 and observed values; runtime remains SQL-only.
|
||
|
||
The active base catalog form `ЗадачиАссистентаУправления.ФормаСписка`
|
||
confirmed the compact marker-55 dynamic-list layout. Parameter `54` is the
|
||
property-bag entry count; its typed key/value pairs decode keys `5`, `6`, `8`,
|
||
`9`, `11`, `12`, `14`, and `16` as `AutoRefresh`, `AutoRefreshPeriod`,
|
||
`ChoiceFoldersAndItems`, `RestoreCurrentRow`, `ShowRoot`, `AllowRootChoice`,
|
||
`UpdateOnDataChange`, and `AllowGettingCurrentRowURL`. The following tail
|
||
records contain the user-settings-group item id, `InitialTreeView`, and the
|
||
standard `DefaultPicture` field reference. The same live payload confirms
|
||
`CommandBarLocation=None` at parameter `6` and `DefaultItem=true` at parameter
|
||
`16`. Compact marker-22 command bars expose `Autofill` at parameter `28`.
|
||
Marker-35 label fields with nested subtype marker `11` store
|
||
`AutoMaxWidth/MaxWidth` in nested positions `15/16` of parameter `39`.
|
||
|
||
The SQL/XML comparison normalizes these public XML names to the decoder's
|
||
Russian semantic vocabulary and treats marker-55 `Динамический список` as the
|
||
SQL implementation of XML `Table`. XML is used only to validate the learned
|
||
routes; runtime decoding reads the SQL payload alone.
|
||
|
||
The base item form of the same catalog confirmed more compact-layout routes.
|
||
Root parameter `20` is a typed enum with type GUID
|
||
`59ef2b80-c86b-11d5-a3c1-0050bae0a776`; value `0` decodes
|
||
`UseForFoldersAndItems=Items`. Root event bindings may reside in a direct form
|
||
block such as `1.23`; they are discovered by the GUID/handler pair shape rather
|
||
than a fixed position. GUID `bf0ac0e1-bcbb-4dfe-8fc4-0b1923b461a6` identifies
|
||
`BeforeWriteAtServer`. Compact pages options `{3,1,...}` decode
|
||
`PagesRepresentation=TabsOnTop`.
|
||
|
||
Object-backed data paths resolve public standard fields `-2/-3/-4/-5` as
|
||
`Code/Description/Parent/Ref`; custom object and tabular-section fields are
|
||
resolved from the element name and owning table. XML `AdditionalColumns`
|
||
definitions are correlated with the physical SQL form element by their full
|
||
data path. Across both active forms of
|
||
`Catalog.ЗадачиАссистентаУправления`, the comparison now covers 144 XML
|
||
elements, including the logical additional column, with no missing elements,
|
||
property mismatches, or XML-only properties.
|