Initial SQL-only 1C adapter baseline

This commit is contained in:
2026-07-22 03:03:47 +03:00
commit e2503b77e7
545 changed files with 184711 additions and 0 deletions
+200
View File
@@ -0,0 +1,200 @@
#!/usr/bin/env python3
"""Resolve composite primitive/reference value groups from a 1C SQL read result."""
from __future__ import annotations
import argparse
import json
from collections import defaultdict
from pathlib import Path
from typing import Any
SUFFIX_KIND = {
"_TYPE": "type_marker",
"_S": "string",
"_N": "number",
"_L": "boolean",
"_T": "datetime",
"_RRRef": "reference_id",
"_RTRef": "reference_type",
}
def load_json(path: Path) -> dict[str, Any]:
return json.loads(path.read_text(encoding="utf-8-sig"))
def value_hex(value: Any) -> str | None:
if isinstance(value, dict) and value.get("kind") == "binary":
return value.get("hex")
return None
def is_zero_binary(value: Any) -> bool:
hex_value = value_hex(value)
return bool(hex_value) and set(hex_value) <= {"0"}
def is_present(value: Any) -> bool:
if value is None:
return False
if isinstance(value, str):
return value != ""
if isinstance(value, dict) and value.get("kind") == "binary":
return not is_zero_binary(value)
return True
def column_suffix(column: str) -> str | None:
for suffix in sorted(SUFFIX_KIND, key=len, reverse=True):
if column.endswith(suffix):
return suffix
return None
def group_row(row: dict[str, Any]) -> dict[str, list[dict[str, Any]]]:
groups: dict[str, list[dict[str, Any]]] = defaultdict(list)
for cell_key, cell in row.items():
column = str(cell.get("column") or "")
suffix = column_suffix(column)
if not suffix:
continue
metadata_path = str(cell.get("metadata_path") or cell_key)
groups[metadata_path].append({"cell_key": cell_key, "suffix": suffix, "cell": cell})
return groups
def reference_types(types: list[str]) -> list[str]:
return [item for item in types if item.startswith("cfg:") and "Ref." in item]
def primitive_types(types: list[str]) -> list[str]:
return [item for item in types if item.startswith("xs:")]
def selected_branch(parts: dict[str, dict[str, Any]], types: list[str]) -> dict[str, Any]:
primitive_candidates = []
for suffix in ("_S", "_N", "_L", "_T"):
part = parts.get(suffix)
if part and is_present(part["cell"].get("value")):
primitive_candidates.append(
{
"suffix": suffix,
"kind": SUFFIX_KIND[suffix],
"value": part["cell"].get("value"),
}
)
rrref = parts.get("_RRRef")
rtref = parts.get("_RTRef")
if rrref and is_present(rrref["cell"].get("value")):
refs = reference_types(types)
return {
"branch": "reference",
"reference_id": rrref["cell"].get("value"),
"reference_type_marker": rtref["cell"].get("value") if rtref else None,
"candidate_reference_types": refs,
"selection_evidence": "RRRef is non-zero; RTRef chooses target when present, otherwise value_type must contain a single reference type",
}
if primitive_candidates:
return {
"branch": "primitive",
"primitive_values": primitive_candidates,
"candidate_primitive_types": primitive_types(types),
"selection_evidence": "primitive physical value column is present/non-empty",
}
return {
"branch": "empty_or_unknown",
"candidate_primitive_types": primitive_types(types),
"candidate_reference_types": reference_types(types),
"selection_evidence": "no non-empty primitive or reference value column observed",
}
def collect_scope(rows: list[dict[str, Any]], *, scope: str, table_part_name: str | None, table_part_table: str | None) -> list[dict[str, Any]]:
result = []
for row_index, row in enumerate(rows):
for metadata_path, members in group_row(row).items():
if len(members) < 2:
continue
parts = {member["suffix"]: member for member in members}
if "_TYPE" not in parts:
continue
first = members[0]["cell"]
value_type = first.get("value_type") or {}
types = list(value_type.get("types") or [])
if len(types) < 2:
continue
result.append(
{
"scope": scope,
"table_part_name": table_part_name,
"table_part_table": table_part_table,
"row_index": row_index,
"metadata_path": metadata_path,
"metadata_name": first.get("metadata_name"),
"metadata_uuid": first.get("metadata_uuid"),
"value_types": types,
"type_marker": parts["_TYPE"]["cell"].get("value"),
"physical_parts": {
suffix: {
"kind": SUFFIX_KIND[suffix],
"column": part["cell"].get("column"),
"cell_key": part["cell_key"],
"value": part["cell"].get("value"),
}
for suffix, part in sorted(parts.items())
},
"selected": selected_branch(parts, types),
}
)
return result
def main() -> int:
parser = argparse.ArgumentParser(description="Resolve composite primitive/reference SQL values.")
parser.add_argument("--read-result", type=Path, required=True)
parser.add_argument("--output", type=Path, required=True)
args = parser.parse_args()
read_result = load_json(args.read_result)
composites = []
composites.extend(
collect_scope(
read_result.get("main", {}).get("rows") or [],
scope="main",
table_part_name=None,
table_part_table=read_result.get("main", {}).get("table"),
)
)
for part in read_result.get("table_parts") or []:
composites.extend(
collect_scope(
part.get("rows") or [],
scope="table_part",
table_part_name=part.get("name"),
table_part_table=part.get("table"),
)
)
summary = defaultdict(int)
for item in composites:
summary[item["selected"]["branch"]] += 1
result = {
"schema": "onec_sql_composite_value_resolution.v1",
"read_result_path": str(args.read_result),
"source": {"kind": read_result.get("kind"), "identity": read_result.get("identity")},
"summary": {
"composite_values": len(composites),
"by_branch": dict(sorted(summary.items())),
},
"composites": composites,
}
args.output.parent.mkdir(parents=True, exist_ok=True)
args.output.write_text(json.dumps(result, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
print(json.dumps({"output": str(args.output), "summary": result["summary"]}, ensure_ascii=False))
return 0
if __name__ == "__main__":
raise SystemExit(main())